Рекурсия? Как я могу группировать заказы по общим пунктам - PullRequest
0 голосов
/ 21 февраля 2020

Настройка такова. У меня есть Orders и OrderDetails, а OrderDetails имеет номер позиции. Заказы имеют много OrderDetails. Из ~ 1000 ордеров с ~ 10 000 ордеров LineDetail мне нужны 16 лучших ордеров, которые имеют наибольшее количество общих предметов.

После недели исследований вот моя попытка. Когда я добрался до 8-й итерации, мне пришлось остановиться. Это было все oop, но я не мог понять, как динамически устанавливать имена временных таблиц. Я также не мог понять, как определить, когда у меня было 16 лучших заказов.


IF OBJECT_ID('tempdb..#PartNums') IS NOT NULL DROP Table #PartNums
--Gets the part number that is in the most orders
CREATE TABLE #PartNums (ctr int Identity, PartNum varchar(50), CONT int)
INSERT INTO #PartNums SELECT TOP(1) D.PartNum, Count(D.PartNum) AS CONT FROM OrderDetails D
group by  D.PartNum
order by CONT desc

--Gets the orders that have the number one part number in it
IF OBJECT_ID('tempdb..#Orders') IS NOT NULL DROP Table #Orders
CREATE TABLE #Orders ( Id int, Ord1 varchar(50), Ord2 varchar(50),PartNum varchar(50))
INSERT INTO #Orders SELECT O.Id,O.Ord1,O.Ord2, D.PartNum
FROM            OrderDetails D INNER JOIN
                         Orders O ON D.OrderId = O.Id
                         where D.PartNum IN (SELECT partNum from #PartNums WHERE ctr = 1)

-- Using just the orders that have the number 1 part number
-- Get the part number that is next most popular in the orders
--Exclude the first part number from the grouping

INSERT INTO #PartNums SELECT TOP(1)  D.PartNum, Count(D.PartNum) as CONT from OrderDetails D inner join #Orders O ON O.Id = D.OrderId
where NOT D.PartNum IN (SELECT partNum from #PartNums)
group by D.PartNum
order by CONT desc


--Gets the orders that have the number one part number in it
IF OBJECT_ID('tempdb..#Orders2') IS NOT NULL DROP Table #Orders2
CREATE TABLE #Orders2 ( Id int, Ord1 varchar(50), Ord2 varchar(50),PartNum varchar(50))
INSERT INTO #Orders2 SELECT O.Id,O.Ord1,O.Ord2, D.PartNum
FROM            OrderDetails D INNER JOIN
                         #Orders O ON D.OrderId = O.Id
                         where D.PartNum IN (SELECT partNum from #PartNums WHERE ctr = 2)


INSERT INTO #PartNums SELECT TOP(1)  D.PartNum, Count(D.PartNum) as CONT from OrderDetails D inner join #Orders2 O ON O.Id = D.OrderId
where NOT D.PartNum IN (SELECT partNum from #PartNums)
group by D.PartNum
order by CONT desc


--Gets the orders that have the number one part number in it
IF OBJECT_ID('tempdb..#Orders3') IS NOT NULL DROP Table #Orders3
CREATE TABLE #Orders3 ( Id int, Ord1 varchar(50), Ord2 varchar(50),PartNum varchar(50))
INSERT INTO #Orders3 SELECT O.Id,O.Ord1,O.Ord2, D.PartNum
FROM            OrderDetails D INNER JOIN
                         #Orders2 O ON D.OrderId = O.Id
                         where D.PartNum IN (SELECT partNum from #PartNums WHERE ctr = 3)


INSERT INTO #PartNums SELECT TOP(1)  D.PartNum, Count(D.PartNum) as CONT from OrderDetails D inner join #Orders3 O ON O.Id = D.OrderId
where NOT D.PartNum IN (SELECT partNum from #PartNums)
group by D.PartNum
order by CONT desc


--Gets the orders that have the number one part number in it
IF OBJECT_ID('tempdb..#Orders4') IS NOT NULL DROP Table #Orders4
CREATE TABLE #Orders4 ( Id int, Ord1 varchar(50), Ord2 varchar(50),PartNum varchar(50))
INSERT INTO #Orders4 SELECT O.Id,O.Ord1,O.Ord2, D.PartNum
FROM            OrderDetails D INNER JOIN
                         #Orders3 O ON D.OrderId = O.Id
                         where D.PartNum IN (SELECT partNum from #PartNums WHERE ctr = 4)


INSERT INTO #PartNums SELECT TOP(1)  D.PartNum, Count(D.PartNum) as CONT from OrderDetails D inner join #Orders4 O ON O.Id = D.OrderId
where NOT D.PartNum IN (SELECT partNum from #PartNums)
group by D.PartNum
order by CONT desc


--Gets the orders that have the number one part number in it
IF OBJECT_ID('tempdb..#Orders5') IS NOT NULL DROP Table #Orders5
CREATE TABLE #Orders5 ( Id int, Ord1 varchar(50), Ord2 varchar(50),PartNum varchar(50))
INSERT INTO #Orders5 SELECT O.Id,O.Ord1,O.Ord2, D.PartNum
FROM            OrderDetails D INNER JOIN
                         #Orders4 O ON D.OrderId = O.Id
                         where D.PartNum IN (SELECT partNum from #PartNums WHERE ctr = 5)

INSERT INTO #PartNums SELECT TOP(1)  D.PartNum, Count(D.PartNum) as CONT from OrderDetails D inner join #Orders5 O ON O.Id = D.OrderId
where NOT D.PartNum IN (SELECT partNum from #PartNums)
group by D.PartNum
order by CONT desc


--Gets the orders that have the number one part number in it
IF OBJECT_ID('tempdb..#Orders6') IS NOT NULL DROP Table #Orders6
CREATE TABLE #Orders6 ( Id int, Ord1 varchar(50), Ord2 varchar(50),PartNum varchar(50))
INSERT INTO #Orders6 SELECT O.Id,O.Ord1,O.Ord2, D.PartNum
FROM            OrderDetails D INNER JOIN
                         #Orders5 O ON D.OrderId = O.Id
                         where D.PartNum IN (SELECT partNum from #PartNums WHERE ctr = 6)

INSERT INTO #PartNums SELECT TOP(1)  D.PartNum, Count(D.PartNum) as CONT from OrderDetails D inner join #Orders6 O ON O.Id = D.OrderId
where NOT D.PartNum IN (SELECT partNum from #PartNums)
group by D.PartNum
order by CONT desc

--Gets the orders that have the number one part number in it
IF OBJECT_ID('tempdb..#Orders7') IS NOT NULL DROP Table #Orders7
CREATE TABLE #Orders7( Id int, Ord1 varchar(50), Ord2 varchar(50),PartNum varchar(50))
INSERT INTO #Orders7 SELECT O.Id,O.Ord1,O.Ord2, D.PartNum
FROM            OrderDetails D INNER JOIN
                         #Orders6 O ON D.OrderId = O.Id
                         where D.PartNum IN (SELECT partNum from #PartNums WHERE ctr = 7)


INSERT INTO #PartNums SELECT TOP(1)  D.PartNum, Count(D.PartNum) as CONT from OrderDetails D inner join #Orders7 O ON O.Id = D.OrderId
where NOT D.PartNum IN (SELECT partNum from #PartNums)
group by D.PartNum
order by CONT desc


--Gets the orders that have the number one part number in it
IF OBJECT_ID('tempdb..#Orders8') IS NOT NULL DROP Table #Orders8
CREATE TABLE #Orders8( Id int, Ord1 varchar(50), Ord2 varchar(50),PartNum varchar(50))
INSERT INTO #Orders8 SELECT O.Id,O.Ord1,O.Ord2, D.PartNum
FROM            OrderDetails D INNER JOIN
                         #Orders7 O ON D.OrderId = O.Id
                         where D.PartNum IN (SELECT partNum from #PartNums WHERE ctr = 7)

SELECT DISTINCT OrderId FROM OrderDetails where PartNum in (SELECT partNum from #PartNums);

SELECT * FROM #Orders8;

1 Ответ

0 голосов
/ 22 февраля 2020

Поскольку DDL не был задан, я создаю «простой» пример для этого FIDDLE

SELECT 
    Parts, 
    STRING_AGG(id,',') WITHIN GROUP (ORDER BY Id) as PartsCombinationInOrder,
    COUNT(*)           as NumberOfTimes
FROM (
  SELECT
     Orders.Id, STRING_AGG(Order_details.Part_no,',') WITHIN GROUP (ORDER BY Order_details.Part_no) as Parts
  FROM Orders
  INNER JOIN Order_Details on Order_Details.Order_id = Orders.Id
  GROUP BY Orders.Id
) x
GROUP BY x.Parts
ORDER BY COUNT(*) DESC;

Краткое описание работы STRING_AGG () см. STRING_AGG

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

РЕДАКТИРОВАТЬ:

Еще одна попытка найти все комбинации статей в заказах

WITH Orders as(
    SELECT * FROM (VALUES(1),(2),(3),(4)) Orders(Order_Id)
    )
    , Order_Details as (
    SELECT * FROM (VALUES
            (1,1,1),(1,2,2),(1,3,3),(1,4,4),(1,5,5),(1,6,6),
            (2,1,1),(2,2,2),(2,3,3),(2,4,4),(2,5,5),(2,6,6),
            (3,1,1),(3,2,2),(3,3,3),(3,4,4),(3,5,5),(3,6,6), 
            (4,1,1),(4,2,2),(4,3,7),(4,4,3),(4,5,5),(4,6,6)
        ) Order_Details(Order_id,Line_no,Part_no) 
    ),combinations as (
    SELECT o.Order_Id, 
        od1.Line_no as Line1, od1.Part_no as Part1, 
        od2.Line_no as Line2, od2.Part_no as Part2, 
        od3.Line_no as Line3, od3.Part_no as Part3
    FROM Orders o
    INNER JOIN Order_Details od1 ON od1.Order_id = o.Order_Id       
    INNER JOIN Order_Details od2 ON od2.Order_id = o.Order_Id AND od2.Line_no > od1.Line_no
    INNER JOIN Order_Details od3 ON od3.Order_id = o.Order_Id AND od3.Line_no > od2.Line_no AND od3.Line_no > od1.Line_no
    )
SELECT c.Order_Id, c.Part1, c.Part2, c.Part3, q.count
FROM combinations c
CROSS APPLY (SELECT count(*) as count FROM combinations q WHERE q.Part1=c.Part1 and q.Part2=c.Part2 and q.Part3=c.Part3) q
ORDER BY q.count DESC,c.Part1,c.Part2,c.Part3, c.Order_Id;

вывод:

Order_Id    Part1       Part2       Part3       count
----------- ----------- ----------- ----------- -----------
1           1           2           3           4
2           1           2           3           4
3           1           2           3           4
4           1           2           3           4
1           1           2           5           4
2           1           2           5           4
✂✂✂✂✂
4           3           5           6           4
1           1           2           4           3
2           1           2           4           3
3           1           2           4           3
1           1           3           4           3
2           1           3           4           3
3           1           3           4           3
1           1           4           5           3
✂✂✂✂✂
2           4           5           6           3
3           4           5           6           3
4           1           2           7           1
4           1           7           3           1
4           1           7           5           1
4           1           7           6           1
4           2           7           3           1
4           2           7           5           1
4           2           7           6           1
4           7           3           5           1
4           7           3           6           1
4           7           5           6           1

(80 rows affected)

Я думаю, что эта часть легко расширяется, чтобы иметь 'Part4', 'Part5' и др. c, что позволяет сочетать более 3 статей, как показано выше.

...