我有一个循环,用于检查表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
答案 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