我们在1:N关系中有2个表。例如,项目及其自定义属性。
CREATE TABLE item (
id BIGINT PRIMARY KEY
);
CREATE TABLE property (
id BIGINT PRIMARY KEY,
oneFK BIGINT REFERENCES item (id),
key CHARACTER VARYING(5),
value CHARACTER VARYING(5)
);
让我们有一些演示数据:
INSERT INTO item VALUES (1);
INSERT INTO property VALUES
(1, 1, 'key1', 'val1'),
(2, 1, 'key2', 'val2'),
(3, 1, 'key3', 'val3');
我们在这些表格中有数百万行后,要求他们导出'他们到另一个定义为:
的表CREATE TABLE flat (
id BIGINT PRIMARY KEY,
key1 CHARACTER VARYING(5),
key2 CHARACTER VARYING(5),
key3 CHARACTER VARYING(5)
);
如此天真的插入可能看起来像:
INSERT INTO flat (id, key1, key2, key3)
SELECT
i.id,
(SELECT p.value
FROM property p
WHERE p.oneFK = i.id AND p.key = 'key1'),
(SELECT p.value
FROM property p
WHERE p.oneFK = i.id AND p.key = 'key2'),
(SELECT p.value
FROM property p
WHERE p.oneFK = i.id AND p.key = 'key3')
FROM item i;
但是会有5个嵌套的选择,每个都在相当大的表中。所以我根本不喜欢这样。有不同的提议,我更喜欢它,怀疑它实际上会表现得更好,并且发现它有点荒谬:
INSERT INTO flat
SELECT
i.id,
--lots of other properties
max(CASE WHEN key = 'key1'
THEN 'key1'
ELSE NULL END)
--so on.
FROM item i
JOIN property p ON i.id = p.oneFK
GROUP BY i.id --, lots of other properties
那么实际建议和执行此任务的有效方法是什么?理想情况下,在通用sql中,oracle / postgres特定(理想情况下都是)也很好。
答案 0 :(得分:2)
第二个提案"可能是这样做的方法(使用条件聚合来转动)。但它与您在问题中的内容略有不同:
INSERT INTO flat
( id /* , other columns */, key1, key2, key3 )
SELECT id -- , other columns
, MAX(CASE WHEN key = 'key1' THEN val END) AS key1 -- ELSE NULL is superfluous
, MAX(CASE WHEN key = 'key2' THEN val END) AS key2
, MAX(CASE WHEN key = 'key3' THEN val END) AS key3
FROM item i JOIN property p
ON i.id = p.oneFK
GROUP BY i.id;
也就是说,我假设您希望存储在val
列中的值而不是key
列中存储的密钥索引。顺便说一下,我希望你没有一个名为key
的列,因为这是一个保留字。
希望这会有所帮助。这应该至少在Oracle和Postgres中都有效。