SQL Server中嵌套循环的更好方法

时间:2018-10-17 17:05:10

标签: sql sql-server

我有一个嵌套循环,用于检查表descripttmp13列的所有名称和日期,并将它们作为单独的行存储在其他表(tmp14)中。问题是while循环执行了很长时间。我不知道如何使其运行更快。我已经尝试了上一篇文章中的一些建议,但是我并不是很成功。任何人都可以提出一些建议来解决这个可怕的问题。

这是我的代码,检查descript列中的名称和日期。 Descript是一个文本列,可以有多个名称和日期。我想将这些名称和日期存储在单独的行中。

DECLARE @Id INT
DECLARE @count INT
DECLARE @product_num INT
DECLARE @REQUESTED VARCHAR(50)
DECLARE @FirstDate VARCHAR(255)
DECLARE @RequestedBy VARCHAR(255)

DECLARE @name NVARCHAR(256)
DECLARE @date NVARCHAR(256)
DECLARE @desc NVARCHAR(256)

DECLARE @dateposition INT
DECLARE @nameposition INT
DECLARE @nameend INT

SELECT @count = MAX(id) 
FROM #TMP13

SET @id = 1;

WHILE (@id <= @count)
BEGIN
    SELECT @desc = descript FROM #TMP13 WHERE Id = @Id
    SELECT @product_num = p_Num FROM #TMP13 WHERE Id = @Id
    SELECT @REQUESTED = REQUESTED FROM #TMP13 WHERE Id = @Id
    SELECT @FirstDate = DATE1 FROM #TMP13 WHERE Id = @Id
    SELECT @RequestedBy = BY1 FROM #TMP13 WHERE Id = @Id


while (patindex('%[0-9][0-9]/[0-9][0-9]/[0-9][0-9][0-9][0-9]%',@desc) > 0)
begin
    set @dateposition = patindex('%[0-9][0-9]/[0-9][0-9]/[0-9][0-9][0-9]%',@desc)
    set @date = SUBSTRING(@desc,@dateposition,10)

    set @nameposition = CHARINDEX('-', @desc)+2
    set @nameend = CHARINDEX(' ', @desc, @nameposition)+1
    set @name = SUBSTRING(@desc,@nameposition,@nameend-@nameposition)

    insert into #TMP14 
    values (@Id,@product_num,@REQUESTED, @FirstDate ,@RequestedBY, @date, @name)

    set @desc = SUBSTRING(@desc,@nameend,1024)  
end

set @id = @id + 1;

end

select * from #tmp14;

---样本表

CREATE TABLE #Tmp13(
p_Num             INTEGER  NOT NULL PRIMARY KEY 
REQUESTED          varchar(50),
DATE1            VARCHAR(50),   
BY1              VARCHAR(50),
DESCRIPT         TEXT

);

INSERT INTO #tmp13 (p_Num, REQUESTED, DATE1, BY1, DESCRIPT) 
VALUES (100, 'John', '5/30/2017', 'James', '05/30/2017 12:25am Eastern Standard Time - Mjames reported changes in the pages 05/30/2017 10:35AM JRachael agreed to work on the report and report to James 05/30/2017 10:00 AM James reports errors in page.',NULL);

INSERT INTO #tmp13(WO_NUM, Opendate, ClosedDate, Note) 
VALUES (200, 'John', '6/1/2017', 'Rachael', '06/1/2017 3:20PM Eastern Standard Time - Rsubramaniam reported phone is not functional 06/1/2017 4:00PM Service took the phone and replaced it with new one');

输出:

 Id  product_num REQUESTED FirstDate  RequestedBY date name date Name                                                               

 1   100          John      5/30/2017  james      5/30/2017 mjames  5/30/2017 jRachael

1 个答案:

答案 0 :(得分:0)

这是一个选项。如果它遵循一致的格式,则仅查看解析列中的多个日期和名称。您需要进行调整以适合您的解决方案。还有...

  

这仅适用于以下情况:

     
      
  • 日期和名称以一致且可重复的格式存储在DESCRIPT字段中,例如:“ dd / mm / yyyy时区-名称文本dd / mm / yyyy   时区-名称文字dd / mm / yyyy时区-名称文字“
  •   

我不确定这会给您带来什么样的效果,并且如果该字段中日期和名称的存储格式发生变化,它将无法正常工作。这就是了解格式是否一致和可重复的重要性。

在示例中,我们基本上是将阶段分为单个单词,然后进行过滤以获取所需内容。根据SQL Server的版本,我提供了两个不同的选项来实现拆分。

  • SQL Server 2016+版本,因为它将使用SPLIT_STRING
  • 另一个应该可以追溯到2012年的版本。它使用XML

没有太多示例数据,但是根据您的评论和答复,我正在做一些假设,您可能需要根据您的特定需求进行调整。

  1. 我们一直在该字段中出现日期
  2. 以及与日期关联的名称,该名称紧接在该日期之后的“-”之后。

这是一个例子:

DECLARE @tmp13 TABLE
    (
        [p_Num] INTEGER NOT NULL
      , [DESCRIPT] NVARCHAR(MAX)
       PRIMARY KEY([p_Num])
    );

DECLARE @tmp13Parse TABLE
    (
        [Id] INT
      , [Position] BIGINT
      , [Value] NVARCHAR(500)
      unique clustered ([Id], [Position])
    );


--insert test data
INSERT INTO @tmp13 (
                       [p_Num]
                     , [DESCRIPT]
                   )
VALUES ( 100
       , '05/30/2017 12:25am Eastern Standard Time - Mjames reported changes in the pages 05/30/2017 10:35AM Eastern Standard Time - JRachael agreed to work on the report and report to James 05/30/2017 10:00AM Eastern Standard Time - James reports errors in page.' )
     , ( 200
       , '05/29/2017 12:25am Central Stanard Time - TSmith reported changes in the pages 05/29/2017 10:35AM Central Stanard Time - JRachael agreed to work on the report and report to James 05/29/2017 10:00AM Central Stanard Time - GregNoName reports errors in page.' )
     , ( 300
       , '05/28/2017 12:25am Eastern Standard Time - Mjames reported changes in the pages 05/28/2017 10:35AM Eastern Standard Time - JName agreed to work on the report and report to James 05/28/2017 10:00AM Eastern Standard Time - James reports errors in page.' )
     , ( 400
       , '05/27/2017 12:25am Central Stanard Time - Mjames reported changes in the pages 05/27/2017 10:35AM Central Stanard Time - JRachael agreed to work on the report and report to James 05/27/2017 10:00AM Eastern Standard Time - AnotherName reports errors in page.' )
     , ( 500
       , '05/26/2017 12:25am Eastern Standard Time - MJohnson reported changes in the pages 05/26/2017 10:35AM Eastern Standard Time - FTestname agreed to work on the report and report to James 05/26/2017 10:00AM Eastern Standard Time - James reports errors in page.' )
     , ( 600
       , '05/25/2017 12:25am Eastern Standard Time - Mjames reported changes in the pages 05/25/2017 10:35AM Eastern Standard Time - JRachael agreed to work on the report and report to James 05/25/2017 10:00AM Eastern Standard Time - James reports errors in page.' )
     , ( 700
       , '05/24/2017 12:25am Eastern Standard Time - TTaylor reported changes in the pages 05/24/2017 10:35AM Eastern Standard Time - JRachael agreed to work on the report and report to James 05/24/2017 10:00AM Eastern Standard Time - TMoreTestNames reports errors in page.' );


--Basically what we are doing is loading a table with each individual word making sure we keep which Id it was associated with. 
--along with the Position of where it was in the phrase.
--Two options below depending on SQL Version.

--SQL Version 2016+, we'll use SPLIT_STRING, code is a little more easier
INSERT INTO @tmp13Parse (
                            [Id]
                          , [Position]
                          , [Value]
                        )
            SELECT      [a].[p_Num]
                      , [b].[Position]
                      , [b].[Value]
            FROM        @tmp13 [a]
            CROSS APPLY (
                            SELECT [Value]
                                 , ROW_NUMBER() OVER ( ORDER BY (
                                                                    SELECT 1
                                                                )
                                                     ) AS [Position]
                            FROM   STRING_SPLIT([a].[DESCRIPT], ' ') --this will handle returning a table based on how you split it, in this case a space.
                        ) AS [b];

--Prior to SQL Version 2016 back to 2012, use this option which is using a XML to split the data.
INSERT INTO @tmp13Parse (
                            [Id]
                          , [Position]
                          , [Value]
                        )
            SELECT      [a].[p_Num]
                      , [ss].[Position]
                      , [ss].[Value]
            FROM        @tmp13 [a]
            CROSS APPLY (
                            SELECT      ROW_NUMBER() OVER ( ORDER BY (SELECT 1)) AS [Position]
                                      , [y].[i].[value]('(./text())[1]', 'nvarchar(max)') AS [Value]
                            FROM        (
                                            SELECT [x] = CONVERT(XML, '<i>'+ REPLACE([a].[DESCRIPT], ' ', '</i><i>')+ '</i>').[query]('.')
                                        ) AS [a]
                            CROSS APPLY [x].[nodes]('i') AS [y]([i])
                        ) AS [ss];


--After we have split the data we'll now go after the specific values
SELECT      [a].[Id]
          , [a].[Value] AS [Date]
          , [ccc].[Value] AS [Name]
FROM        @tmp13Parse [a]
--First cross apply - what is the position of '-' after my date field. add 1 since the next value should be the name I'm after.
CROSS APPLY (
                SELECT MIN([aa].[Position]) + 1 AS [nameAnchorPosition]
                FROM   @tmp13Parse [aa]
                WHERE  [aa].[Id] = [a].[Id]
                       AND [aa].[Value] = '-'
                       AND [aa].[Position] > [a].[Position]
            ) AS [bb]
--Second cross apply - Now, based on where I identified '-' to be, plus 1, give me that value.
CROSS APPLY (
                SELECT [cc].[Value]
                FROM   @tmp13Parse [cc]
                WHERE  [cc].[Id] = [a].[Id]
                       AND [cc].[Position] = [bb].[nameAnchorPosition]
            ) AS [ccc]
WHERE       TRY_CONVERT(DATE, [a].[Value]) > '1900-01-01';  --will return all those values that are a date as starting point, long with their position.

我使用记录和解析的两个选项对记录为54000的一台服务器进行了快速测试,并且都在4-10秒内给出了结果。您的里程可能会有所不同。