Запрос, чтобы соответствовать отношениям внешнего ключа - PullRequest
0 голосов
/ 21 декабря 2018

У меня есть две таблицы в базе данных Postgres, представляющей простые ордера с рынка.Основная таблица с информацией о заказе и подробная таблица с указанием специфики покупки, с внешним ключом, возвращаемым в мастер.Достаточно просто.

Более тысячи заказов с рынка, я хотел бы найти некоторые конкретные заказы на основе того, что было куплено и в каком количестве.

У меня есть еще две таблицы, аналогичным образом, мастер и потомок, где я создаю "пачку" и детали товаров из рынка.

Для пример :Пакет А содержит 2 яблока и 3 апельсина.Я определяю это в таблицах.Теперь я хотел бы узнать, сколько ордеров и какие ордера с рынка соответствуют этой конкретной комбинации точно .

Важно, чтобы это было точное совпадение.Заказ, содержащий дополнительные товары или с любым другим количеством, не совпадает.

В скрипте SQL я настроил простой пример с данными.Сырой DDL ниже.Два заказа в таблице должны соответствовать Пакету А.

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 голосов
/ 21 декабря 2018

Я бы хотел выяснить, сколько ордеров и какие ордера с рынка соответствуют этой конкретной комбинации.

Исходя из этого, я бы предположил, что, поскольку у вас есть 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
0 голосов
/ 21 декабря 2018

Предполагая, что 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_id и order_id, которые точно совпадают.

db <> fiddle здесь

Существует сто один альтернативный способ написания запроса.Что является самым быстрым, зависит от количества элементов, распределения данных, ограничений и, что наиболее важно, от доступных индексов.

Это специальное приложение .Вот арсенал приемов:

Один альтернативный , возможно, быстрее: создать представления или материализованные представления родительских таблиц, включая количество элементов:

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

.. затем, если все элементы совпадают, нам больше не нужно исключать дополнительные элементы.И у нас есть все столбцы из родительской таблицы в нашем распоряжении, чтобы вернуть то, что вы хотите вернуть ...

...