【问题标题】:If Exists in the same SQL table Cursor Update如果在同一个 SQL 表中存在游标更新
【发布时间】:2019-03-24 15:23:40
【问题描述】:

我需要一些关于 SQL 逻辑/语言的建议。我有一个游标,可以在表格中循环查看数千个 year + customer_id + order_no 组合。

数据样本

year customer_id  order_no  markerA   markerB   markerC  MarkerD
2018 32329        523142
2018 32329        523243
2018 39566        523508
2018 42352        523214
2017 17675        470537
2017 21486        479414
2017 39566        479038
2017 42352        479220

等等

我需要我的逻辑做的是说将组合的值拉到 customer_id + year + order_no 之上

如果没有 customer_no 再次出现(在初始拉取之后)然后MarkerA - 'Y'.

如果 customer_no 再次出现 -- 如果那一年是相同的,则在初始拉取中,则 MarkerB -'Y'.

如果 customer_no 再次出现 -- 但年份不同,如果年份是 - 1 则进一步查看 MarkerC -'Y'.

但是如果 customer_no 再次出现 -- 但年份不同,如果年份不是 year - 1 则进一步审查,但在其他地方确实存在第 2 年或更大的线,则 MarkerD -'Y'.

declare @order_year int
declare @customer_id int
declare @order_dt datetime
declare @order_no int

BEGIN   

    DECLARE db_cursor CURSOR FOR 
    Select distinct year, customer_id, order_dt, order_no From #Compare_Data 

    OPEN db_cursor  
    FETCH NEXT FROM db_cursor INTO @order_year, @customer_no, @order_dt, @order_no

        WHILE @@FETCH_STATUS = 0  
        BEGIN 

...我知道我需要一系列 IF 语句,但我不确定如果存在比较什么/如何比较。不可能说是否存在值,因为当然存在值,您只是从表中提取了 customer_no/season 等。我怎么说除了我正在查看的值之外是否存在值。

        FETCH NEXT FROM db_cursor INTO @customer_no, @order_dt, @order_no
        END 

    CLOSE db_cursor  
    DEALLOCATE db_cursor

END

【问题讨论】:

  • 不清楚,对我来说似乎是相互矛盾的要求。您的解决方案是不确定的,因为表没有内在顺序。
  • @paparazzo 我怎样才能清除这个 - 我需要评估每个行组合并查看数据是否出现在表格的其他地方。在第一次评估中,我必须比较其余数据中的 customer_id,而不考虑其他任何内容等等。

标签: sql-server tsql if-statement cursor exists


【解决方案1】:

如果我正确理解您的问题,您是按降序检查客户订单,以确定订单状态,以便与客户的最新订单进行比较。

老实说,我有点不确定我是否了解您的要求,所以我的解决方案应该会给您提供您需要做什么的要点。

首先不要使用光标。很少有使用它们的正当理由,它们既慢又昂贵。这个问题应该用窗口函数来解决。

窗口函数可让您通过对分区执行聚合函数从某一行查看结果集的窗口。因此,例如,如果您想要所有具有相同客户 ID 的行的最短年份,您可以写 MIN([Year]) OVER (PARTITION BY CustomerId)

下面是尝试解决您的问题。我可以想象您将不得不调整 CASE 表达式以适应您的确切标准。

-- Setup test data
DECLARE @CompareData TABLE ( [Year] INT, CustomerId INT, OrderNo INT );

INSERT INTO @CompareData 
VALUES 
    (2018, 32329, 523142),
    (2018, 32329, 523243),
    (2018, 39566, 523508),
    (2018, 42352, 523214),
    (2017, 17675, 470537),
    (2017, 21486, 479414),
    (2017, 39566, 479038),
    (2017, 42352, 479220),
    (2016, 42352, 479220);

-- solution
WITH src AS (
    SELECT *
        --, ROW_NUMBER() OVER 
        --     (PARTITION BY CustomerId ORDER BY Year DESC, OrderNo DESC) DescOrderIdx
        , COUNT(CustomerId) OVER (PARTITION BY CustomerId) CustCount
        , MIN([Year]) OVER (PARTITION BY CustomerId) MinYear
        , MAX([Year]) OVER (PARTITION BY CUstomerId) MaxYear
    FROM @CompareData
)
SELECT [Year], CustomerId, OrderNo
    , CASE WHEN CustCount = 1 THEN 'Y' ELSE '' END [MarkerA]
    , CASE WHEN CustCount > 1 AND [Year] = MaxYear THEN 'Y' ELSE '' END [MarkerB]
    , CASE WHEN CustCount > 1 AND [Year] = MaxYear - 1 THEN 'Y' ELSE '' END [MarkerC]
    , CASE WHEN CustCount > 1 AND [Year] < MaxYear - 1 THEN 'Y' ELSE '' END [MarkerD]
FROM src

案例陈述的工作方式如下:

  1. 仅当仅存在一条客户记录时才为真
  2. 如果存在多个客户记录,则为 true,但年份等于最大年份
  3. 如果存在多个客户记录,则为真,但年份等于最大年份 - 1
  4. 如果存在多个客户记录,则为 true,但年份小于最大年份 - 1。

我注释掉了派生列 DescOrderIdx,因为虽然我的解决方案不需要它,但可能需要它来满足您的确切要求。如果不标记第一个订单,则应使用检查该值是否不等于 1(最近的订单索引)作为附加条件。

【讨论】:

  • 所以对于场景#3(MarkerC)我需要做的是遍历每一行并说它不应该是我最大的年份 - 1 它应该是当年减去去年。换句话说,如果您今年有订单,去年有订单,那么您就有资格。
  • 然后添加:AND MaxYear = YEAR(GETDATE())。我确定它们是您需要解决的其他特殊性。这种方法(使用窗口函数)要么适用于您,要么不会。您可以从这里了解详细信息。
  • 我如何确定给定客户是否存在差距。例如,我有一位客户有四行。第 1 行 - 2018 年,第 2 行 - 2018 年,第 3 行 - 2016 年和第 4 行 2015 年。我必须从最后开始。 (找到最小值)。标记一下。这很容易。我正在使用您创建的 minYear。 (我发现最小年份是 2015 年)。但我需要确保每年与前一年之间的差异不超过 1。所以在 2015 年和 2016 年之间它是好的,但是从 2016 年到 2018 年它会跳跃。那很糟糕。我如何确定。谢谢。
  • 听起来对你有用的WINDOW函数是LAG:[Year] - LAG([Year]) OVER (PARTITION BY CustomerId ORDER BY OrderNo DESC, [Year] DESC)。因此,如果之前的订单提前两年,您将得到 -2。哎呀,这在我的回答中是最有用的,因为它最能反映使用光标并存储以前的值。
  • LAG/LEAD 功能非常棒。这解决了问题。感谢您的帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-05-31
  • 1970-01-01
  • 2011-07-17
  • 1970-01-01
  • 1970-01-01
  • 2016-01-19
  • 1970-01-01
相关资源
最近更新 更多