postgresql 一次选择多个多对多关系的最佳实践

qybjjes1  于 5个月前  发布在  PostgreSQL
关注(0)|答案(1)|浏览(62)

我想知道处理以下问题的最佳方法是什么:
我有一个DB结构,其中许多表都链接到我的Person表,如下所示:

phone n-n person_phone_realtion n-n person n-n person_email_realtionn-n email

字符串
我想查询我的表,并将结果解析为JSON,并将多对多的值存储在数组中。是只访问一次数据库并将我的JOIN查询的结果(见下面的示例)解析为我想要的模式更好,还是应该多次访问数据库并保持查询结果较小?
此场景的最佳做法是什么
使用以下声明创建:

DROP TABLE IF EXISTS phone CASCADE;
DROP TABLE IF EXISTS email CASCADE;
DROP TABLE IF EXISTS person CASCADE;
DROP TABLE IF EXISTS person_phone_realtion CASCADE;
DROP TABLE IF EXISTS person_email_realtion CASCADE;

CREATE TABLE phone (
    phon_id text  NOT NULL,
    CONSTRAINT phone_pk PRIMARY KEY (phon_id)
);

CREATE TABLE email (
    emai_id text  NOT NULL,
    CONSTRAINT email_pk PRIMARY KEY (emai_id)
);

CREATE TABLE person (
    pers_id INTEGER  NOT NULL,
    CONSTRAINT person_pk PRIMARY KEY (pers_id)
);

CREATE TABLE person_phone_realtion (
    pers_id int  NOT NULL,
    phon_id int  NOT NULL
);

CREATE TABLE person_email_realtion (
    pers_id int  NOT NULL,
    email_id int  NOT NULL
);

INSERT INTO person(pers_id)
VALUES (1),(2),(3),(4),(5);

INSERT INTO email(emai_id)
VALUES ('a'),('b'),('c');

INSERT INTO phone(phon_id)
VALUES ('D'),('E'),('F');

INSERT INTO person_email_realtion(pers_id, email_id)
VALUES (1,'a'),(1,'b'), (1,'c'),(2,'b'),(3,'c');

INSERT INTO person_phone_realtion(pers_id, phon_id)
VALUES (1,'D'),(2,'D'), (2,'E'),(5,'F');

DROP TABLE IF EXISTS phone CASCADE;
DROP TABLE IF EXISTS email CASCADE;
DROP TABLE IF EXISTS person CASCADE;
DROP TABLE IF EXISTS person_phone_realtion CASCADE;
DROP TABLE IF EXISTS person_email_realtion CASCADE;

CREATE TABLE phone (
    phon_id text  NOT NULL,
    CONSTRAINT phone_pk PRIMARY KEY (phon_id)
);

CREATE TABLE email (
    emai_id text  NOT NULL,
    CONSTRAINT email_pk PRIMARY KEY (emai_id)
);

CREATE TABLE person (
    pers_id INTEGER  NOT NULL,
    CONSTRAINT person_pk PRIMARY KEY (pers_id)
);

CREATE TABLE person_phone_realtion (
    pers_id int  NOT NULL,
    phon_id int  NOT NULL
);

CREATE TABLE person_email_realtion (
    pers_id int  NOT NULL,
    email_id int  NOT NULL
);

INSERT INTO person(pers_id)
VALUES (1),(2),(3),(4),(5);

INSERT INTO email(emai_id)
VALUES ('a'),('b'),('c');

INSERT INTO phone(phon_id)
VALUES ('D'),('E'),('F');

INSERT INTO person_email_realtion(pers_id, email_id)
VALUES (1,'a'),(1,'b'), (1,'c'),(2,'b'),(3,'c');

INSERT INTO person_phone_realtion(pers_id, phon_id)
VALUES (1,'D'),(2,'D'), (2,'E'),(5,'F');


现在我可以使用JOIN一次查询所有的关系,这会导致很多重复的内容:

SELECT * FROM person
RIGHT JOIN person_phone_realtion
ON person.pers_id = person_phone_realtion.pers_id 
RIGHT JOIN phone
ON person_phone_realtion.phon_id = phone.phon_id
RIGHT JOIN person_email_realtion
ON person.pers_id = person_email_realtion.pers_id
RIGHT JOIN email
ON person_email_realtion.email_id = email.emai_id;


我会得到类似这样的结果:

pers_id phon_id emai_id
1       D       a
1       D       b
1       D       c
2       E       b
2       D       b


生成的JSON应该看起来像这样:

[
    {
        "person" : 1,
        "email": [
            "a", "b", "c"
        ],
        "phone":[
            "D"
        ]
    },
    {
        "person" : 2,
        "email": [
            "b"
        ],
        "phone":[
            "D", "E"
        ]
    }
]

3z6pesqy

3z6pesqy1#

通常最好只访问一次数据库。您应该沿着每个维度预先聚合值沿着:

select p.*, pp.phones, pe.emails
from person p left join
     (select pers_id, array_agg(ppr.phone_id) as phones
      from person_phone_realtion ppr 
      group by pers_id
     ) pp
     on p.pers_id = pp.pers_id left join
     (select pers_id, array_agg(per.email_id) as emails
      from person_email_realtion ppr 
      group by pers_id
     ) pe
     on p.pers_id = pe.pers_id ;

字符串
如果您愿意,可以聚合为字符串或JSON。

相关问题