sql - 一次选择形成大量多对多关系的最佳实践

标签 sql postgresql

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

我有一个 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"
        ]
    }
]

最佳答案

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

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。

关于sql - 一次选择形成大量多对多关系的最佳实践,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43822080/

相关文章:

sql - 批量插入 - UNIX 文件的行终止符 + "\l"行终止符

sql - 转换为 float : CAST vs CONVERT vs *1. 0

PHP 选择多个表作为数组

function - 在 PostgreSQL SQL 函数中为多个结果抛出错误

mysql - 如何在 DBIx::Class 的 SELECT 部分使用子查询?

mysql - 错误代码1215,无法添加外键约束

mysql - 匹配sql中的多个数字并获取行中所有匹配项的计数

postgresql - Heroku PostgreSQL 中的 `ERROR: cannot execute TRUNCATE TABLE in a read-only transaction`

ruby-on-rails - 设计:尝试设置用户名字段

PostgreSQL 不允许我按顺序对列进行分组