T-SQL возвращает все комбинации таблицы

В другом вопросе я спросил обо всех возможных трехсторонних комбинациях таблицы, предполагая, что есть 3 статьи. В этом вопросе я хотел бы еще больше расширить проблему, чтобы вернуть все n-сторонние комбинации таблицы с n различными статьями. У одного товара может быть несколько поставщиков. Моя цель состоит в том, чтобы иметь группы комбинаций с каждой группой, имеющей каждый артикль.

Ниже приведен пример таблицы, но имейте в виду, что статей может быть больше, чем эти 3.

+---------+----------+
| Article | Supplier |
+---------+----------+
|    4711 | A        |
|    4712 | B        |
|    4712 | C        |
|    4712 | D        |
|    4713 | C        |
|    4713 | E        |
+---------+----------+

Для приведенного выше примера было бы возможно 6 пар комбинаций с 18 наборами данных. Ниже показано, как должно выглядеть решение для приведенного выше примера:

+----------------+---------+----------+
| combination_nr | article | supplier |
+----------------+---------+----------+
|              1 |    4711 | A        |
|              1 |    4712 | B        |
|              1 |    4713 | C        |
|              2 |    4711 | A        |
|              2 |    4712 | B        |
|              2 |    4713 | E        |
|              3 |    4711 | A        |
|              3 |    4712 | C        |
|              3 |    4713 | C        |
|              4 |    4711 | A        |
|              4 |    4712 | D        |
|              4 |    4713 | E        |
|              5 |    4711 | A        |
|              5 |    4712 | D        |
|              5 |    4713 | C        |
|              6 |    4711 | A        |
|              6 |    4712 | D        |
|              6 |    4713 | E        |
+----------------+---------+----------+

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

Вопросы: Вам нужно ровно "n" комбинаций? Или "до n"? Какую роль играют поставщики? Они действительно не кажутся важными.

Gordon Linoff 10.12.2020 16:40

До n решений. Роль поставщика заключается в том, что у одного товара может быть несколько поставщиков. Поэтому мне нужно, чтобы каждая статья была в каждой группе, а количество групп зависит от того, сколько поставщиков у статей. Например, если бы у меня был только 1 товар, но 3 разных поставщика, у нас было бы 3 «группы».

Tom el Safadi 10.12.2020 16:45
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
0
2
292
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Если вы хотите сгенерировать все комбинации статей, вы можете использовать рекурсивное CTE. Однако трюк состоит в том, чтобы хранить все идентификаторы на каждом уровне, но для этого требуется использовать строку (какого-то рода).

Итак, для статей вы можете сделать:

with a as (
      select distinct article from t
     ),
     cte as (
      select a.article, 1 as lev, convert(varchar(max), article) as grp
      from a
      union all
      select a.article, lev + 1, concat(grp, ':', a.article)
      from cte join
           a
           on a.article > cte.article
     )
select cte.combination_number, s.value
from (select cte.*, row_number() over (order by lev, grp) as combination_number
      from cte
     ) cte cross apply
     string_split(grp, ':') s
order by cte.combination_number, s.value;

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

Здесь db<>рабочий пример.

Обновлено:

Если вы хотите, чтобы пары поставщиков товаров рассматривались как единое целое, но разные товары в каждой комбинации, вы можете настроить приведенный выше запрос:

with a as (
      select distinct article from t
     ),
     cte as (
      select a.article, 1 as lev, convert(varchar(max), article) as grp
      from a
      union all
      select a.article, lev + 1, concat(grp, ':', a.article)
      from cte join
           a
           on a.article > cte.article
     )
select cte.combination_number, s.value
from (select cte.*, row_number() over (order by lev, grp) as combination_number
      from cte
     ) cte cross apply
     string_split(grp, ':') s
order by cte.combination_number, s.value;

Скрипт db<> также имеет это решение.

Ответ принят как подходящий

Чтобы создать этот результат, вам нужно определить две вещи:

  • Набор данных с идентификаторами для количества возможных комбинаций.
  • Способ определить, когда пара «товар/поставщик» соответствует идентификатору комбинации.

Для того, чтобы выяснить набор комбинаций, вам нужно сначала выяснить количество комбинаций. Для этого вам нужно выяснить размер каждого набора поставщиков для каждого артикула. Получение размера легко достигается с помощью агрегатной функции count, но для того, чтобы вычислить комбинации, мне нужно произведение всех значений, что не так просто сделать. К счастью, в другом SO-вопросе был ответ о том, как это сделать.

Теперь, когда количество комбинаций определено, необходимо сгенерировать идентификаторы. В TSQL нет хорошего способа сделать это. В итоге я использовал простой рекурсивный CTE. У этого есть недостаток, заключающийся в том, что он может создавать только до 32767 комбо. Есть и другие способы получения этих значений, если вам нужно больше.

Чтобы определить, когда пара "товар/поставщик" совпадает с комбинацией, я использую оконную функцию ROW_NUMBER, разбивающую по артикулам и отсортированную по поставщикам, чтобы получить порядковый номер для каждой пары. Затем он использует старый трюк с использованием модуля числа комбинации по порядковому номеру, чтобы определить, отображается ли пара.

По-прежнему существует проблема, заключающаяся в том, что нет гарантии, что избыточные пары не будут объединены в пары. Чтобы решить эту проблему, был добавлен CTE, который вычисляет количество возможных комбинаций, предшествующих артикулу. Намерение состоит в том, чтобы значение в более поздней статье повторялось количество раз для комбинации, прежде чем будет отображаться следующее по порядку. Я назвал это множителем (хотя я делю на него comboId), и это то, что обеспечит отличные результаты.

WITH ComboId AS (
    -- Product from https://stackoverflow.com/questions/3912204/why-is-there-no-product-aggregate-function-in-sql
    SELECT 0 [ComboId], exp (sum (log (SequenceCount))) MaxCombos
    FROM (
        SELECT COUNT(*) SequenceCount
        FROM src 
        GROUP BY Article
    ) x
    UNION ALL
    SELECT ComboId + 1, MaxCombos
    FROM ComboId
    WHERE ComboId + 1 < MaxCombos
)
, Multiplier AS (
    SELECT s.Article
        , COALESCE(exp (sum (log (SequenceCount)) OVER (ORDER BY Article ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING)), 1) [Multiplier]
    FROM (
        SELECT Article, COUNT(*) SequenceCount
        FROM src
        GROUP BY Article
    ) s
)
, Sequenced AS (
    SELECT s.Article, s.Supplier, m.Multiplier
        , ROW_NUMBER() OVER (PARTITION BY s.Article ORDER BY s.Supplier) - 1 ArtSupplierSeqNum
        , COUNT(*) OVER (PARTITION BY s.Article) MaxArtSupplierSeqNum
    FROM src s
    INNER JOIN Multiplier m ON m.Article = s.Article
)
SELECT c.ComboId + 1 [ComboId], s.Article, s.Supplier
FROM ComboId c
INNER JOIN Sequenced s ON s.ArtSupplierSeqNum = CAST(c.ComboId / Multiplier as INT) % s.MaxArtSupplierSeqNum
ORDER BY ComboId, Article
OPTION (MAXRECURSION 32767)

Это именно то, что мне было нужно! Большое спасибо за Вашу помощь!!

Tom el Safadi 11.12.2020 13:13

Другие вопросы по теме