查询以匹配外键关系
Query to match foreign key relationships
我在 this is Postgres 数据库中有两个 table 表示来自市场的简单订单。包含有关订单信息的 master table,以及包含购买细节的详细信息 table,以及返回 master 的外键。很简单。
来自市场的数千个订单,我想根据购买的商品和数量找到一些特定的订单。
我还有两个 table,以类似的方式,一个大师和一个 child,我在其中创建 "pack" 和来自市场的详细信息。
对于示例:包装 A 包含 2 个苹果和 3 个橙子。我在 table 中定义了它。现在我想知道有多少订单,以及市场上的哪些订单与特定组合完全匹配。
精确匹配很重要。包含其他产品或任何不同数量的订单不匹配。
在 SQL Fiddle 中,我设置了带有数据的简单示例。原始 DDL 如下。 table 中的两个订单应与 A 包匹配。
http://sqlfiddle.com/#!17/b4f55
CREATE TABLE customer_order(
order_id serial PRIMARY KEY NOT NULL,
customer_name VARCHAR(100) NOT NULL
);
CREATE TABLE order_detail(
id serial PRIMARY KEY,
order_id INTEGER,
item_sku VARCHAR(50),
item_quantity INTEGER,
FOREIGN KEY(order_id) REFERENCES customer_order(order_id)
);
INSERT INTO customer_order (customer_name) VALUES ('John');
INSERT INTO customer_order (customer_name) VALUES ('Mary');
INSERT INTO customer_order (customer_name) VALUES ('Bill');
INSERT INTO order_detail (order_id, item_sku, item_quantity) VALUES (1, 'APPLE', 2);
INSERT INTO order_detail (order_id, item_sku, item_quantity) VALUES (1, 'ORANGE', 3);
INSERT INTO order_detail (order_id, item_sku, item_quantity) VALUES (2, 'ORANGE', 5);
INSERT INTO order_detail (order_id, item_sku, item_quantity) VALUES (3, 'APPLE', 2);
INSERT INTO order_detail (order_id, item_sku, item_quantity) VALUES (3, 'ORANGE', 3);
CREATE TABLE pack_master(
pack_id serial PRIMARY KEY NOT NULL,
name VARCHAR(100) NOT NULL
);
CREATE TABLE pack_child(
id serial PRIMARY KEY,
pack_id INTEGER,
item_sku VARCHAR(50),
item_quantity INTEGER,
FOREIGN KEY(pack_id) REFERENCES pack_master(pack_id)
);
INSERT INTO pack_master (name) VALUES ('Pack A');
INSERT INTO pack_master (name) VALUES ('Pack B');
INSERT INTO pack_child (pack_id, item_sku, item_quantity) VALUES (1, 'APPLE', 2);
INSERT INTO pack_child (pack_id, item_sku, item_quantity) VALUES (1, 'ORANGE', 3);
INSERT INTO pack_child (pack_id, item_sku, item_quantity) VALUES (2, 'GRAPES', 5);
假设 pack_child (pack_id, item_sku)
以及 order_detail (order_id, item_sku)
被定义为 UNIQUE
,这将有效:
SELECT pc.pack_id, od.order_id
FROM pack_child pc
LEFT JOIN order_detail od USING (item_sku, item_quantity)
GROUP BY 1, 2
HAVING count(*) = count(od.id) -- every item of the pack has a match
AND NOT EXISTS (
SELECT
FROM order_detail od1
LEFT JOIN pack_child pc1 ON pc1.item_sku = od1.item_sku
AND pc1.item_quantity = od1.item_quantity
AND pc1.pack_id = pc.pack_id
WHERE od1.order_id = od.order_id
AND pc1.id IS NULL -- and order has no additional item
);
Returns 所有完全匹配的 pack_id
和 order_id
对。
db<>fiddle here
有一百零一种编写查询的替代方法。哪个最快取决于基数、数据分布、约束以及最重要的可用索引。
这是relational-division的一个特殊应用。这是一个技术库:
- How to filter SQL results in a has-many-through relation
一个备选方案,可能更快:创建视图或 materialized views 父 table 包括项目数:
CREATE MATERIALIZED VIEW v_pack_master AS
SELECT *
FROM pack_master
JOIN (
SELECT pack_id, count(*) AS items
FROM pack_child
GROUP BY 1
) c USING (pack_id);
CREATE MATERIALIZED VIEW v_customer_order AS
SELECT *
FROM customer_order
JOIN (
SELECT order_id, count(*) AS items
FROM order_detail
GROUP BY 1
) c USING (order_id);
(订单通常以后不会更改,因此可能是物化视图的可行候选对象。)
只有可以有多个订单项,索引才可能支付(索引表达式按此顺序):
CREATE INDEX foo ON v_customer_order (items, order_id);
查询现在只考虑具有匹配项目计数开头的订单:
SELECT * -- pack_id, order_id
FROM v_pack_master pm
LEFT JOIN v_customer_order co USING (items)
JOIN LATERAL (
SELECT count(*) AS items
FROM pack_child pc
JOIN order_detail od USING (item_sku, item_quantity)
WHERE pc.pack_id = pm.pack_id
AND od.order_id = co.order_id
) x USING (items);
.. 那么,如果所有项目都匹配,我们就不必再排除其他项目。我们可以立即使用父 table 的所有列,到 return 任何你想要的 return ...
I'd like to find how many orders, and which orders from the market
match that specific combination.
由此我假设,由于您有 2 个苹果订单和 2 个橙色订单,数量为 2,您的结果应该类似于下面的 table,因为它们存在于包装中具有相同的 item_sku 和数量。
item_sku | Count
--------+------
Apple | 2
Orange | 2
SQL:
SELECT OD.item_sku, count(OD.item_sku)
FROM order_detail as OD
JOIN pack_child as PC
ON OD.item_sku = PC.item_sku
WHERE OD.item_sku = PC.item_sku AND (OD.item_quantity = PC.item_quantity)
GROUP BY OD.item_sku
我在 this is Postgres 数据库中有两个 table 表示来自市场的简单订单。包含有关订单信息的 master table,以及包含购买细节的详细信息 table,以及返回 master 的外键。很简单。
来自市场的数千个订单,我想根据购买的商品和数量找到一些特定的订单。
我还有两个 table,以类似的方式,一个大师和一个 child,我在其中创建 "pack" 和来自市场的详细信息。
对于示例:包装 A 包含 2 个苹果和 3 个橙子。我在 table 中定义了它。现在我想知道有多少订单,以及市场上的哪些订单与特定组合完全匹配。
精确匹配很重要。包含其他产品或任何不同数量的订单不匹配。
在 SQL Fiddle 中,我设置了带有数据的简单示例。原始 DDL 如下。 table 中的两个订单应与 A 包匹配。
http://sqlfiddle.com/#!17/b4f55
CREATE TABLE customer_order(
order_id serial PRIMARY KEY NOT NULL,
customer_name VARCHAR(100) NOT NULL
);
CREATE TABLE order_detail(
id serial PRIMARY KEY,
order_id INTEGER,
item_sku VARCHAR(50),
item_quantity INTEGER,
FOREIGN KEY(order_id) REFERENCES customer_order(order_id)
);
INSERT INTO customer_order (customer_name) VALUES ('John');
INSERT INTO customer_order (customer_name) VALUES ('Mary');
INSERT INTO customer_order (customer_name) VALUES ('Bill');
INSERT INTO order_detail (order_id, item_sku, item_quantity) VALUES (1, 'APPLE', 2);
INSERT INTO order_detail (order_id, item_sku, item_quantity) VALUES (1, 'ORANGE', 3);
INSERT INTO order_detail (order_id, item_sku, item_quantity) VALUES (2, 'ORANGE', 5);
INSERT INTO order_detail (order_id, item_sku, item_quantity) VALUES (3, 'APPLE', 2);
INSERT INTO order_detail (order_id, item_sku, item_quantity) VALUES (3, 'ORANGE', 3);
CREATE TABLE pack_master(
pack_id serial PRIMARY KEY NOT NULL,
name VARCHAR(100) NOT NULL
);
CREATE TABLE pack_child(
id serial PRIMARY KEY,
pack_id INTEGER,
item_sku VARCHAR(50),
item_quantity INTEGER,
FOREIGN KEY(pack_id) REFERENCES pack_master(pack_id)
);
INSERT INTO pack_master (name) VALUES ('Pack A');
INSERT INTO pack_master (name) VALUES ('Pack B');
INSERT INTO pack_child (pack_id, item_sku, item_quantity) VALUES (1, 'APPLE', 2);
INSERT INTO pack_child (pack_id, item_sku, item_quantity) VALUES (1, 'ORANGE', 3);
INSERT INTO pack_child (pack_id, item_sku, item_quantity) VALUES (2, 'GRAPES', 5);
假设 pack_child (pack_id, item_sku)
以及 order_detail (order_id, item_sku)
被定义为 UNIQUE
,这将有效:
SELECT pc.pack_id, od.order_id
FROM pack_child pc
LEFT JOIN order_detail od USING (item_sku, item_quantity)
GROUP BY 1, 2
HAVING count(*) = count(od.id) -- every item of the pack has a match
AND NOT EXISTS (
SELECT
FROM order_detail od1
LEFT JOIN pack_child pc1 ON pc1.item_sku = od1.item_sku
AND pc1.item_quantity = od1.item_quantity
AND pc1.pack_id = pc.pack_id
WHERE od1.order_id = od.order_id
AND pc1.id IS NULL -- and order has no additional item
);
Returns 所有完全匹配的 pack_id
和 order_id
对。
db<>fiddle here
有一百零一种编写查询的替代方法。哪个最快取决于基数、数据分布、约束以及最重要的可用索引。
这是relational-division的一个特殊应用。这是一个技术库:
- How to filter SQL results in a has-many-through relation
一个备选方案,可能更快:创建视图或 materialized views 父 table 包括项目数:
CREATE MATERIALIZED VIEW v_pack_master AS
SELECT *
FROM pack_master
JOIN (
SELECT pack_id, count(*) AS items
FROM pack_child
GROUP BY 1
) c USING (pack_id);
CREATE MATERIALIZED VIEW v_customer_order AS
SELECT *
FROM customer_order
JOIN (
SELECT order_id, count(*) AS items
FROM order_detail
GROUP BY 1
) c USING (order_id);
(订单通常以后不会更改,因此可能是物化视图的可行候选对象。)
只有可以有多个订单项,索引才可能支付(索引表达式按此顺序):
CREATE INDEX foo ON v_customer_order (items, order_id);
查询现在只考虑具有匹配项目计数开头的订单:
SELECT * -- pack_id, order_id
FROM v_pack_master pm
LEFT JOIN v_customer_order co USING (items)
JOIN LATERAL (
SELECT count(*) AS items
FROM pack_child pc
JOIN order_detail od USING (item_sku, item_quantity)
WHERE pc.pack_id = pm.pack_id
AND od.order_id = co.order_id
) x USING (items);
.. 那么,如果所有项目都匹配,我们就不必再排除其他项目。我们可以立即使用父 table 的所有列,到 return 任何你想要的 return ...
I'd like to find how many orders, and which orders from the market match that specific combination.
由此我假设,由于您有 2 个苹果订单和 2 个橙色订单,数量为 2,您的结果应该类似于下面的 table,因为它们存在于包装中具有相同的 item_sku 和数量。
item_sku | Count
--------+------
Apple | 2
Orange | 2
SQL:
SELECT OD.item_sku, count(OD.item_sku)
FROM order_detail as OD
JOIN pack_child as PC
ON OD.item_sku = PC.item_sku
WHERE OD.item_sku = PC.item_sku AND (OD.item_quantity = PC.item_quantity)
GROUP BY OD.item_sku