While循环在SQL Server中花费很长时间

时间:2018-10-15 19:23:48

标签: sql-server tsql

我有一个循环,用于检查表descript的{​​{1}}列中的所有名称和日期,并将它们作为单独的行存储在其他表(tmp13)中。问题是while循环执行了很长时间。我不知道如何使其运行更快。

这是我的代码,检查tmp14列中的名称和日期。 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
    SELCET @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

3 个答案:

答案 0 :(得分:1)

将嵌套循环转换为两个CROSS JOINED选择,并使用它来插入值。整体结构将是这样

INSERT INTO #TMP14 
    (Id, product_num, REQUESTED, FirstDate , RequestedBY, date, name)
SELECT ...
CROSS JOIN
SELECT ...

现在,您必须将生成数字序列的外部循环转换为这些SELECT语句之一,而将将字符串拆分为其他SELECT语句的内部循环转换为

这个SO问题对第一个问题有几个答案:How to generate a range of numbers between two numbers?

这个特殊问题可以回答第二个问题:Turning a Comma Separated string into individual rows

SQL-Server和数据库服务器通常是高度优化的查询引擎,但是在循环处理表方面不是很好。

答案 1 :(得分:0)

虽然循环会很慢,因为当您使用过程逻辑以声明性编程语言进行开发时,就会发生这种情况。为此,您可以使用patternSplitCM。示例数据存在问题,但这应该可以满足您的需求。

SELECT t.p_Num,
       t.REQUESTED,
       t.date1,
       t.BY1,
       parsedDate1 = MAX(CASE WHEN s.ItemNumber = 1 THEN s.Item END),
       parsedDate2 = MAX(CASE WHEN s.ItemNumber > 1 THEN s.Item END)
FROM   #Tmp13 AS t
CROSS 
APPLY    dbo.patternSplitCM(t.DESCRIPT, '[0-9/]') AS s
WHERE    s.[Matched] = 1 AND TRY_CAST(s.item AS DATE) IS NOT NULL
GROUP BY t.p_Num,t.REQUESTED, t.date1,t.BY1

结果:

p_Num       REQUESTED    date1        BY1       parsedDate1  parsedDate2
----------- ------------ ------------ --------- ------------ ------------
100         John         5/30/2017    James     05/30/2017   05/30/2017
200         John         6/1/2017     Rachael   06/1/2017    06/1/2017

最后,为了获得最佳性能,您希望p_Num,REQUESTED,DATE1,BY1列上的索引支持GROUP BY子句。索引看起来像:

CREATE UNIQUE NONCLUSTERED INDEX uq_xxx ON #Tmp13(p_Num,REQUESTED,DATE1,BY1) 
--INCLUDE (DESCRIPT); -- if you can change this to VARCHAR(8000) or VARCHAR(max)

还请注意,使用并行执行计划,这可能会运行得更快。为此,您可以使用TRACEFLAG 8649或Adam Machanic的make_parallel()。

答案 2 :(得分:-1)

一些速度改进 使用单选而不是多选来设置变量

SELCET @desc = descript,  @product_num = p_Num ,  @REQUESTED = REQUESTED  , @FirstDate = DATE1,  @RequestedBy = BY1  FROM #TMP13 WHERE Id = @Id

删除文本数据类型(不建议使用),使用varchar(max)

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

尽可能使用varchar代替nvarchar。(@ desc变量)

请勿在循环中重复patindex调用

set @dateposition=999999--loop start
while (@dateposition > 0)
begin
    set @dateposition = patindex('%[0-9][0-9]/[0-9][0-9]/[0-9][0-9][0-9]%',@desc)
    if(@dateposition>0)begin
    ....
    end
end
相关问题