查询以匹配外键关系

时间:2018-12-21 05:09:30

标签: sql postgresql relational-division sql-match-all

我有两个表,这是Postgres数据库,代表来自市场的简单订单。主表包含有关订单的信息,明细表包含购买的详细信息,外键返回主数据库。很容易。

在市场上成千上万的订单中,我想根据购买的数量和数量找到一些特定的订单。

我又有两个桌子,以类似的方式,分别是一个主人和一个孩子,我在其中创建一个“包装”并从市场上购买明细物品。

对于示例:包装A包含2个苹果和3个橙子。我在表格中定义。现在,我想查找多少个订单,以及市场中哪些订单与该特定组合完全匹配

重要的是要完全匹配。包含其他产品或不同数量的订单不匹配。

在SQL Fiddle中,我已经设置了带有数据的简单示例。原始DDL在下面。表中的两个订单应与Pack 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);

2 个答案:

答案 0 :(得分:2)

假设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
   );

返回所有完全匹配的pack_idorder_id对。

db <>提琴here

编写查询有一百零一种替代方法。最快的速度取决于基数,数据分布,约束以及最重要的是可用索引。

这是的特殊应用。这是一系列技术:

一个替代,可能更快:创建视图或父表的materialized views,包括项目数:

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);

..然后,如果所有项目都匹配,我们就不必再排除其他项目。而且我们可以立即使用父表中的所有列,以返回您想要返回的内容...

答案 1 :(得分:0)

  

我想查找多少订单,以及市场上哪些订单   匹配该特定组合。

据此,我假设由于您有2个数量为2的苹果订单和2个数量为2的橙色订单,您的结果应该类似于下表,因为它们存在于包装中且具有相同的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