Запрос, чтобы получить элементы, которые находятся в списке, а не в списке - PullRequest
4 голосов
/ 23 октября 2019

У меня есть таблица 'tbl_Items' с нижними столбцами

    [Id] [int] NULL,
    [ItemNo] [varchar](50) NULL,
    [TotalPieces] [int] NULL

и другая таблица 'tbl_ItemPieces' с нижними столбцами

    [Id] [int] NULL,
    [ItemId] [int] NULL,
    [PieceNo] [int] NULL

примерные значения, как показано ниже:

tbl_Items

Id  ItemNo  TotalPieces
1   1001    5
2   1002    3
3   1003    4

tbl_ItemPieces

Id  ItemId  PieceNo
1   1       1
2   1       2
3   2       1
4   2       3
5   3       3
6   3       4

Я использовал приведенный ниже запрос, чтобы получить количество доступных штук и общее количество штук и количество доступных штук в виде строки, разделенной запятой.

    select 
        a.ItemNo, COUNT(b.PieceNo) ActualPieces, a.TotalPieces,    

        STUFF((SELECT ', ' + CAST( PieceNo as varchar(50))
        FROM tbl_ItemPieces b 
        WHERE b.itemId = a.Id
        FOR XML PATH('')), 1, 2, '')    

    from tbl_Items a
    inner join tbl_ItemPieces b
    on a.Id = b.itemId
    group by a.ItemNo, a.TotalPieces, a.Id

, результат которого ниже

ItemNo  ActualPieces    TotalPieces AvailablePieces
1001    2                  5            1, 2
1002    2                  3            1, 3
1003    2                  4            3, 4

Мне нужен еще один столбец в виде строки, разделенной запятыми, которая содержит номера частей, которых нет в таблице, скажем, если 5 - общая сумма, и части включеныравны 1,3, тогда значение этого столбца равно '2,4,5'

ожидаемый результат

ItemNo  ActualPieces    TotalPieces  AvailablePieces NotAvailablePieces
1001    2               5            1, 2            3,4,5
1002    2               3            1, 3            2
1003    2               4            3, 4            1,2

Ответы [ 2 ]

2 голосов
/ 23 октября 2019

Единственный способ добиться этого с помощью recursive query.

- сначала сгенерировать PieceNo с использованием cte, а затем join результат вашего исходного запроса, исключая PieceNo.

with cte as (
        select a.id, (TotalPieces - count(1)) as ct, 1 as ctr, TotalPieces
        from tbl_Items a
        inner join tbl_ItemPieces b on a.Id = b.itemId
        group by a.id, TotalPieces
        union all 
        select id, ct , ctr + 1, TotalPieces from cte where ctr < TotalPieces
    ) select a.ItemNo, COUNT(b.PieceNo) ActualPieces, a.TotalPieces,
        STUFF((select ', ' + CAST( PieceNo as varchar(50))
            from tbl_ItemPieces b 
            where b.itemId = a.Id
            FOR XML PATH('')), 1, 2, ''),
        STUFF((select ', ' + CAST( ctr as varchar(50))
            from cte b 
            where b.id = a.Id and concat(id, ctr) not in (select concat(itemid, pieceno) from tbl_ItemPieces)
            FOR XML PATH('')), 1, 2, '')
        from tbl_Items a
        inner join tbl_ItemPieces b
        on a.Id = b.itemId
        group by a.ItemNo, a.TotalPieces, a.Id
2 голосов
/ 23 октября 2019

В приведенном ниже решении используется recursive cte (cte_AllPieceNo) для создания списка возможных PieceNo для каждого элемента

. Оттуда просто используйте его и проверьте наличие NOT EXISTS() в tbl_ItemPieces

; with
cte_AllPieceNo as                 -- Added this
(
    select  Id, TotalPieces, PieceNo = 1
    from    tbl_Items
    union all
    select  Id, TotalPieces, PieceNo = PieceNo + 1
    from    cte_AllPieceNo
    where   PieceNo < TotalPieces
)
SELECT   
    a.ItemNo, 
    COUNT(b.PieceNo) ActualPieces, 
    a.TotalPieces,
    STUFF(( SELECT  ', ' + CAST( PieceNo as varchar(50) )
            FROM    tbl_ItemPieces b 
            WHERE   b.ItemId = a.Id
            FOR XML PATH('')), 1, 2, '') as AvailablePieces,
    STUFF(( SELECT  ', ' + CAST( c.PieceNo as varchar(50) ) -- added this
            FROM    cte_AllPieceNo c
            WHERE   c.Id    = a.Id
            AND     NOT EXISTS
                    (
                        SELECT  *
                        FROM    tbl_ItemPieces d
                        WHERE   d.ItemId    = c.Id 
                        AND     d.PieceNo   = c.PieceNo
                    )
            FOR XML PATH('')),1,2, '') as NotAvailablePieces
FROM    tbl_Items a
        INNER JOIN tbl_ItemPieces b on a.Id = b.ItemId
GROUP BY a.ItemNo, 
         a.TotalPieces, 
         a.Id

если у вас есть tally table, вы можете использовать его для замены recursive cte

Здесь приведен раздел кода, который использует таблицу подсчета.

cte_AllPieceNo as
(
    select  Id, PieceNo = n
    from    tbl_Items
            cross join tally
    where   n >= 1
    and     n <= TotalPieces
)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...