0

I have a nested loop that checks all the names and dates from descript column from table tmp13 and stores them as individual rows in other table (tmp14). The problem is that the while loop is executing for a long time. I don't know how to make it run faster. I have tried some suggests already from previous post, but I haven't been very successful. Can anyone give some suggestion to approach this horrible issue.

Here is my code checking the descript columns for names and date. Descript is a text column and can have multiple names and dates. I want to store those names and dates in separate rows.

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;

---sample table

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');

OUTPUT:

 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
M.Doe
  • 99
  • 1
  • 10
  • 1
    You asked the exact same thing yesterday: https://stackoverflow.com/questions/52823457/while-loop-taking-a-long-time-in-sql-server Were you hoping for a different response today? Sounds like the main challenge you're having is parsing multiple names and dates from the DESCRIPT field. Are there a predefined set of "rules" for how dates and names are stored in that field? Also your sample data doesn't work and I don't see where the "Table_Tasks" even comes into play. – Tim Mylott Oct 17 '18 at 18:09
  • 1
    Possible duplicate of [While loop taking a long time in SQL Server](https://stackoverflow.com/questions/52823457/while-loop-taking-a-long-time-in-sql-server) – Ivan Starostin Oct 17 '18 at 18:20
  • yes I asked the question already, but when I tried some of the responses they didnt work for me so, I thought of asking again. I did mention that in the comments section. – M.Doe Oct 17 '18 at 18:37
  • each cell for descript has the same format 05/25/2017 standard pacific time - name text.........05/25/2017 standard pacific time - name text this continues until the end. Each cell can have many dates and names – M.Doe Oct 17 '18 at 18:37

1 Answers1

0

Here's an option. This is only looking at parsing multiple dates and names from your column if it's following a consistent format. You'd need to adjust to fit your solution. And...

This will only work with the following:

  • Dates and names are stored in the field DESCRIPT in a consistent and repeatable format as: "dd/mm/yyyy time zone - name text dd/mm/yyyy time zone - name text dd/mm/yyyy time zone - name text"

I'm not sure what sort of performance this will give you and if the format of how date and names are stored in that field change, it won't work. That's the importance of knowing if the format is consistent and repeatable.

In the example we're basically splitting the phase out into individual words and then filtering to get what you're after. Depending on version of SQL server I have included 2 difference options for how you can do that split.

  • SQL Server Version 2016+ since it'll use SPLIT_STRING
  • Another which should work back to 2012. It uses XML

There wasn't much sample data provide but based on your comments and replies, I'm making some assumptions and you may need to adjust for your specific needs.

  1. We're after all occurrences of dates in the field
  2. and also the name associated with a date and it comes right after the '-' after said date.

Here's an example:

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.

I did a quick test on one of my servers with record set of 54000 using both options for splitting and parsing and both gave me results in 4-10 seconds. Your mileage may vary.

Tim Mylott
  • 2,553
  • 1
  • 5
  • 11