选择形成很多多对多关系的最佳实践

时间:2017-05-06 15:17:07

标签: sql postgresql

我想知道处理以下问题的最佳方法是什么:

我有一个DB-Structure,其中许多表都链接到我的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"
        ]
    }
]

1 个答案:

答案 0 :(得分:2)

一次访问数据库通常是最好的。您应该预先汇总每个维度的值:

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。