查找不按顺序记录

时间:2016-05-12 17:27:48

标签: sql sql-server sequential

我们公司向我们的承包商发行票据簿,我们跟踪书籍和书籍中的个人票证(每本书25张),以确保没有遗漏任何内容。我们的书是数字,书中的个人票也是。这本书与书中的第一张票相同。例如,票券101将包含票证101-125,票证簿126将包含票证126-150等......

每个承包商都会收到一本书,他们必须按顺序使用他们的门票。如果承包商有书101,他们必须按照101,102,103等的顺序使用门票......为了强制执行此操作,我们通过查找无序门票和显示来跟踪门票输入我们的数据库。这在报告中。例如,如果票证进入101,103,则必须在此报告中显示102作为缺失。

目前,这是我运行的SQL查询来检查:

select TicketNum, TicketBookNum, UnitID, DateIssued, IssuedBy
from TicketBooks a
where Used='No'
and TicketNum < (select MAX(b.TicketNum)
        from TicketBooks b
        where b.Used='Yes'
        and b.UnitID=a.UnitID
        and BookType='Truck' or BookType='Work'
        and a.DateIssued <= b.DateIssued)
and BookType='Truck'
or BookType='Work'
order by DateIssued desc

此查询检索所有未使用的故障单,并将它们与已使用的故障单进行比较,以查看未使用的故障单号是否低于设备(承包商)的最高使用故障单号。如果它更低,则表示已跳过票证。再一个例子:使用了101,103,现在最高使用的票号是103而未使用的是102,这意味着它更低并且将显示在报告中。

我们公司有可能退票,并重新发行。在这种情况下,此报告的新售票簿编号可能不正常。例如:

  • 承包商A正在使用票券201
  • 承包商B离开公司并返回机票簿151
  • 承包商A完成票务簿201
  • 承包商A从办公室获得新的售票簿。这是票书151

现在这个SQL语句将把所有书籍151都归还为缺失,因为书籍201中使用的所有票证都是更高的票号和书籍151.我可以尝试只查找序列中的不按顺序记录。书,但我需要能够判断书中的最后一张票是否被跳过,并且是否使用了新书中的第一张票。

我想我需要做的是以某种方式考虑到本书的DateIssued以返回遗失的门票。

我一直在解决这个问题,所以任何帮助都不仅仅是非常感激。

这是我们数据库中的示例数据:

TicketNum  | TicketBookNum   |  UnitID        |      DateIssued    |   IssuedBy
----------------------------------------------------------------------------
105073          105051          151       2016-04-23 10:02:40.000   kbusch
105074          105051          151       2016-04-23 10:02:40.000   kbusch
105075          105051          151       2016-04-23 10:02:40.000   kbusch
102801          102801          117       2016-04-22 10:19:23.000   kbusch
102802          102801          117       2016-04-22 10:19:23.000   kbusch
102803          102801          117       2016-04-22 10:19:23.000   kbusch
102804          102801          117       2016-04-22 10:19:23.000   kbusch
102805          102801          117       2016-04-22 10:19:23.000   kbusch
102806          102801          117       2016-04-22 10:19:23.000   kbusch
102807          102801          117       2016-04-22 10:19:23.000   kbusch
102808          102801          117       2016-04-22 10:19:23.000   kbusch
102809          102801          117       2016-04-22 10:19:23.000   kbusch
102810          102801          117       2016-04-22 10:19:23.000   kbusch
102811          102801          117       2016-04-22 10:19:23.000   kbusch
102812          102801          117       2016-04-22 10:19:23.000   kbusch
102813          102801          117       2016-04-22 10:19:23.000   kbusch
102814          102801          117       2016-04-22 10:19:23.000   kbusch
102815          102801          117       2016-04-22 10:19:23.000   kbusch
102816          102801          117       2016-04-22 10:19:23.000   kbusch
102817          102801          117       2016-04-22 10:19:23.000   kbusch
102818          102801          117       2016-04-22 10:19:23.000   kbusch
102819          102801          117       2016-04-22 10:19:23.000   kbusch
102820          102801          117       2016-04-22 10:19:23.000   kbusch
102821          102801          117       2016-04-22 10:19:23.000   kbusch
102822          102801          117       2016-04-22 10:19:23.000   kbusch
102823          102801          117       2016-04-22 10:19:23.000   kbusch
102824          102801          117       2016-04-22 10:19:23.000   kbusch
102825          102801          117       2016-04-22 10:19:23.000   kbusch

如您所见,票务本102801中的所有25张票都存在。造成这种情况的原因是因为该单位过去已经完成了票务手册103001。机票102801是无序发出的。我想要的预期输出不会显示这本书,因为它是最新的,但如果没有使用他们上一本书中的最后一张票,并且使用了本书中的第一张票,则该跳过的票将显示在此报告中。即使本书中的门票数量低于上一本书的数量。

为Tom H的解决方案编辑:一些丢失的门票没有显示。例如:

单元403具有于2015年4月2日发行的票券94801.单元403使用了票号94801-94815,但尚未使用票证94816-94825。此外,403单元于2015年7月1日发行了售票簿96751并使用了票据96751-96762,因此我需要本报告中显示的票证94816-94825。

另一个例子:

第142单元于2015年10月2日获得了售票簿99751,并使用了门票99751-99754而不是书中的最后一张票,票99775.从那时起,142号单元已经发行了几本书并使用了这些书中的所有票;因此,票据99775应显示在报告中。

EDIT2:

下面是显示403票的数据的图片

enter image description here

下面显示了您提供的查询返回的记录

enter image description here

2 个答案:

答案 0 :(得分:1)

我相信这会给你你想要的东西。它会创建所有可能的票号的结果集,然后检查您的表中没有哪些票号。数字的两个CTE只是生成0到24的数字。请注意,如果您的票证大小可能会发生变化,这不是一个好的解决方案。此外,您可能应该有单独的TicketBooksTicketsUsedTickets表。

;WITH CTE_TBs AS (
    SELECT
        TicketBookNum,
        UnitID,
        CASE WHEN LEAD(DateIssued) OVER (PARTITION BY UnitID ORDER BY DateIssued) IS NULL THEN 1 ELSE 0 END AS LastBook,
        MAX(TicketNum) AS MaxTicketNum
    FROM
        (SELECT DISTINCT TicketBookNum, TicketNum, UnitID, DateIssued FROM TicketBooks) SQ      -- Necessary because your database isn't properly normalized
    GROUP BY
        TicketBookNum, UnitID, DateIssued
),
CTE_Nums_1_5 AS (SELECT 1 AS num UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5),
CTE_Nums AS (SELECT ROW_NUMBER() OVER (ORDER BY T1.num) - 1 AS num FROM CTE_Nums_1_5 AS T1 CROSS JOIN CTE_Nums_1_5 AS T2)
SELECT
    TB.UnitID,
    TB.TicketBookNum,
    TB.TicketBookNum + N.num AS MissingTicketNum
FROM
    CTE_TBs TB
LEFT OUTER JOIN CTE_Nums N ON (TB.TicketBookNum + N.num <= TB.MaxTicketNum) OR TB.LastBook = 0
WHERE
    NOT EXISTS (SELECT * FROM TicketBooks WHERE TicketNum = TB.TicketBookNum + N.num)

答案 1 :(得分:1)

一旦承包商开始使用书籍,他必须在开始新书之前使用所有门票。

因此,对于给定的承包商,数据库中只有一本书(或没有)可以少于25行。如果给定的承包商在数据库中有多于一行且少于25行 - 则存在问题。

请注意,上述逻辑在发行书籍或承包商开始使用时并不关心。

CTE_IncompleteBooks将所有门票分为承包商和书籍,计算每本书中使用的门票数量,并仅留下少于25张门票的书籍。

然后我们需要过滤那些只有一本不完整图书的承包商。

CTE_IncompleteBooksCount为每个承包商计算不完整的图书。

最终SELECT仅返回拥有多本不完整图书的承包商。

WITH
CTE_IncompleteBooks
AS
(
    SELECT
        UnitID
        ,TicketBookNum
        ,COUNT(*) AS UsedTicketCount
    FROM TicketBooks
    --WHERE Used=Yes -- not clear from the question whether you need this filter
    GROUP BY
        UnitID
        ,TicketBookNum
    HAVING COUNT(*) < 25
)
,CTE_IncompleteBooksCount
AS
(
    SELECT
        UnitID
        ,TicketBookNum
        ,UsedTicketCount
        ,COUNT(*) OVER (PARTITION BY UnitID) AS IncompleteBookCount
    FROM CTE_IncompleteBooks
)
SELECT
    UnitID
    ,TicketBookNum
    ,UsedTicketCount
    ,IncompleteBookCount
FROM CTE_IncompleteBooksCount
WHERE IncompleteBookCount > 1
ORDER BY
    UnitID
    ,TicketBookNum
;