[英]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) 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
排序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.