【问题标题】:Update with nested case for previous dates使用先前日期的嵌套大小写更新
【发布时间】:2013-11-07 08:55:31
【问题描述】:

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

Document_ID, Customer_ID, Date, LoanedMoney.

我创建了一个新的LoanedDate 列。我想通过以下方式将日期填写到LoanedDate

  1. 如果之前的Date 或之前的日期不存在(它并不意味着总是-1 天)且具有相同的Document_IDCustomer_ID 为空-> 复制LoanedMoney 值并使用该值更新价值;
  2. 如果之前的Date(并不总是-1 天)与相同的Document_IDCustomer_ID 不为空-> 复制它并使用该值更新;
  3. 否则将NULL 设置为LoanedDate

我判断前一个Date的大概逻辑(不知道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

【问题讨论】:

  • 您能否在您的问题中添加几行示例数据和预期结果?
  • 请更新您的问题。不要在cmets中写额外的数据,这样太不可读了。
  • 对不起,完成。请看上面。
  • 如何在Date = 2012-04-30 的第一行获得LoanedDate = 2012-04-28?从前面的解释中我真的无法理解。
  • 因为LoanedMoney &gt; 0 自 2012 年 4 月 28 日起。

标签: sql sql-server sql-update case


【解决方案1】:

尝试这样的事情 - 只需几个相关的子查询来执行所有规则。第一个子查询是查找所有小于当前日期的日期,第二个子查询是消除最新 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

【讨论】:

  • 似乎或多或少的路要走。谢谢你,尤其是 COALESCE!我以前没用过。
  • 很有趣,但我需要更多时间才能了解它的工作原理。感谢您的精彩想法 - 这非常有帮助!
猜你喜欢
  • 1970-01-01
  • 2017-08-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-22
  • 1970-01-01
  • 1970-01-01
  • 2015-07-18
相关资源
最近更新 更多