Вывод только надмножество строк - PullRequest
1 голос
/ 17 апреля 2019

Данная таблица Products (ProductGroupId INT NOT NULL, ProductId INT NOT NULL) Моя задача - выводить только строки с ProductGroupId, который является расширенным набором других групп продуктов

Например, для данных


    ProductGroupId ProductId
        1             101
        1             102
        1             103
        2             101
        3             102
        4             102
        4             103
        5             104

Я ожидаю


    ProductGroupId ProductId
        1             101
        1             102
        1             103
        5             104

Поскольку ProductGroupId = 1 является расширенным набором групп продуктов 2,3,4

Мое решение на данный момент:

Первый : мы генерируем пары из двух ProductGroupId, которые могут содержать друг друга

Второй : Мы проверяем, содержит ли расширенный набор ProductGroupId (Parent) все ProductIds другого ProductGroupId (Child)) и Parent имеет больше ProductIds, чем Child.

К этому моменту у нас будет:

    ProductGroupId ProductId
        1             101
        1             102
        1             103

Поэтому необходимо добавить строки, в которых ProductId уникально идентифицирует ProductGroupId

    ProductGroupId ProductId
        5             104

После UNION у нас есть

    ProductGroupId ProductId
        1             101
        1             102
        1             103
        5             104

Код

DECLARE @products TABLE
(
    ProductGroupId INT NOT NULL,
    ProductId INT NOT NULL
)

INSERT INTO @products
SELECT 1, 101
UNION
SELECT 1, 102
UNION
SELECT 1, 103
UNION
SELECT 2, 101
UNION
SELECT 3, 102
UNION 
SELECT 4, 102
UNION
SELECT 4, 103
UNION
SELECT 5, 104

;WITH possible_pairs
AS
(
    SELECT ParentId = parent.ProductGroupId,
        ChildId = child.ProductGroupId
    FROM @products parent
        JOIN @products child
            ON child.ProductId = parent.ProductId
    WHERE parent.ProductGroupId <> child.ProductGroupId
    GROUP BY parent.ProductGroupId, child.ProductGroupId
)
, supersets
AS
(
    SELECT ParentId, ChildId
    FROM possible_pairs pp
    WHERE NOT EXISTS
    (
        -- All ProductIds in child exist in parent
        SELECT ProductId
        FROM @products p_child
        WHERE p_child.ProductGroupId = pp.ChildId
        EXCEPT
        SELECT ProductId
        FROM @products p_parent
        WHERE p_parent.ProductGroupId = pp.ParentId
    )
    AND EXISTS
    (
        -- Parent has more ProductIds then child
        SELECT ProductId
        FROM @products p_parent
        WHERE p_parent.ProductGroupId = pp.ParentId
        EXCEPT
        SELECT ProductId
        FROM @products p_child
        WHERE p_child.ProductGroupId = pp.ChildId
    )
)
SELECT p.*
FROM @products p
    JOIN supersets s
        ON p.ProductGroupId = s.ParentId
WHERE NOT EXISTS
(
    -- We need to filter product groups that at the same time are superset and subset. 
    -- Eg. product group 4, that is superset for 3 and subset for 1
    SELECT s2.ChildId
    FROM supersets s2
    WHERE s2.ChildId = s.ParentId
)
/*
    ProductGroupId ProductId
        1             101
        1             102
        1             103
*/
UNION 
SELECT p.*
FROM @products p
WHERE NOT EXISTS
(
    SELECT *
    FROM supersets s2
    WHERE s2.ParentId = p.ProductGroupId
        OR s2.ChildId = p.ProductGroupId
)
/*
    ProductGroupId ProductId
        5             104
*/

Ответы [ 2 ]

0 голосов
/ 17 апреля 2019

Я проверил ниже в SQL Server, и он работает нормально.Я создал еще одну таблицу @productSuper для хранения надмножества.

    DECLARE @products TABLE (ProductGroupId INT NOT NULL, ProductId INT NOT NULL)

INSERT INTO @products
SELECT 1, 101
UNION
SELECT 1, 102
UNION
SELECT 1, 103
UNION
SELECT 2, 101
UNION
SELECT 3, 102
UNION 
SELECT 4, 102
UNION
SELECT 4, 103
UNION
SELECT 5, 104

SELECT *
FROM @products;

DECLARE @productSuper TABLE (ProductGroupId INT NOT NULL, ProductId INT NOT NULL)
DECLARE @currentProductGroupId INT = 1
DECLARE @maxProductGroupId INT

SET @maxProductGroupId = (
        SELECT Max(ProductGroupId)
        FROM @products
        )

INSERT INTO @productSuper
SELECT ProductGroupId, ProductId
FROM @products AS parent
WHERE ProductGroupId = @currentProductGroupId -- Assuming to start this as start parent group

WHILE @currentProductGroupId <= @maxProductGroupId
BEGIN
    INSERT INTO @productSuper
    SELECT ProductGroupId, ProductId
    FROM @products AS child
    WHERE ProductGroupId = @currentProductGroupId AND NOT EXISTS (
            SELECT ProductId
            FROM @productSuper AS Super
            WHERE Super.ProductId = child.ProductId
            )

    SET @currentProductGroupId += 1
END

SELECT *
FROM @productSuper
0 голосов
/ 17 апреля 2019

использование row_number()

select * from
(
select productid,ProductGroupId, row_number() over(partition by productid order by ProductGroupId) as rn
from tablename
)A where rn=1
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...