Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/26.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql server 如何使用嵌套while循环提高性能?_Sql Server_Tsql_Sql Server 2012 - Fatal编程技术网

Sql server 如何使用嵌套while循环提高性能?

Sql server 如何使用嵌套while循环提高性能?,sql-server,tsql,sql-server-2012,Sql Server,Tsql,Sql Server 2012,我必须按照下面的链接更新标志。我的逻辑与下面提到的链接中的逻辑完全相同,while循环中还有两个嵌套循环 然而,逻辑可以很好地工作,并且在几行中可以顺利地工作。我有大约150000行,将来会更多,这种循环永远不会结束。我检查了显示计划,它说排序成本是322112%,表扫描成本是242938%。每一行都是循环的。 我们是否可以让代码运行得更快 如评论中所述。。。在处理SQL时,考虑基于集合 declare @table table (ID int, StartDate date, EndDate

我必须按照下面的链接更新标志。我的逻辑与下面提到的链接中的逻辑完全相同,while循环中还有两个嵌套循环

然而,逻辑可以很好地工作,并且在几行中可以顺利地工作。我有大约150000行,将来会更多,这种循环永远不会结束。我检查了显示计划,它说排序成本是322112%,表扫描成本是242938%。每一行都是循环的。
我们是否可以让代码运行得更快

如评论中所述。。。在处理SQL时,考虑基于集合

declare @table table (ID int, StartDate date, EndDate date)
Insert Into @table
(
    ID
    , StartDate
    , EndDate
)
Values
(1,     '2017-01-01',    '2017-02-01'),
(1,     '2017-01-09',    '2017-01-28'),
(1,     '2017-04-01',    '2017-04-30'),
(1,     '2017-04-05',    '2017-05-20'),
(1,     '2017-04-20',    '2017-06-12'),
(2,     '2017-06-02',    '2017-06-20'),
(2,     '2017-06-14',    '2017-07-31'),
(2,     '2017-06-14',    '2017-07-31'),
(2,     '2017-06-19',    '2017-07-31'),
(2,     '2017-06-19',    '2017-07-31')


;with cte as(
select
    t1.ID
    ,t1.StartDate
    ,t1.EndDate
    ,DT = (select min(StartDate) from @table t2  where t2.StartDate > DATEADD(day,30,t1.StartDate))
from
    @table t1),

cte2 as(
select
    ID
    ,StartDate
    ,EndDate
    ,dense_rank() over (order by isnull(DT,(select max(StartDate) from cte))) as Flag
from
    cte)

select 
    ID
    ,StartDate
    ,EndDate
    ,case when Flag % 2 = 0 then 2 else Flag % 2 end as Flag
from cte2

下面是另一种使用递归和

测试设置:

create table t ( ID Int , StartDate Date , EndDate Date , Flag Int ) 
insert into t ( ID , StartDate , EndDate ) Values 
 (1,'2017-01-01','2017-02-01')
,(1,'2017-01-09','2017-01-28')
,(1,'2017-04-01','2017-04-30')
,(1,'2017-04-05','2017-05-20')
,(1,'2017-04-20','2017-06-12')
,(2,'2017-06-02','2017-06-20')
,(2,'2017-06-14','2017-07-31')
,(2,'2017-06-14','2017-07-31')
,(2,'2017-06-19','2017-07-31')
,(2,'2017-06-19','2017-07-31')
,(3,'2017-01-01','2017-02-01')
,(3,'2017-02-01','2017-02-28')
,(3,'2017-04-01','2017-04-30')
,(3,'2017-06-01','2017-05-20')
,(3,'2017-08-01','2017-06-12')
查询:

;with cte as (
  /* anchor = first start date for each id, flag = 1 */
  select t.id, t.startdate, t.enddate, flag=1
  from t
  where not exists (
    select 1
    from t i
    where i.id = t.id
      and i.startdate < t.startdate
    )  
  union all
  /* recursive, get next startdate after 30 days of previous start date
    , increment flag*/
  select s.id, s.startdate, s.enddate, s.flag
  from (
    select t.id, t.startdate, t.enddate, flag=p.flag+1
      , rn = row_number() over (partition by t.id order by t.startdate)
    from t 
      inner join cte p
        on t.id = p.id
       and t.startdate > dateadd(day,30,p.startdate)
    ) s
  where s.rn=1
)
select 
    t.id
  , t.startdate
  , t.enddate
  , x.flag
from t
  cross apply (
    /* get flag for id, startdate from cte */
    select top 1 cte.flag
    from cte
    where cte.id = t.id
      and cte.startdate <= t.startdate
    order by cte.startdate desc
    ) x

这个逻辑非常完美,但我知道这肯定可以用更好的方式来写。如果你们有任何好的替代方法,我很乐意更新我的代码:-

--drop table #t
    create table #t ( ID Int , StartDate Date , EndDate Date , LoopLogic nvarchar(100) ) 
    insert into #t ( ID , StartDate , EndDate ) Values 
     (1,'2017-01-01','2017-02-01')
    ,(1,'2017-01-09','2017-01-28')
    ,(1,'2017-04-01','2017-04-30')
    ,(1,'2017-04-05','2017-05-20')
    ,(1,'2017-04-20','2017-06-12')
    ,(2,'2017-06-02','2017-06-20')
    ,(2,'2017-06-14','2017-07-31')
    ,(2,'2017-06-14','2017-07-31')
    ,(2,'2017-06-19','2017-07-31')
    ,(2,'2017-06-19','2017-07-31')
    ,(3,'2017-01-01','2017-02-01')
    ,(3,'2017-02-01','2017-02-28')
    ,(3,'2017-04-01','2017-04-30')
    ,(3,'2017-06-01','2017-05-20')
    ,(3,'2017-08-01','2017-06-12')
    --drop table #auth
    select *
    into #auth 
    from #t
    order by ID,StartDate

    Declare
    @LoopLogic As nvarchar(100) = '0'
    ,@Flag As int=0
    ,@StartDate As Date
    ,@LookupDate1 As Date
    ,@LookupDate As Date
    ,@ID As nvarchar(100)
    ,@PrevID As nvarchar(100) = '0'
    ,@DaysDiff As int
    ,@ReauthFlag As int=0;

    --drop table #Temp_SO_Check
    Select
        *
    Into #Temp_SO_Check
    From #auth;

    CREATE  INDEX IDX_C_Users_UserID ON #Temp_SO_Check(ID,StartDate,LoopLogic);


    --Select * From #Temp_SO_Check As tsc  order by auth#,servicedate

    While Exists
    (
        Select Top 1
                   ID
            From    #Temp_SO_Check
            Where   LoopLogic Is Null
    )
    Begin
        Select Top 1
                    @ID = ID
                    , @StartDate = StartDate
            From    #Temp_SO_Check
            Where   LoopLogic Is Null
            Order By
                    ID
                    , StartDate;

    If @PrevID <> @ID
    Begin
            set @Flag=1;
            Set @LoopLogic ='Initial_'+Cast(@Flag as nvarchar(50)) ;
            Set @PrevID = @ID;
            set @ReauthFlag=0;
    end 
          else 
    Begin
    if((@LookupDate is not null) and (datediff(day,@LookupDate,@StartDate))<14)-- if the startdate is less than 14 days from lookdate then it is reauth
    Begin       
             Set @LoopLogic ='Re-Auth_'+Cast(@Flag as nvarchar(50)) ;
    End
    Else if ((@LookupDate is not null) and (datediff(day,@LookupDate,@StartDate))>14) -- if the startdate is greater than 14 days from lookdate then it is Initial
    Begin
             set @Flag=@Flag+1;
             set @ReauthFlag=0;
             Set @LoopLogic ='Initial_'+Cast(@Flag as nvarchar(50));
    End
    End
             Set @LookupDate = DateAdd(Day, 30, @StartDate);
    if( (@LoopLogic like '%Re-Auth_%') and (@LookupDate<>@LookupDate1))
    Begin
             Set @ReauthFlag=@ReauthFlag+1;
             Set @LoopLogic ='Re-Auth_'+Cast(@Flag as nvarchar(50))+'_'+Cast(@ReauthFlag as nvarchar(50)) ;

    End
             set @LookupDate1=@LookupDate;

    Update
            #Temp_SO_Check
            Set
            LoopLogic = @LoopLogic
            Where
            ID = @ID
            And StartDate Between @StartDate
                        And     @LookupDate;

    End;
    select * from #Temp_SO_Check

在嵌套循环中提高性能的方法是先取消嵌套循环,然后在不使用循环的情况下重写查询。我认为上一个问题有错误,标志应该每次递增,而不是从1到2,然后on@Lamak我也这么认为,这样会更容易,但这并不是预期的结果。我不认为这会让事情变得更容易,因为我还认为国旗需要继续升到4、5、6……等等。谢谢你的帮助。。现在,我只是在OrderBy列上添加了索引,代码在6分钟内运行@西蒙。我肯定会尝试你的基本代码,而不是while循环。你的标志中有3、4、5,这不是预期的结果。。。为什么会这样?@scsimon he以我自己的身份假设旗帜需要继续计数,而相关问题实际上表明了这一点,但结果表明并非如此。不管怎样,这是一个很棒的机会answer@scsimon据我所知,这取决于你如何解释链接问题中的这一行:这一行在同一个ID上继续,直到它达到同一ID的最后一个记录。我认为这意味着30天窗口的过程,将flag增加1,直到每个Id的行结束。我假设您将其理解为:继续对其余行使用flag_2。我没有说这不可怕,我只做了UV-Zim的回答。今天这个标签上出现了有趣的敌意。@scsimon我确实同意,链接的问题肯定可以使用一些更好的样本数据和结果。
--drop table #t
    create table #t ( ID Int , StartDate Date , EndDate Date , LoopLogic nvarchar(100) ) 
    insert into #t ( ID , StartDate , EndDate ) Values 
     (1,'2017-01-01','2017-02-01')
    ,(1,'2017-01-09','2017-01-28')
    ,(1,'2017-04-01','2017-04-30')
    ,(1,'2017-04-05','2017-05-20')
    ,(1,'2017-04-20','2017-06-12')
    ,(2,'2017-06-02','2017-06-20')
    ,(2,'2017-06-14','2017-07-31')
    ,(2,'2017-06-14','2017-07-31')
    ,(2,'2017-06-19','2017-07-31')
    ,(2,'2017-06-19','2017-07-31')
    ,(3,'2017-01-01','2017-02-01')
    ,(3,'2017-02-01','2017-02-28')
    ,(3,'2017-04-01','2017-04-30')
    ,(3,'2017-06-01','2017-05-20')
    ,(3,'2017-08-01','2017-06-12')
    --drop table #auth
    select *
    into #auth 
    from #t
    order by ID,StartDate

    Declare
    @LoopLogic As nvarchar(100) = '0'
    ,@Flag As int=0
    ,@StartDate As Date
    ,@LookupDate1 As Date
    ,@LookupDate As Date
    ,@ID As nvarchar(100)
    ,@PrevID As nvarchar(100) = '0'
    ,@DaysDiff As int
    ,@ReauthFlag As int=0;

    --drop table #Temp_SO_Check
    Select
        *
    Into #Temp_SO_Check
    From #auth;

    CREATE  INDEX IDX_C_Users_UserID ON #Temp_SO_Check(ID,StartDate,LoopLogic);


    --Select * From #Temp_SO_Check As tsc  order by auth#,servicedate

    While Exists
    (
        Select Top 1
                   ID
            From    #Temp_SO_Check
            Where   LoopLogic Is Null
    )
    Begin
        Select Top 1
                    @ID = ID
                    , @StartDate = StartDate
            From    #Temp_SO_Check
            Where   LoopLogic Is Null
            Order By
                    ID
                    , StartDate;

    If @PrevID <> @ID
    Begin
            set @Flag=1;
            Set @LoopLogic ='Initial_'+Cast(@Flag as nvarchar(50)) ;
            Set @PrevID = @ID;
            set @ReauthFlag=0;
    end 
          else 
    Begin
    if((@LookupDate is not null) and (datediff(day,@LookupDate,@StartDate))<14)-- if the startdate is less than 14 days from lookdate then it is reauth
    Begin       
             Set @LoopLogic ='Re-Auth_'+Cast(@Flag as nvarchar(50)) ;
    End
    Else if ((@LookupDate is not null) and (datediff(day,@LookupDate,@StartDate))>14) -- if the startdate is greater than 14 days from lookdate then it is Initial
    Begin
             set @Flag=@Flag+1;
             set @ReauthFlag=0;
             Set @LoopLogic ='Initial_'+Cast(@Flag as nvarchar(50));
    End
    End
             Set @LookupDate = DateAdd(Day, 30, @StartDate);
    if( (@LoopLogic like '%Re-Auth_%') and (@LookupDate<>@LookupDate1))
    Begin
             Set @ReauthFlag=@ReauthFlag+1;
             Set @LoopLogic ='Re-Auth_'+Cast(@Flag as nvarchar(50))+'_'+Cast(@ReauthFlag as nvarchar(50)) ;

    End
             set @LookupDate1=@LookupDate;

    Update
            #Temp_SO_Check
            Set
            LoopLogic = @LoopLogic
            Where
            ID = @ID
            And StartDate Between @StartDate
                        And     @LookupDate;

    End;
    select * from #Temp_SO_Check