我的数据库结构如下所示:
CREATE TABLE categories (
name VARCHAR(30) PRIMARY KEY
);
CREATE TABLE additives (
name VARCHAR(30) PRIMARY KEY
);
CREATE TABLE beverages (
name VARCHAR(30) PRIMARY KEY,
description VARCHAR(200),
price NUMERIC(5, 2) NOT NULL CHECK (price >= 0),
category VARCHAR(30) NOT NULL REFERENCES categories(name) ON DELETE CASCADE ON UPDATE CASCADE
);
CREATE TABLE b_additives_xref (
bname VARCHAR(30) REFERENCES beverages(name) ON DELETE CASCADE ON UPDATE CASCADE,
aname VARCHAR(30) REFERENCES additives(name) ON DELETE CASCADE ON UPDATE CASCADE,
PRIMARY KEY(bname, aname)
);
INSERT INTO categories VALUES
('Cocktails'), ('Biere'), ('Alkoholfreies');
INSERT INTO additives VALUES
('Kaliumphosphat (E 340)'), ('Pektin (E 440)'), ('Citronensäure (E 330)');
INSERT INTO beverages VALUES
('Mojito Speciale', 'Cocktail mit Rum, Rohrzucker und Minze', 8, 'Cocktails'),
('Franziskaner Weißbier', 'Köstlich mildes Hefeweizen', 6, 'Biere'),
('Augustiner Hell', 'Frisch gekühlt vom Fass', 5, 'Biere'),
('Coca Cola', 'Coffeeinhaltiges Erfrischungsgetränk', 2.75, 'Alkoholfreies'),
('Sprite', 'Erfrischende Zitronenlimonade', 2.50, 'Alkoholfreies'),
('Karaffe Wasser', 'Kaltes, gashaltiges Wasser', 6.50, 'Alkoholfreies');
INSERT INTO b_additives_xref VALUES
('Coca Cola', 'Kaliumphosphat (E 340)'),
('Coca Cola', 'Pektin (E 440)'),
('Coca Cola', 'Citronensäure (E 330)');
我想要实现的是列出所有饮料及其属性(price
,description
等),并从additives
表中添加另一列b_additives_xref
,它与每种饮料中含有的所有添加剂结合在一起。
我的查询目前看起来像这样,并且几乎正常工作(我猜):
SELECT
beverages.name AS name,
beverages.description AS description,
beverages.price AS price,
beverages.category AS category,
string_agg(additives.name, ', ') AS additives
FROM beverages, additives
LEFT JOIN b_additives_xref ON b_additives_xref.aname = additives.name
GROUP BY beverages.name
ORDER BY beverages.category;
输出如下:
Coca Cola | Coffeeinhaltiges Erfrischungsgetränk | 2.75 | Alkoholfreies | Kaliumphosphat (E 340), Pektin (E 440), Citronensäure (E 330)
Karaffe Wasser | Kaltes, gashaltiges Wasser | 6.50 | Alkoholfreies | Kaliumphosphat (E 340), Pektin (E 440), Citronensäure (E 330)
Sprite | Erfrischende Zitronenlimonade | 2.50 | Alkoholfreies | Kaliumphosphat (E 340), Pektin (E 440), Citronensäure (E 330)
Augustiner Hell | Frisch gekühlt vom Fass | 5.00 | Biere | Kaliumphosphat (E 340)[...]
当然,这是错误的,因为只有'可口可乐'在b_additives_xref
表格中有现有行。
除了“可口可乐”行之外,所有其他行在“添加剂”列中应具有“空”或“空字段”值。我做错了什么?
答案 0 :(得分:2)
关于你的一些建议
CREATE TABLE category (
category_id int PRIMARY KEY
,category text UNIQUE NOT NULL
);
CREATE TABLE beverage (
beverage_id serial PRIMARY KEY
,beverage text UNIQUE NOT NULL -- maybe not unique?
,description text
,price int NOT NULL CHECK (price >= 0) -- in Cent
,category_id int NOT NULL REFERENCES category ON UPDATE CASCADE
-- not: ON DELETE CASCADE
);
CREATE TABLE additive (
additive_id serial PRIMARY KEY
,additive text UNIQUE
);
CREATE TABLE bev_add (
beverage_id int REFERENCES beverage ON DELETE CASCADE ON UPDATE CASCADE
,additive_id int REFERENCES additive ON DELETE CASCADE ON UPDATE CASCADE
,PRIMARY KEY(beverage_id, additive_id)
);
serial
列,对于小表,使用简单integer
列。有可能,饮料和添加剂的名称并不是严格独特的,您希望不时更改它们,这使它们成为主键的不良候选者。 integer
列也更小,处理速度更快。enum
。text
instead of character varying (n)
. ON DELETE CASCADE
向查找表定义fk约束之前,请三思
通常情况下,如果您删除某个类别(错误地),不会自动删除所有饮料。integer
列而不是NUMERIC(5, 2)
(使用Cent而不是€/ $)。更小,更快,更简单。
需要时输出格式。这个密切相关答案的更多建议和链接:
How to implement a many-to-many relationship in PostgreSQL?
适应新架构和一些一般性建议。
SELECT b.*, string_agg(a.additive, ', ' ORDER BY a.additive) AS additives
-- order by optional for sorted list
FROM beverage b
JOIN category c USING (category_id)
LEFT JOIN bev_add ba USING (beverage_id) -- simpler now
LEFT JOIN additive a USING (additive_id)
GROUP BY b.beverage_id, c.category_id
ORDER BY c.category;
USING
in joins。category
和GROUP BY category_id
或category
(建议架构的缺点)。答案 1 :(得分:1)
我相信你正在寻找这个
SELECT
B.name AS name,
B.description AS description,
B.price AS price,
B.category AS category,
string_agg(A.name, ', ') AS additives
FROM Beverages B
LEFT JOIN b_additives_xref xref ON xref.bname = B.name
Left join additives A on A.name = xref.aname
GROUP BY B.name
ORDER BY B.category;
输出
NAME DESCRIPTION PRICE CATEGORY ADDITIVES
Coca Cola Coffeeinhaltiges Erfrischungsgetränk 2.75 Alkoholfreies Kaliumphosphat (E 340), Pektin (E 440), Citronensäure (E 330)
问题是您在beverages
和additives
表之间有笛卡尔积?
FROM beverages, additives
每条记录都有其他记录。它们都需要显式连接到外部参照表。
答案 2 :(得分:1)
我正在寻找的查询如下:
SELECT
B.name AS name,
B.description AS description,
B.price AS price,
B.category AS category,
string_agg(A.name, ', ') AS additives
FROM beverages B
LEFT JOIN b_additives_xref xref ON xref.bname = B.name
LEFT JOIN additives A on A.name = xref.aname
GROUP BY B.name
ORDER BY B.category;
积分转到布拉德,因为他在答案中给了我解决方案。评价。