我在PostgreSQL中有一个类似于以下内容的数据库结构:
DROP TABLE IF EXISTS medium CASCADE;
DROP TABLE IF EXISTS works CASCADE;
DROP DOMAIN IF EXISTS nameVal CASCADE;
DROP DOMAIN IF EXISTS numID CASCADE;
DROP DOMAIN IF EXISTS alphaID CASCADE;
CREATE DOMAIN alphaID AS VARCHAR(10);
CREATE DOMAIN numID AS INT;
CREATE DOMAIN nameVal AS VARCHAR(40);
CREATE TABLE works (
w_alphaID alphaID NOT NULL,
w_numID numID NOT NULL,
w_title nameVal NOT NULL,
PRIMARY KEY(w_alphaID,w_numID));
CREATE TABLE medium (
m_alphaID alphaID NOT NULL,
m_numID numID NOT NULL,
m_title nameVal NOT NULL,
FOREIGN KEY(m_alphaID,m_numID) REFERENCES
works ON UPDATE CASCADE ON DELETE CASCADE);
INSERT INTO works VALUES('AB',1,'Sunset'),
('CD',2,'Beach'),
('EF',3,'Flower');
INSERT INTO medium VALUES('AB',1,'Wood'),
('AB',1,'Oil'),
('CD',2,'Canvas'),
('CD',2,'Oil'),
('CD',2,'Bronze'),
('EF',3,'Paper'),
('EF',3,'Pencil');
SELECT * FROM works;
SELECT * FROM medium;
SELECT w_alphaID AS alphaID, w_numID AS numID, w_title AS
Name_of_work, m_title AS Material_used
FROM works, medium WHERE
works.w_alphaID = medium.m_alphaID
AND works.w_numID = medium.m_numID;
输出看起来像这样:
w_alphaid | w_numid | w_title
-----------+---------+---------
AB | 1 | Sunset
CD | 2 | Beach
EF | 3 | Flower
(3 rows)
m_alphaid | m_numid | m_title
-----------+---------+---------
AB | 1 | Wood
AB | 1 | Oil
CD | 2 | Canvas
CD | 2 | Oil
CD | 2 | Bronze
EF | 3 | Paper
EF | 3 | Pencil
(7 rows)
alphaid | numid | name_of_work | material_used
---------+-------+--------------+---------------
AB | 1 | Sunset | Wood
AB | 1 | Sunset | Oil
CD | 2 | Beach | Canvas
CD | 2 | Beach | Oil
CD | 2 | Beach | Bronze
EF | 3 | Flower | Paper
EF | 3 | Flower | Pencil
(7 rows)
现在我的问题是我应该使用什么查询来使最后SELECT
语句的格式看起来像这样:
alphaid | numid | name_of_work | material_used_1 | material_used_2 | material_used_3
---------+-------+--------------+-----------------+-----------------+---------------
AB | 1 | Sunset | Wood | Oil |
CD | 2 | Beach | Canvas | Oil | Bronze
EF | 3 | Flower | Paper | Pencil |
(3 rows)
我研究了使用string_agg()
,但是将值放在一个单元格中,但我希望每个值都有一个单独的单元格。我尝试使用join来查看我是否可以实现这样的输出但到目前为止没有成功。感谢您抽出宝贵时间来研究这个问题。
答案 0 :(得分:1)
您可以在子查询中使用string_agg(),然后将该字符串分成不同的列。另请参阅how to split string into columns
上的此问题SELECT alphaID, numID, Name_of_Work
,split_part(Material_used, ',', 1) AS Material_used_1
,split_part(Material_used, ',', 2) AS Material_used_2
,split_part(Material_used, ',', 3) AS Material_used_3
,split_part(Material_used, ',', 4) AS Material_used_4
FROM (
SELECT w_alphaID AS alphaID, w_numID AS numID, w_title AS Name_of_work,
String_Agg( m_title, ',' ) AS Material_used
FROM works, medium
WHERE works.w_alphaID = medium.m_alphaID
AND works.w_numID = medium.m_numID
GROUP BY w_alphaID, w_numID, w_title ) t
答案 1 :(得分:1)
使用更简单的架构会更简单:
medium
serial
列)而不是两列域类型的多列PK和FK。alpha_id
而不是m_alphaID
和w_alphaID
等。除此之外,以下是您的设置 的解决方案 :
crosstab()
解决方案 crosstab()
查询存在以下几个具体问题:
基础知识(首先阅读!):
针对您的特殊情况:
解决方案:
SELECT alphaid, numid, name_of_work, material_1, material_2, material_3
FROM crosstab(
'SELECT rn, w.alphaid, w.numid, w.name_of_work
, row_number() OVER (PARTITION BY rn) AS mat_nr -- order undefined!
, m_title AS Material_used
FROM (
SELECT w_alphaID AS alphaid, w_numID AS numid, w_title AS name_of_work
, row_number() OVER (ORDER BY w_alphaID, w_numID) AS rn
FROM works
) w
JOIN medium m ON w.alphaid = m.m_alphaID
AND w.numid = m.m_numID
ORDER BY rn, mat_nr'
, 'VALUES (1), (2), (3)' -- add more ...
)
AS ct (
rn bigint, alphaid text, numid int, name_of_work text
, material_1 text, material_2 text, material_3 text -- add more ...
);
如果无法安装附加模块tablefunc或顶级性能不重要,这个更简单的查询会做同样的,慢一点:
SELECT w_alphaid AS alphaid, w_numid AS numid, w_title AS name_of_work
, arr[1] AS material_used_1
, arr[2] AS material_used_2
, arr[3] AS material_used_3 -- add more?
FROM works w
LEFT JOIN (
SELECT m_alphaid, m_numid, array_agg(m_title::text) AS arr
FROM medium
GROUP BY m_alphaid, m_numid
) m ON w.w_alphaid = m.m_alphaid
AND w.w_numid = m.m_numid;
转换为text
(或varchar
...)是必要的,因为您的自定义域没有预定义的数组类型。或者,您可以定义缺少的数组类型。
与上述内容的一个细微差别:在此使用LEFT JOIN
而非JOIN
来保留works
中包含 no 相关资料的行完全medium
。
由于您返回整个表格,因此在加入之前汇总medium
中的行会更便宜。对于较小的选择,首先加入并且然后聚合可能更便宜。相关: