【问题标题】:Optimizing T-SQL Insert -- temptable, CTEs, WHILE loops优化 T-SQL 插入 -- temptable、CTE、WHILE 循环
【发布时间】:2010-11-22 02:53:32
【问题描述】:

我有一种情况,我需要处理并最终将 1000 条记录从临时表插入到数据库表中。在每次插入之前,我需要确保满足条件,并且在每次插入之后,我需要更新数据库中的第二个表。我的问题是,目前,运行查询大约需要 25 分钟,我想大幅缩短该时间,以便我的应用程序响应更快。请问我该怎么做?

DECLARE @rowcounter as INTEGER 


                    CREATE TABLE #temporary_phonetable 
                    (
                          rownumber int  not null  identity(1,1), 
                          record_no BIGINT,
                          phone_name BIGINT, 
                          phone_number Varchar(25)  not null  ,
                           responsemessage Varchar(200)  not null  ,
                          messagepriority Varchar(14)  not null  , 
                          phone_id BIGINT, 
                          AD_show  BIGINT, 
                          power_show Varchar(400), 
                          service_provider VARCHAR(30),
                          Phone_flag  VARCHAR(30),
                          questionMessage BIGINT,
                          PRIMARY KEY (phone_id, phone_number, rownumber)
                        ,UNIQUE (questionMessage, record_no, rownumber) 
                    )
                    --GET PHONE DATA 
                                --if phone numbers are sent in from the client, then we want to process those instead 
            IF ( ( ( @listofphones IS NULL OR LEN(@listofphones) <1) AND LEN(@peoplegroups) >0)  )
             BEGIN 
                   --NO PHONENUMBER BUT THERE ARE GROUPS AVAILABLE                        
                              INSERT INTO #temporary_phonetable(phone_name, phone_number, messagepriority, phone_id, AD_show, power_show, responsemessage) 
                              SELECT n.phone_name, n.phone_number,u.messagepriority, n.phone_id , u.AD_show, u.power_show , CASE  @includegreetings   WHEN 1 THEN LTRIM(RTRIM(phone_name)) + @responsemessages 
                                      ELSE @responsemessages END as text_message
                              FROM user u WITH(NOLOCK) 
                              INNER JOIN Phonenumbers n  WITH(NOLOCK) ON n.user_no = u.user_no
                                INNER JOIN PeopleGroupRelations g ON  g.phone_id=n.phone_id  
                                INNER JOIN ( Select items FROM Split(@peoplegroups, @listofphonesdelimiter)) gg ON g.group_no = gg.items 
                              WHERE n.user_no=@userid 
                              AND n.status=''active''
                              SET @rowcounter = @@ROWCOUNT
             END 
             ELSE  IF ( LEN(@listofphones) >1  AND LEN(@peoplegroups) >0)
              BEGIN  
                      --PHONENUMBER AND GROUPS 
                              INSERT INTO #temporary_phonetable(phone_name, phone_number, messagepriority, phone_id, AD_show, power_show, responsemessage) 
                              SELECT n.phone_name, n.phone_number,u.messagepriority, n.phone_id , u.AD_show, u.power_show , CASE  @includegreetings   WHEN 1 THEN LTRIM(RTRIM(phone_name)) + @responsemessages 
                                      ELSE @responsemessages END as text_message
                              FROM  Split(@listofphones, ''|'')  s
                              INNER JOIN PhoneNumbers n  WITH(NOLOCK) ON n.phone_number = s.items
                              INNER JOIN User u WITH(NOLOCK)  ON n.user_no =u.user_no
                              INNER JOIN PeoplegroupRelations g ON  g.phone_id=n.phone_id  
                              INNER JOIN ( Select items FROM Split(@peoplegroups, @listofphonesdelimiter)) gg ON g.group_no = gg.items 
                              WHERE n.user_no=@userid 
                              AND n.status=''active''
                              SET @rowcounter = @@ROWCOUNT
             END 
              ELSE  IF ( LEN(@listofphones) >1  AND LEN(@peoplegroups) >0)
              BEGIN  
                      --PHONENUMBER AND NO GROUPS 
                              INSERT INTO #temporary_phonetable(phone_name, phone_number, messagepriority, phone_id, AD_show, power_show, responsemessage) 
                              SELECT n.phone_name, n.phone_number,u.messagepriority, n.phone_id , u.AD_show, u.power_show , CASE  @includegreetings   WHEN 1 THEN LTRIM(RTRIM(phone_name)) + @responsemessages 
                                      ELSE @responsemessages END as text_message
                              FROM  Split(@listofphones, ''|'')  s
                              INNER JOIN PhoneNumbers n  WITH(NOLOCK) ON n.phone_number = s.items
                              INNER JOIN User u WITH(NOLOCK)  ON n.user_no =u.user_no
                              INNER JOIN PeopleGroupRelations g ON  g.phone_id=n.phone_id  
                              INNER JOIN ( Select items FROM Split(@peoplegroups, @listofphonesdelimiter)) gg ON g.group_no = gg.items 
                              WHERE n.user_no=@userid 
                              AND n.status=''active''
                              SET @rowcounter = @@ROWCOUNT
             END  
             ELSE 
                    BEGIN 
                          -- NO PHONENUMBER NO GROUP --- IE. SEND TO ALL PHONE NUMBERS
                                 INSERT INTO #temporary_phonetable(phone_name, phone_number, messagepriority, phone_id, AD_show, power_show,responsemessage) 
                                SELECT   n.phone_name, n.phone_number,u.messagepriority, n.phone_id , u.AD_show, u.power_show , CASE  @includegreetings   WHEN 1 THEN LTRIM(RTRIM(phone_name)) + @responsemessages 
                                      ELSE @responsemessages END as text_message
                                FROM User u  
                                          INNER JOIN PhoneNumbers n ON n.user_no = u.user_no
                                WHERE
                                        n.status=''active''
                                        AND  n.user_no=@userid 
                                    SET @rowcounter = @@ROWCOUNT 
                    END



                  IF( @rowcounter>0)
                  BEGIN 
                                    DECLARE @service_provider as Varchar(30)
                                    DECLARE @PhoneType as Varchar(30)

                                    IF (LOWER(RTRIM(LTRIM(@sendresponseswhen))) ='now')
                                    BEGIN 
                                                SET @dateresponsessent = GETDATE()
                                     END

                                                      DECLARE @rownumber int
                                                      DECLARE @power_show BIT
                                                      DECLARE  @AD_show BIT 
                                                      set @rownumber = 0 
                                                      WHILE @rownumber < @rowcounter
                                                      BEGIN
                                                                    set @rownumber = @rownumber + 1
                                                                    -- THE VARIABLES 
                                                                                DECLARE @record_no as BIGINT
                                                                                DECLARE @phone_name VARCHAR(30)
                                                                                DECLARE @messagepriority as INTEGER
                                                                                DECLARE @phone_number VARCHAR(30)
                                                                                DECLARE @phone_id BIGINT
                                                                                DECLARE @questionMessage BIGINT

                                                                     SELECT 
                                                                              @phone_name = n.phone_name, @phone_number =n.phone_number, @messagepriority =n.messagepriority, @phone_id=n.phone_id , 
                                                                              @AD_show=n.AD_show, @power_show=n.power_show
                                                                        FROM 
                                                                              #temporary_phonetable n WITH(NOLOCK) 
                                                                        WHERE n.rownumber = @rownumber

                                                                        SET @record_no = AddMessageToQueue(@phone_number, @responsemessages, @dateresponsessent, @savednames, @userid, un.messagepriority, @responsetype, 
                                                                        un.AD_show, un.power_show, @service_provider, @PhoneType)  


                                                                        If(@questionid > 0)
                                                                        BEGIN 
                                                                                SET @questionMessage = AddQuestionMessage(@questionid,@phone_id,  @record_no, DATEADD(d, 30, GETDATE()) )
                                                                        END 


                                                UPDATE #temporary_phonetable SET record_no = @record_no, questionMessage=@questionMessage WHERE phone_number = @phone_number  AND rownumber = @rownumber
                                    END 
                                    IF( @power_show >0)
                                    BEGIN 
                                          SET @responsemessages = @responsemessages + dbo.returnPoweredBy()
                                    END
                                    IF( @AD_show > 0)
                                    BEGIN 
                                          SELECT @responsemessages = @responsemessages + CASE 
                                                                                                            WHEN (LEN(@responsemessages)  + 14)<  160 THEN    dbo.returnAD(@responsemessages) 
                                                                                                            ELSE '''' END 
                                    END


                                    RETURN @rowcounter
                        END

我相信这是大部分问题所在。

WHILE @rownumber

                                                                 SELECT 
                                                                          @phone_name = n.phone_name, @phone_number =n.phone_number, @messagepriority =n.messagepriority, @phone_id=n.phone_id , 
                                                                          @AD_show=n.AD_show, @power_show=n.power_show
                                                                    FROM 
                                                                          #temporary_phonetable n WITH(NOLOCK) 
                                                                    WHERE n.rownumber = @rownumber

                                                                    SET @record_no = AddMessageToQueue(@phone_number, @responsemessages, @dateresponsessent, @savednames, @userid, un.messagepriority, @responsetype, 
                                                                    un.AD_show, un.power_show, @service_provider, @PhoneType)  


                                                                    If(@questionid > 0)
                                                                    BEGIN 
                                                                            SET @questionMessage = AddQuestionMessage(@questionid,@phone_id,  @record_no, DATEADD(d, 30, GETDATE()) )
                                                                    END 


                                            UPDATE #temporary_phonetable SET record_no = @record_no, questionMessage=@questionMessage WHERE phone_number = @phone_number  AND rownumber = @rownumber
                                END 

【问题讨论】:

  • 找出您(冗长)发布的代码中导致速度变慢的原因,我们或许可以提供帮助。
  • 减速从 WHILE LOOP 开始。在此之前,代码运行大约 0.16 秒。一旦它到达 While 循环,处理时间就会增加 25 分钟

标签: sql sql-server tsql ado.net


【解决方案1】:

在您的临时表中为rownumber 添加一个唯一约束。将WHILE 重写为CTE。使用APPLY调用函数。

【讨论】:

  • 这不算吗?唯一(questionMessage,record_no,rownumber)。或者这不是你的意思?
  • 并非如此。你想让它变得非常快:WHERE n.rownumber = @rownumber。此字段上的唯一约束或索引可以创造奇迹。它需要真正的性能继续摆脱循环。
  • 还想补充一点,AddMessageToQueue 和 AddQuestionQueue 是 STOREDPROCS 而不是函数。
  • 循环嵌套存储过程。哎呀。首先将嵌套的过程重写为内联函数。
  • 代码中有很多处理(大约 50 行)。这就是为什么我将该部分删除到它自己的单独存储过程中的原因。你是说我应该将所有代码复制回这个主进程?
【解决方案2】:

您可能还想考虑使用表变量而不是临时表。您不会写入 tempdb,并且由于表变量是在内存中创建的,因此它们会更快。

This article 有一个很好的比较。

【讨论】:

  • 我试过了,但差别不大。请问还有什么建议吗?
  • 您可以 INSERT..SELECT 满足条件的数据库,而不是使用 while 循环逐行检查表变量。如果您还需要知道哪些行不符合条件,您可以反转条件来找出答案。
  • 如果您查看查询循环,我必须为那里的每一行实际执行一个存储过程,这就是我有循环的原因。我知道我无法使用 INSERT 对每一行执行该操作。 . . .SELECT,或者我可以吗?
  • 我的意思是尝试将该逻辑从过程中取出,而不是逐行应用,而是一次将其应用到整个表。
猜你喜欢
  • 2019-11-30
  • 2011-05-14
  • 2021-04-19
  • 1970-01-01
  • 1970-01-01
  • 2010-10-01
  • 1970-01-01
  • 2016-03-18
  • 2023-03-18
相关资源
最近更新 更多