多表,多行SQL选择

时间:2017-05-08 22:35:40

标签: sql postgresql

如果给出下面的架构,我如何列出有关自由职业者的所有信息?包括利基,语言,市场等。我遇到的问题是每个自由职业者可以为每个表有多个条目。那么,我该怎么做?甚至可以使用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)

);

1 个答案:

答案 0 :(得分:1)

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等联接表中丢失不必要的属性,那么您可以这样做

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_nicheUNIQUE上没有freelancer_id约束,这意味着您可以在多个市场中拥有相同的自由职业者(这是可以的,可能是有意的)。< / p>

但是你也对UNIQUE这两个属性都没有(freelancer_id, niche_id)约束,这意味着每个自由职业者都可以多次在同一个利基中。 (“乔在电子学。三次”)。 您可以通过(freelancer_id, niche_id) UNIQUE中的freelancer_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进行排序,因为重复项只能通过排序(或散列,但没关系)来检测。如果您在市场,壁龛,语言等上拥有1000个不同的自由职业者行,那么您要求您的DBMS排序1.000 * 100.000 = 100.000.000行以将重复项减少到唯一的1000行。 这是1亿不必要的行。

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

(通过数据重复,我的意思是数据(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删除它。)

创建UNIQUE数据JOIN表

您可以阻止连接表中的重复项。 删除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,将被拒绝,因为信息已经存在一次。添加另一个时间不会添加更多信息并且会使你的查询运行时慢得多。)< / p>

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

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

CREATE INDEX ON freelancer_by_niche (niche_id) ;

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

<强>摘要

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