简体   繁体   English

在SQL Server中避免游标

[英]Avoiding Cursor in SQL Server

I am trying to avoid cursor to get the following desired output from current format. 我试图避免光标从当前格式获得以下所需的输出。 Currently I have to use a cursor and since the dataset is very huge it takes me around 2 hours to run it. 当前,我必须使用游标,并且由于数据集非常庞大,因此我花了大约2个小时来运行它。 Is there a way to avoid cursor 有没有办法避免游标

这是当前日期格式和所需的输出 Here is my current Code 这是我当前的代码

Declare @CustomerId CHAR(8)
Declare @StartDate DateTime
Declare @PlStartDate DateTime
Declare @ProviderNo CHAR(8)
Declare @Code CHAR(3)       

Declare @PreviousCustomerId CHAR(8)
Declare @PreviousStartDate DateTime
Declare @PreviousRealStartDate DateTime
Declare @PreviousProviderNo CHAR(8)
Declare @PreviousCode CHAR(3)           
Declare @RowNumber smallint 
Declare @providers CURSOR

SET @providers = CURSOR  FAST_FORWARD  FOR
Select CustomerId, StartDate, PlStartDate, ProviderNo, Code, ProviderSSN, RowNumber  
From dbo.[provider] ORDER by CustomerId, StartDate

OPEN @providers 
FETCH NEXT From @providers  INTO @CustomerId, @StartDate, @PlStartDate,@ProviderNo, @Code, @ProviderSSN, @RowNumber 

WHILE @@FETCH_STATUS = 0
BEGIN  

    If @RowNumber <>1 AND @CustomerId = @PreviousCustomerId AND @Code = @PreviousCode AND (@ProviderNo = @PreviousProviderNo)       
    BEGIN   
        Update dbo.provider
        SET StartDate = @PreviousRealStartDate
        Where CustomerId = @CustomerId AND  StartDate = @StartDate;     
    END
    ELSE
    BEGIN
        Set @StartDate = @StartDate;

        Update dbo.provider
        Set StartDate = @StartDate
        Where CustomerId = @CustomerId AND  StartDate = @StartDate
    END

    Set @PreviousCustomerId = @CustomerId
    Set @PreviousCode = @Code       
    Set @PreviousProviderNo =  @ProviderNo 
    if @StartDate IS NOT NULL
        Set @PreviousRealStartDate = @StartDate
    Set @PreviousStartDate = @StartDate

    FETCH NEXT From @providers INTO @CustomerId, @StartDate, @PlStartDate,@ProviderNo, @Code, @RowNumber        
END 

CLOSE @providers
DEALLOCATE @providers

Current Format 当前格式

Cust ID Start Date      End Date    Code    Provider
7063903 2/11/2009   2/17/2009   DEF 485960
7063903 2/17/2009   2/24/2009   DEF 485960
7063903 2/24/2009   4/6/2009    LHF 479407
7063903 4/6/2009    9/11/2009   DEF 487398
7063903 8/31/2010   9/1/2010    DEF 487398
7063903 8/28/2011   11/25/2011  ABC 531428
7063903 3/1/2012    6/25/2012   DEF 487398
7063903 6/25/2012   3/22/2013   DEF 487398
7063903 3/22/2013   4/23/2014   DEF 487398
7063903 4/23/2014   5/1/2014    DEF 487398
7063903 5/1/2014    7/1/2015    DEF 487398
7063903 7/1/2015    8/28/2015   DEF 531428
7063903 8/28/2015   11/25/2015  ABC 531428
7063903 11/25/2015  9/21/2016   ABC 531428

Desired Output              

CustID  Start Date  End Date    Code    Provider
7063903 2/11/2009   2/24/2009   DEF 485960
7063903 2/24/2009   4/6/2009    LHF 479407
7063903 4/6/2009    9/1/2010    DEF 487398
7063903 8/28/2011   11/25/2011  ABC 531428
7063903 4/6/2009    7/1/2015    DEF 487398
7063903 7/1/2015    8/28/2015   DEF 531428
7063903 8/28/2015   9/21/2016   ABC 531428
  SELECT customer
       , code
       , provider
       , MIN (start_date) start_date
       , MAX (end_date) end_date
    FROM dbo.provider
GROUP BY customer, code, provider;

This solution fails when the provider is repeated like below. 像下面这样重复提供程序时,此解决方案将失败。 Look at the red arrows: 查看红色箭头:

updatred

Tom, 汤姆,

I must apologize, I misunderstood the original problem. 我必须道歉,我误解了原来的问题。 You want the answers grouped where customer/code/provider are the same, and start date = previous end date. 您希望将答案分组,其中客户/代码/提供者相同,并且开始日期=前一个结束日期。

The SQL below does this by setting a value of 1 whenever customer/code/provider don't match, or the start date does not equal the previous end date. 下面的SQL通过在客户/代码/提供者不匹配或开始日期不等于前一个结束日期时将值设置为1来实现此目的。 I then sum all of the 1's on the previous records to create a value I can group by. 然后,我将先前记录中的所有1求和,以创建可以分组的值。 I then perform the min/max as in my original answer, this time including the group. 然后,我按照原始答案中的最小值/最大值执行操作,这次包括组。

I get the same answer as you requested, with one exception. 除了一个例外,我得到的答案与您要求的相同。 Your first arrow should result in two rows, not a single row. 您的第一个箭头应显示两行,而不是一行。 Please give this a try. 请尝试一下。

Thanks, 谢谢,

-Brian 布赖恩

WITH
    aset
    AS
        (SELECT customer
              , code
              , provider
              , start_date
              , end_date
              , CASE
                    WHEN LAG (customer)
                             OVER (
                                 PARTITION BY customer, code, provider ORDER BY end_date
                             ) = customer
                     AND LAG (provider)
                             OVER (
                                 PARTITION BY customer, code, provider ORDER BY end_date
                             ) = provider
                     AND LAG (code)
                             OVER (
                                 PARTITION BY customer, code, provider ORDER BY end_date
                             ) = code
                     AND LAG (end_date)
                             OVER (
                                 PARTITION BY customer, code, provider ORDER BY end_date
                             ) = start_date
                    THEN
                        0
                    ELSE
                        1
                END
                    flag
           FROM deleteme_tbl),
    bset
    AS
        (SELECT customer
              , code
              , provider
              , flag
              , start_date
              , end_date
              , SUM (flag)
                    OVER (
                        ORDER BY
                            customer
                          , code
                          , provider
                          , start_date
                        RANGE UNBOUNDED PRECEDING
                    )
                    grp
           FROM aset)
  SELECT customer
       , code
       , provider
       , MIN (start_date) start_date
       , MAX (end_date) end_date
    FROM bset
GROUP BY customer
       , code
       , provider
       , grp
ORDER BY customer
       , code
       , provider
       , start_date;

Yes, use a window functions instead, consider using of LAG() 是的,请改用窗口函数,请考虑使用LAG()

Select
       CustomerId
      , StartDate
      , PlStartDate
      , ProviderNo
      , Code
      , ProviderSSN
      , RowNumber 
      , LAG(StartDate) OVER(PARTITION BY CustomerId, Code, ProviderNo 
                            ORDER by StartDate) as lag_date
From dbo.[provider] 
WHERE StartDate IS NULL
ORDER by CustomerId, StartDate

If this is correct you can work this into a common table expression, and then update using that. 如果正确,则可以将其处理为公用表表达式,然后使用该表表达式进行更新。 Test it on something small first though. 不过先在小东西上进行测试。 eg 例如

with cte as ( query shown above )
update cte
set StartDate = lag_date

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM