SQL Server,按日期范围分配键

时间:2016-12-19 10:11:56

标签: sql-server-2014

票据

billdate    billno
-------------------
2015-04-13  8090
2015-03-11  8089
2015-02-14  8088
2015-02-10  8087

TRANSACTIONS

transactiondate billno
-----------------------
2015-04-15      8090
2015-04-13      8090
2015-04-12      8090
2015-04-10      8090
2015-03-12      8090
2015-03-11      8089
2015-03-10      8089
2015-02-11      8088
2015-02-01      8087

我需要将billno分配给事务订单项,如上所述。账单范围之外的交易 - 即2015-04-13之外的那些在日期范围窗口内不匹配的交易需要与最后一个可用账单一起使用。反之亦然,诸如2015-02-01等底端的交易需要与最后一张账单一起使用。

不太确定如何进行此查询。

1 个答案:

答案 0 :(得分:0)

您可以使用游标或递归公用表表达式(CTE)。我建议说远离游标,所以这里是CTE的一个例子。

此查询中有很多内容,所以我会尝试将其分解。

如果对这两个表执行FULL JOIN,你会得到一些看起来像答案的东西,但是有很多NULL值 每笔交易:

DECLARE @Bills TABLE (billdate DATE, billno INT)
DECLARE @Transactions TABLE (transactiondate DATE)

INSERT INTO @Bills (billdate, billno) VALUES ('2015-04-13',  8090)
INSERT INTO @Bills (billdate, billno) VALUES ('2015-03-11',  8089)
INSERT INTO @Bills (billdate, billno) VALUES ('2015-02-14',  8088)
INSERT INTO @Bills (billdate, billno) VALUES ('2015-02-10',  8087)

INSERT INTO @Transactions (transactiondate) VALUES ('2015-04-15') 
INSERT INTO @Transactions (transactiondate) VALUES ('2015-04-13')      
INSERT INTO @Transactions (transactiondate) VALUES ('2015-04-12')      
INSERT INTO @Transactions (transactiondate) VALUES ('2015-04-10')      
INSERT INTO @Transactions (transactiondate) VALUES ('2015-03-12')      
INSERT INTO @Transactions (transactiondate) VALUES ('2015-03-11')      
INSERT INTO @Transactions (transactiondate) VALUES ('2015-03-10')      
INSERT INTO @Transactions (transactiondate) VALUES ('2015-02-11')      
INSERT INTO @Transactions (transactiondate) VALUES ('2015-02-01')  

SELECT
    b.billdate,
    b.billno,
    t.transactiondate,
    COALESCE(b.billdate, t.transactiondate) AS [eventdate]
FROM @Bills b
FULL JOIN @Transactions t
    ON b.billdate = t.transactiondate
ORDER BY eventdate DESC

Full Join Results

要达到解决方案,我们需要将前一个事件的billno拉到当前事件,如果当前事件的billno是NULL。

为了实现这一点,我们可以使用递归CTE。递归CTE不允许在(http://msdn.microsoft.com/en-us/library/ms175972.aspx)内进行连接,因此我必须将FULL JOIN重构为UNION ALL,别名为" AnchorSet"。该 CTE是INNER JOINed自身,与rownumber / eventdate的偏移量,而billno IS NULL。

DECLARE @Bills TABLE (billdate DATE, billno INT)
DECLARE @Transactions TABLE (transactiondate DATE)

INSERT INTO @Bills (billdate, billno) VALUES ('2015-04-13',  8090)
INSERT INTO @Bills (billdate, billno) VALUES ('2015-03-11',  8089)
INSERT INTO @Bills (billdate, billno) VALUES ('2015-02-14',  8088)
INSERT INTO @Bills (billdate, billno) VALUES ('2015-02-10',  8087)

INSERT INTO @Transactions (transactiondate) VALUES ('2015-04-15') 
INSERT INTO @Transactions (transactiondate) VALUES ('2015-04-13')      
INSERT INTO @Transactions (transactiondate) VALUES ('2015-04-12')      
INSERT INTO @Transactions (transactiondate) VALUES ('2015-04-10')      
INSERT INTO @Transactions (transactiondate) VALUES ('2015-03-12')      
INSERT INTO @Transactions (transactiondate) VALUES ('2015-03-11')      
INSERT INTO @Transactions (transactiondate) VALUES ('2015-03-10')      
INSERT INTO @Transactions (transactiondate) VALUES ('2015-02-11')      
INSERT INTO @Transactions (transactiondate) VALUES ('2015-02-01')      

SELECT
    b.billdate,
    b.billno,
    t.transactiondate,
    COALESCE(b.billdate, t.transactiondate) AS [eventdate]
FROM @Bills b
FULL JOIN @Transactions t
    ON b.billdate = t.transactiondate
ORDER BY eventdate DESC

;WITH AnchorSet AS 
(
    SELECT
        MIN(billdate) AS billdate,
        MIN(billno) AS billno,
        MIN(transactiondate) as transactiondate,
        eventdate
    FROM
    (
        SELECT
            billdate,
            billno,
            transactiondate,
            COALESCE(billdate, transactiondate) AS [eventdate]
        FROM
        (
            SELECT
                billdate,
                billno,
                NULL AS [transactiondate]
            FROM @Bills b

            UNION ALL

            SELECT
                NULL AS billdate,
                NULL AS billno,
                transactiondate
            FROM @transactions t
        )innerset
    )innerset2
    GROUP BY eventdate  
)
, cte (billdate, billno, transactiondate, eventdate, rownumber, recursionlevel)
AS
(
    SELECT
        billdate,
        billno,
        transactiondate,
        eventdate,
        ROW_NUMBER() OVER (ORDER BY eventdate DESC) AS [rownumber],
        0 AS [recursionlevel]
    FROM 
        AnchorSet
    WHERE 
        billno IS NULL

    UNION ALL

    SELECT
        COALESCE(RecursiveSet.billdate, cte.billdate) AS [billdate],
        COALESCE(RecursiveSet.billno, cte.billno) AS [billno],
        COALESCE(RecursiveSet.transactiondate, cte.transactiondate) AS [transactiondate],
        COALESCE(RecursiveSet.eventdate, cte.eventdate) AS [eventdate],
        COALESCE(RecursiveSet.rownumber, cte.rownumber) AS [rownumber],
        recursionlevel + 1 AS [recursionlevel]
    FROM
    (
        SELECT
            billdate,
            billno,
            transactiondate,
            eventdate,
            ROW_NUMBER() OVER (ORDER BY eventdate DESC) AS [rownumber]
        FROM AnchorSet      
    )RecursiveSet
    INNER JOIN cte ON cte.rownumber = RecursiveSet.rownumber - 1
)

SELECT
    billno,
    transactiondate
FROM
(
    SELECT
        billno,
        transactiondate,
        ROW_NUMBER() OVER (PARTITION BY transactiondate ORDER BY transactiondate DESC, rownumber DESC, recursionlevel DESC, billno DESC) AS [finalrownumber]
    FROM cte
)SomeSet
WHERE finalrownumber = 1
ORDER BY transactiondate DESC

Recursive CTE Results

这将为您提供您正在寻找的内容,但最终交易将在最后一个账单之后为空。您可以创建查询以获取最新的billno,并将其应用于事务的billnos的剩余NULL值。我将此作为练习留给读者。