繁体   English   中英

多表,多行SQL选择

[英]Multi-table, multi-row SQL select

给定以下架构,我如何列出有关自由职业者的所有信息? 包括利基市场,语言,市场等。我遇到的问题是,每个自由职业者可以为每个表包含多个条目。 那么,我该怎么做? 甚至可以使用SQL,还是我需要使用我的主要语言(golang)?

CREATE TABLE freelancer (
  freelancer_id         SERIAL PRIMARY KEY,
  ip                    inet NOT NULL,
  username              VARCHAR(20) NOT NULL,
  password              VARCHAR(100) NOT NULL,
  email                 citext NOT NULL UNIQUE,
  email_verified        int NOT NULL,
  fname                 VARCHAR(20) NOT NULL,
  lname                 VARCHAR(20) NOT NULL,
  phone_number          VARCHAR(30) NOT NULL,
  address               VARCHAR(50) NOT NULL,
  city                  VARCHAR(30) NOT NULL,
  state                 VARCHAR(30) NOT NULL,
  zip                   int NOT NULL,
  country               VARCHAR(30) NOT NULL,
);

CREATE TABLE market (
market_id       SERIAL PRIMARY KEY,
market_name     VARCHAR(30) NOT NULL,
);

CREATE TABLE niche (
niche_id        SERIAL PRIMARY KEY,
niche_name      VARCHAR(30) NOT NULL,
);

CREATE TABLE medium (
medium_id       SERIAL PRIMARY KEY,
medium_name     VARCHAR(30) NOT NULL,
);

CREATE TABLE format (
format_id       SERIAL PRIMARY KEY,
format_name     VARCHAR(30) NOT NULL,
);

CREATE TABLE lang (
lang_id         SERIAL PRIMARY KEY,
lang_name       VARCHAR(30) NOT NULL,
);

CREATE TABLE freelancer_by_niche (
id      SERIAL PRIMARY KEY,
niche_id        int NOT NULL REFERENCES niche (niche_id),
freelancer_id   int NOT NULL REFERENCES freelancer (freelancer_id)
);


CREATE TABLE freelancer_by_medium (
id      SERIAL PRIMARY KEY,
medium_id       int NOT NULL REFERENCES medium (medium_id),
freelancer_id   int NOT NULL REFERENCES freelancer (freelancer_id)

);

CREATE TABLE freelancer_by_market (
id      SERIAL PRIMARY KEY,
market_id       int NOT NULL REFERENCES market (market_id),
freelancer_id   int NOT NULL REFERENCES freelancer (freelancer_id)
);

CREATE TABLE freelancer_by_format (
id      SERIAL PRIMARY KEY,
format_id       int NOT NULL REFERENCES format (format_id),
freelancer_id   int NOT NULL REFERENCES freelancer (freelancer_id)

);

CREATE TABLE freelancer_by_lang (
id      SERIAL PRIMARY KEY,
lang_id         int NOT NULL REFERENCES lang (lang_id),
freelancer_id   int NOT NULL REFERENCES freelancer (freelancer_id)

);
SELECT *  
FROM freelancer  
INNER JOIN freelancer_by_niche USING (freelancer_id)  
INNER JOIN niche USING (niche_id)  
INNER JOIN freelancer_by_medium USING (freelancer_id)  
INNER JOIN medium USING (medium_id)  
INNER JOIN freelancer_by_market USING (freelancer_id)  
INNER JOIN market USING (market_id)  
INNER JOIN freelancer_by_format USING (freelancer_id)  
INNER JOIN format USING (format_id)  
INNER JOIN freelancer_by_lang USING (freelancer_id)  
INNER JOIN lang USING (lang_id);  

而且,如果您想丢失诸如freelancer_by_format类的freelancer_by_format表中不必要的属性,则可以执行此操作

SELECT a.ip, a.username, a.password, a.email, a.email_verified,  
a.fname, a.lname, a.phone_number, a.address, a.city,  
a.state, a.zip, a.country,  
b.niche_name, c.medium_name, d.market_name, e.format_name, f.lang_name  
FROM freelancer a  
INNER JOIN freelancer_by_niche USING (freelancer_id)  
INNER JOIN niche b USING (niche_id)  
INNER JOIN freelancer_by_medium USING (freelancer_id)  
INNER JOIN medium c USING (medium_id)  
INNER JOIN freelancer_by_market USING (freelancer_id)  
INNER JOIN market d USING (market_id)  
INNER JOIN freelancer_by_format USING (freelancer_id)  
INNER JOIN format e USING (format_id)  
INNER JOIN freelancer_by_lang USING (freelancer_id)  
INNER JOIN lang f USING (lang_id);  

如果要更改列名,例如将“ market_name”更改为“ market”,则可以使用

SELECT a.ip, ... ,  
       d.market_name "market", e.format_name AS "format", ...  
FROM ...  

备注在你的连接表(例如freelancer_by_niche )没有UNIQUE的约束freelancer_id ,这意味着你可以在多个市场同样自由职业者(这是确定,可能意)。

但是,这两个属性(freelancer_id, niche_id)也没有UNIQUE约束,这意味着每个自由职业者可能多次出现在SAME生态位中。 (“乔在电子领域。三遍”)。 您可以防止通过使(freelancer_id, niche_id) UNIQUEfreelancer_by_niche 这样,您也就不需要代理(人工) PRIMARY KEY freelancer_by_id (id)

那么,可能出什么问题了?

例如,假设有关自由职业者在相同的利基空间中的相同信息三遍(行的相同数据部分三遍):

freelancer_by_niche  
id | freelancer_id | niche_id  
 1 |       1       |    1    -- <-- same data (1, 1), different serial id
 2 |       1       |    1    -- <-- same data (1, 1), different serial id
 3 |       1       |    1    -- <-- same data (1, 1), different serial id

然后,上述查询的结果将以相同的内容(!)返回每个可能的行三(!)次,因为freelancer_by_niche可以与所有其他JOIN组合三次。

您可以将上面的SELECT DISTINCT a.id, ... FROM ...DISTINCT一起使用,以消除重复项。 如果您获得许多重复的行,例如5个JOIN表(freelancer_by_niche,freelancer_by_medium等)中的每一个都有10个数据重复,该怎么办? 您将得到10 * 10 * 10 * 10 * 10 = 10 ^ 5 = 100000重复项,这些重复项具有完全相同的信息。 如果然后要求DBMS使用SELECT DISTINCT ...消除重复项,则它必须对100000 duplicate rows per different row排序100000 duplicate rows per different row ,因为只能通过排序(或散列,但没关系)来检测重复项。 如果市场, 1.000 * 100.000 = 100.000.000 ,语言等自由职业者有1000行,那么您要求DBMS排序1.000 * 100.000 = 100.000.000行,以将重复项减少到唯一的1000行。 那就是一亿不必要的行。

请为freelancer_by_niche和其他JOIN表设置UNIQUE (freelancer_id, niche_id)

(通过重复数据,我的意思是数据(niche_id, freelancer_id)是相同的,并且只有id是自动递增的序列号。)

您可以通过执行以下操作轻松重现该问题:

-- this duplicates all data of your JOIN tables once. Do it many times.
INSERT INTO freelancer_by_niche  
  SELECT (niche_id, freelancer_id) FROM freelancer_by_niche;  
INSERT INTO freelancer_by_medium  
  SELECT (medium_id, freelancer_id) FROM freelancer_by_medium;  
INSERT INTO freelancer_by_market  
  SELECT (market_id, freelancer_id) FROM freelancer_by_market;  
INSERT INTO freelancer_by_format  
  SELECT (format_id, freelancer_id) FROM freelancer_by_format;  
INSERT INTO freelancer_by_lang  
  SELECT (lang_id, freelancer_id) FROM freelancer_by_lang;  

使用显示重复项

SELECT * FROM freelancer_by_lang;

现在尝试SELECT * FROM freelancer INNER JOIN ...事情。 如果仍能快速运行,则一次又一次地执行所有INSERT INTO freelancer_by_niche ... ,直到永远需要计算出结果为止。 (或者您得到重复的商品,可以使用DISTINCT删除)。

创建唯一数据联接表

您可以防止联接表中的重复项。 删除id SERIAL PRIMARY KEY并将其替换为多属性PRIMARY KEY(a,b):

CREATE TABLE freelancer_by_niche (
   niche_id        int NOT NULL REFERENCES niche (niche_id),
   freelancer_id   int NOT NULL REFERENCES freelancer (freelancer_id), 
   PRIMARY KEY (freelancer_id, niche_id)
);

(将其应用于所有联接表)。 PRIMARY KEY (freelancer_id, niche_id)将创建一个UNIQUE索引。 这样,您将无法插入重复的数据(尝试使用上面的INSERT ,将被拒绝,因为信息已经存在一次。添加其他时间不会添加更多的信息,并且会使查询运行速度大大降低)。

JOIN表另一部分的非唯一索引使用PRIMARY KEY (freelancer_id, niche_id) ,Postgres在这两个属性(列)上创建一个唯一索引。 freelancer_id访问或JOINing速度很快,因为它在索引中排名第一。 访问或加入freelancer_by_niche.niche_id会很慢(对freelancer_by_niche全表扫描)。

因此,您也应该在此表freelancer_by_niche的第二部分niche_id上创建一个INDEX。

CREATE INDEX ON freelancer_by_niche (niche_id) ;

然后在niche_id上加入该表的速度也会更快,因为它们通过索引加速。 索引使查询速度更快(通常)。

摘要

您有一个很好的规范化数据库架构! 这很好。 但是可以进行一些小的改进(请参见上文)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM