使用先前日期的嵌套大小写进行更新

时间:2013-10-28 14:21:36

标签: sql sql-server sql-update case

我有一个包含以下列的表:

Document_ID, Customer_ID, Date, LoanedMoney.

我已经创建了一个新的LoanedDate列。我希望以下列方式填写日期LoanedDate

  1. 如果上一个Date或上一个日期不存在(并不总是指-1天),且Document_IDCustomer_ID为空,则为>复制LoanedMoney值并使用该值更新;
  2. 如果具有相同DateDocument_ID的上一个Customer_ID(并不总是指-1天)不为空 - >复制它并使用该值更新;
  3. 否则将NULL设置为LoanedDate
  4. 我确定前一个日期的近似逻辑(不知道如何将它放在case语句中):

    SELECT TOP 1 Z1.Date
    FROM DBANME Z1
    LEFT JOIN DBNAME Z2
    ON Z1.Document_ID = Z2.Document_ID AND Z1.Customer_ID = Z2.Customer_ID AND CONVERT(CHAR(8), Z1.Date, 112) < CONVERT(CHAR(8), Z2.Date, 112)
    ORDER BY Date DESC
    

    我做过这样的事情:

    DECLARE @Min datetime, @Prev datetime
    SELECT @Min = MIN(Date) FROM DBNAME     
    
    UPDATE DBNAME
    SET LoanedDate CASE
    
    WHEN (LoanedMoney > 0) 
    --  SET @Prev = 
        CASE WHEN @Prev IS NULL THEN Date
        ELSE @Prev
    ELSE NULL   
    

    实施它的最佳方法是什么?

    我有什么:

    Document_ID, Customer_ID, Date, LoanedMoney, LoanedDate
    1, 1, 2012-04-30, 30, NULL 
    1, 1, 2012-04-29, 50, NULL 
    1, 1, 2012-04-28, 50, NULL 
    1, 1, 2012-04-27, 0, NULL
    1, 1, 2012-04-26, 20, NULL
    

    我的期望:

    Document_ID, Customer_ID, Date, LoanedMoney, LoanedDate
    1, 1, 2012-04-30, 30, 2012-04-28 
    1, 1, 2012-04-29, 50, 2012-04-28 
    1, 1, 2012-04-28, 50, 2012-04-28 
    1, 1, 2012-04-27, 0, NULL -- Because LoanedMoney = 0
    1, 1, 2012-04-26, 20, 2012-04-26
    

1 个答案:

答案 0 :(得分:0)

尝试这样的事情 - 只需要执行所有规则的几个相关子查询。第一个子查询是查找小于当前的所有日期,第二个查询是在最新的LoanedMoney = 0之后消除值。从中选择MIN,如果它为空,则使用COALESCE将其替换为当前日期。

UPDATE z1
SET LoanedDate = 
    CASE WHEN LoanedMoney = 0 THEN NULL
         ELSE
          COALESCE ( 
             (SELECT MIN(DATE) 
                FROM DBNAME z2 
                WHERE z1.Customer_ID = z2.Customer_ID 
                    AND z1.Document_ID = z2.Document_ID 
                    AND z2.Date < z1.Date   
                    AND z2.Date > (SELECT MAX(DATE) 
                                    FROM dbo.DBNAME z3
                                    WHERE z3.Customer_ID = z2.Customer_ID 
                                        AND z3.Document_ID = z2.Document_ID 
                                        AND z3.Date < z1.Date
                                        AND z3.LoanedMoney = 0)

        ), Date) END
FROM dbo.DBNAME z1

<强> SQLFiddle DEMO

编辑 - 选项2

这是另一种使用ROW_NUMBER()函数和recursive CTE等不同概念的方法。可能有点难以理解,但它会胜过大表上的第一个查询。

WITH CTE_Prep AS 
(
    SELECT *, ROW_NUMBER() OVER (PARTITION BY Document_ID, Customer_ID ORDER BY [DATE]) RN
    FROM DBNAME
)
,RCTE AS 
(
    SELECT *,  CASE WHEN LoanedMoney>0 THEN Date END AS LoanedDate2
    FROM CTE_Prep 
    WHERE RN = 1

    UNION ALL

    SELECT p.*,  CASE WHEN p.LoanedMoney=0 THEN NULL 
                      ELSE CASE WHEN r.LoanedDate2 IS NULL THEN p.Date 
                                ELSE r.LoanedDate2 END 
                 END AS LoanedDate2
    FROM CTE_Prep p
    INNER JOIN RCTE r 
        ON p.Customer_ID = r.Customer_ID
        AND p.Document_ID = r.Document_ID
        AND p.RN = r.RN + 1
)
UPDATE z 
    SET z.LoanedDate = r.LoanedDate2
FROM RCTE r
INNER JOIN dbo.DBNAME z ON r.Customer_ID = z.Customer_ID
    AND r.Document_ID = z.Document_ID
    AND r.Date = z.Date;

<强> SQLFiddleDEMO