SQL 服务器如何使用 WHILE 查询从多个结果中输出一个 table 结果
SQL Server How to output one table result from multiple results with a WHILE query
来自这个答案:Is there a way to loop through a table variable in TSQL without using a cursor?
我正在使用方法
WHILE EXISTS(SELECT * FROM #Temp)
问题是它正在输出多个 tables,如果可能的话我想输出一个 table.
Declare @Id int
WHILE EXISTS(SELECT * FROM #Temp)
Begin
Select Top 1 @Id = Id From #Temp
--Do some processing here
Delete #Temp Where Id = @Id
End
所以现在它输出这个:
x y
-- --
1 a
x y
-- --
1 b
但我希望它输出这个:
x y
-- --
1 a
2 b
我正在努力实现的目标,我在一个字段中有这个:
1234,1432,1235
我有一个将字段拆分为记录的过程(它适用于 sql 服务器 2000):
DECLARE @String VARCHAR(100)
SELECT @String = str FROM field --with the 1234,1432,1235
SELECT SUBSTRING(',' + @String + ',', Number + 1,
CHARINDEX(',', ',' + @String + ',', Number + 1) - Number -1)AS str
INTO #temp
FROM master..spt_values
WHERE Type = 'P'
AND Number <= LEN(',' + @String + ',') - 1
AND SUBSTRING(',' + @String + ',', Number, 1) = ','
GO
所以现在,#temp 有:
str
---
1234
1432
1235
所以我需要遍历每条记录来查询我需要的信息。
我希望它输出如下内容:
str name age
--- ---- ---
1234 Bob 23
1432 Jay 41
1235 Tim 12
当前的 While 循环输出是这样的,我不想要:
str name age
--- ---- ---
1234 Bob 23
str name age
--- ---- ---
1432 Jay 41
str name age
--- ---- ---
1235 Tim 12
最终工作结果:
SET NOCOUNT ON;
DECLARE @String VARCHAR(1000);
SELECT @String = Tnn FROM (SELECT
CO.USER_2 AS Tnn
FROM
[VMFG].[dbo].[CUSTOMER_ORDER] AS CO
LEFT JOIN DBO.Tnn_Header AS Tnn ON Tnn.TnnNumber = CO.USER_2 AND Tnn.StatusID = '5' WHERE CO.ID = 'ORDERID') AS Place --with the 1234,1432,1235
DECLARE @Id nvarchar(50),
@Discount nvarchar(50),
@Spin nvarchar(50),
@Commission_Hmm nvarchar(50),
@Commission nvarchar(50),
@TnnID nvarchar(50);
DECLARE @Output TABLE (
TnnNumber nvarchar(50),
Discount nvarchar(50),
Spin nvarchar(50),
Commission_Hmm nvarchar(50),
Commission nvarchar(50),
TnnID nvarchar(50));
DECLARE crs CURSOR STATIC LOCAL READ_ONLY FORWARD_ONLY
FOR SELECT SUBSTRING(',' + @String + ',', Number + 1,
CHARINDEX(',', ',' + @String + ',', Number + 1) - Number -1) AS [ID]
FROM master..spt_values
WHERE Type = 'P'
AND Number <= LEN(',' + @String + ',') - 1
AND SUBSTRING(',' + @String + ',', Number, 1) = ',';
OPEN crs;
FETCH NEXT
FROM crs
INTO @Id;
WHILE (@@FETCH_STATUS = 0)
BEGIN
-- do some processing..
SELECT
@Id = TH.TnnNumber,
@Discount = CASE WHEN COUNT(DISTINCT TL.DiscountCodeID) > 1 THEN 'Varies, View Tnn' ELSE CAST(MAX(DC.Value) AS VARCHAR(60)) END,
@Spin = CASE WHEN TS.SpinID > 4 THEN 'Has Specifics, View Tnn' ELSE TS.Value END,
@Commission_Hmm = CASE WHEN COUNT(DISTINCT TL.Commission_Hmm) > 1 THEN 'Varies, View Tnn' ELSE CAST(MAX( ISNULL(str(TL.Commission_Hmm,12),'Default Comm')) AS VARCHAR(60)) END,
@Commission = CASE WHEN COUNT(DISTINCT TL.Commission) > 1 THEN 'Varies, View Tnn' ELSE CAST(MAX(ISNULL(str(TL.Commission,12),'Default Comm')) AS VARCHAR(60)) END,
@TnnID = TL.TnnID
FROM DBO.Tnn_Header AS TH
LEFT JOIN DBO.Tnn_LINE AS TL ON TH.TnnID = TL.TnnID
LEFT JOIN DBO.Tnn_Spin AS TS ON TH.SpinID = TS.SpinID
LEFT JOIN DBO.Tnn_DiscountCode AS DC ON TL.DiscountCodeID = DC.DiscountCodeID
WHERE TnnNumber = @id
GROUP BY
TH.TnnNumber,
TS.SpinID,
TS.Value,
TL.TnnID
-- end do some processing..
INSERT INTO @Output (TnnNumber, Discount, Spin, Commission_Hmm, Commission, TnnID)
VALUES (@Id, @Discount, @Spin, @Commission_Hmm, @Commission, @TnnID);
FETCH NEXT
FROM crs
INTO @Id;
END;
CLOSE crs;
DEALLOCATE crs;
SELECT TnnNumber, Discount, Spin, Commission_Hmm, Commission, TnnID
FROM @Output;
你的查询有语法错误,但我尝试了下面的查询并且工作正常
-- this is only to populate my data table
Select object_id Id, name Into #Temp From sys.tables
select * into #temp2 from #Temp where 1=2
Declare @Id int
WHILE EXISTS(SELECT * FROM #Temp)
Begin
Select Top 1 @Id = Id
From #Temp
ORDER BY Id -- this order is important
-- use insert...into, NOT select...into
insert into #temp2
select *
from #Temp
where Id = @Id
Delete #Temp Where Id = @Id
End
顺便说一句,您不能在循环中使用 SELECT...INTO,因为第二次迭代会引发错误。
您需要在循环外创建#temp2 并使用 INSERT...INTO 而不是 SELECT...INTO
听从这些糟糕的建议是在浪费时间和精力。如果你绝对必须(特别强调必须)采取逐行方法(CURSOR或WHILE循环),那么你会更好用 CURSOR 关闭。它是一种更高效、更不易出错的内置结构。您只需要使用正确的选项,例如 STATIC
、LOCAL
、READ_ONLY
和 FORWARD_ONLY
。如果游标查询仅命中临时 tables and/or table 变量,则不需要 STATIC
。
人们会对此争论不休并说 "you must avoid cursors at all cost!",但他们还没有进行测试以证明这种流行的观念真的只是一个神话。如果他们已经完成了似乎可以证实这一点的测试,那么他们就没有设置适当的选项,主要是 STATIC
,这会将游标查询的结果转储到临时 table 中。如果没有此选项,获取新行将重新检查基础 tables 以确保它们仍然存在,并且 that 是性能受到影响的地方(I/O加上锁定)。这也是为什么在仅查询临时 tables and/or table 变量时 通常 不需要 STATIC
选项的原因。 "re-checking" 是什么意思?只需查看 @@FETCH_STATUS 的文档即可。 return 值不仅仅涵盖 "success" (0
) 和 "no more rows" (-1
):还有一个 return 值,(-2
), 即 "The row fetched is missing".
SET NOCOUNT ON;
DECLARE @Id INT,
@Name sysname,
@Type VARCHAR(5);
-- the Table Variable replaces #Temp2 in the original query
DECLARE @Output TABLE (Id INT NOT NULL, Name sysname, [Type] VARCHAR(5));
-- the CURSOR replaces #Temp in the original query
DECLARE crs CURSOR STATIC LOCAL READ_ONLY FORWARD_ONLY
FOR SELECT [object_id], name, [type]
FROM sys.objects -- dbo.sysobjects for SQL 2000 -- ATable in the original query
ORDER BY [object_id] ASC;
OPEN crs;
FETCH NEXT
FROM crs
INTO @Id, @Name, @Type;
WHILE (@@FETCH_STATUS = 0)
BEGIN
INSERT INTO @Output (Id, Name, [Type])
VALUES (@Id, @Name, @Type);
-- do some processing..
FETCH NEXT -- replaces the DELETE and re-SELECT in the original query
FROM crs
INTO @Id, @Name, @Type;
END;
CLOSE crs;
DEALLOCATE crs;
SELECT Id, Name, [Type]
FROM @Output;
更新
鉴于正在对拆分 INT 的 CSV 的查询进行迭代,生成的查询将类似于以下内容:
SET NOCOUNT ON;
DECLARE @String VARCHAR(1000);
SELECT @String = str FROM [Table]; --with the 1234,1432,1235
DECLARE @Id INT,
@Name NVARCHAR(50),
@Age TINYINT;
DECLARE @Output TABLE (Id INT NOT NULL, Name NVARCHAR(50), Age TINYINT);
DECLARE crs CURSOR STATIC LOCAL READ_ONLY FORWARD_ONLY
FOR SELECT SUBSTRING(',' + @String + ',', Number + 1,
CHARINDEX(',', ',' + @String + ',', Number + 1) - Number -1) AS [ID]
FROM master..spt_values
WHERE Type = 'P'
AND Number <= LEN(',' + @String + ',') - 1
AND SUBSTRING(',' + @String + ',', Number, 1) = ',';
OPEN crs;
FETCH NEXT
FROM crs
INTO @Id;
WHILE (@@FETCH_STATUS = 0)
BEGIN
-- do some processing..
-- Logic to set value of @Name
-- Logic to set value of @Age
INSERT INTO @Output (Id, Name, Age)
VALUES (@Id, @Name, @Age);
FETCH NEXT
FROM crs
INTO @Id;
END;
CLOSE crs;
DEALLOCATE crs;
SELECT Id, Name, Age
FROM @Output;
来自这个答案:Is there a way to loop through a table variable in TSQL without using a cursor?
我正在使用方法
WHILE EXISTS(SELECT * FROM #Temp)
问题是它正在输出多个 tables,如果可能的话我想输出一个 table.
Declare @Id int
WHILE EXISTS(SELECT * FROM #Temp)
Begin
Select Top 1 @Id = Id From #Temp
--Do some processing here
Delete #Temp Where Id = @Id
End
所以现在它输出这个:
x y
-- --
1 a
x y
-- --
1 b
但我希望它输出这个:
x y
-- --
1 a
2 b
我正在努力实现的目标,我在一个字段中有这个:
1234,1432,1235
我有一个将字段拆分为记录的过程(它适用于 sql 服务器 2000):
DECLARE @String VARCHAR(100)
SELECT @String = str FROM field --with the 1234,1432,1235
SELECT SUBSTRING(',' + @String + ',', Number + 1,
CHARINDEX(',', ',' + @String + ',', Number + 1) - Number -1)AS str
INTO #temp
FROM master..spt_values
WHERE Type = 'P'
AND Number <= LEN(',' + @String + ',') - 1
AND SUBSTRING(',' + @String + ',', Number, 1) = ','
GO
所以现在,#temp 有:
str
---
1234
1432
1235
所以我需要遍历每条记录来查询我需要的信息。
我希望它输出如下内容:
str name age
--- ---- ---
1234 Bob 23
1432 Jay 41
1235 Tim 12
当前的 While 循环输出是这样的,我不想要:
str name age
--- ---- ---
1234 Bob 23
str name age
--- ---- ---
1432 Jay 41
str name age
--- ---- ---
1235 Tim 12
最终工作结果:
SET NOCOUNT ON;
DECLARE @String VARCHAR(1000);
SELECT @String = Tnn FROM (SELECT
CO.USER_2 AS Tnn
FROM
[VMFG].[dbo].[CUSTOMER_ORDER] AS CO
LEFT JOIN DBO.Tnn_Header AS Tnn ON Tnn.TnnNumber = CO.USER_2 AND Tnn.StatusID = '5' WHERE CO.ID = 'ORDERID') AS Place --with the 1234,1432,1235
DECLARE @Id nvarchar(50),
@Discount nvarchar(50),
@Spin nvarchar(50),
@Commission_Hmm nvarchar(50),
@Commission nvarchar(50),
@TnnID nvarchar(50);
DECLARE @Output TABLE (
TnnNumber nvarchar(50),
Discount nvarchar(50),
Spin nvarchar(50),
Commission_Hmm nvarchar(50),
Commission nvarchar(50),
TnnID nvarchar(50));
DECLARE crs CURSOR STATIC LOCAL READ_ONLY FORWARD_ONLY
FOR SELECT SUBSTRING(',' + @String + ',', Number + 1,
CHARINDEX(',', ',' + @String + ',', Number + 1) - Number -1) AS [ID]
FROM master..spt_values
WHERE Type = 'P'
AND Number <= LEN(',' + @String + ',') - 1
AND SUBSTRING(',' + @String + ',', Number, 1) = ',';
OPEN crs;
FETCH NEXT
FROM crs
INTO @Id;
WHILE (@@FETCH_STATUS = 0)
BEGIN
-- do some processing..
SELECT
@Id = TH.TnnNumber,
@Discount = CASE WHEN COUNT(DISTINCT TL.DiscountCodeID) > 1 THEN 'Varies, View Tnn' ELSE CAST(MAX(DC.Value) AS VARCHAR(60)) END,
@Spin = CASE WHEN TS.SpinID > 4 THEN 'Has Specifics, View Tnn' ELSE TS.Value END,
@Commission_Hmm = CASE WHEN COUNT(DISTINCT TL.Commission_Hmm) > 1 THEN 'Varies, View Tnn' ELSE CAST(MAX( ISNULL(str(TL.Commission_Hmm,12),'Default Comm')) AS VARCHAR(60)) END,
@Commission = CASE WHEN COUNT(DISTINCT TL.Commission) > 1 THEN 'Varies, View Tnn' ELSE CAST(MAX(ISNULL(str(TL.Commission,12),'Default Comm')) AS VARCHAR(60)) END,
@TnnID = TL.TnnID
FROM DBO.Tnn_Header AS TH
LEFT JOIN DBO.Tnn_LINE AS TL ON TH.TnnID = TL.TnnID
LEFT JOIN DBO.Tnn_Spin AS TS ON TH.SpinID = TS.SpinID
LEFT JOIN DBO.Tnn_DiscountCode AS DC ON TL.DiscountCodeID = DC.DiscountCodeID
WHERE TnnNumber = @id
GROUP BY
TH.TnnNumber,
TS.SpinID,
TS.Value,
TL.TnnID
-- end do some processing..
INSERT INTO @Output (TnnNumber, Discount, Spin, Commission_Hmm, Commission, TnnID)
VALUES (@Id, @Discount, @Spin, @Commission_Hmm, @Commission, @TnnID);
FETCH NEXT
FROM crs
INTO @Id;
END;
CLOSE crs;
DEALLOCATE crs;
SELECT TnnNumber, Discount, Spin, Commission_Hmm, Commission, TnnID
FROM @Output;
你的查询有语法错误,但我尝试了下面的查询并且工作正常
-- this is only to populate my data table
Select object_id Id, name Into #Temp From sys.tables
select * into #temp2 from #Temp where 1=2
Declare @Id int
WHILE EXISTS(SELECT * FROM #Temp)
Begin
Select Top 1 @Id = Id
From #Temp
ORDER BY Id -- this order is important
-- use insert...into, NOT select...into
insert into #temp2
select *
from #Temp
where Id = @Id
Delete #Temp Where Id = @Id
End
顺便说一句,您不能在循环中使用 SELECT...INTO,因为第二次迭代会引发错误。 您需要在循环外创建#temp2 并使用 INSERT...INTO 而不是 SELECT...INTO
听从这些糟糕的建议是在浪费时间和精力。如果你绝对必须(特别强调必须)采取逐行方法(CURSOR或WHILE循环),那么你会更好用 CURSOR 关闭。它是一种更高效、更不易出错的内置结构。您只需要使用正确的选项,例如 STATIC
、LOCAL
、READ_ONLY
和 FORWARD_ONLY
。如果游标查询仅命中临时 tables and/or table 变量,则不需要 STATIC
。
人们会对此争论不休并说 "you must avoid cursors at all cost!",但他们还没有进行测试以证明这种流行的观念真的只是一个神话。如果他们已经完成了似乎可以证实这一点的测试,那么他们就没有设置适当的选项,主要是 STATIC
,这会将游标查询的结果转储到临时 table 中。如果没有此选项,获取新行将重新检查基础 tables 以确保它们仍然存在,并且 that 是性能受到影响的地方(I/O加上锁定)。这也是为什么在仅查询临时 tables and/or table 变量时 通常 不需要 STATIC
选项的原因。 "re-checking" 是什么意思?只需查看 @@FETCH_STATUS 的文档即可。 return 值不仅仅涵盖 "success" (0
) 和 "no more rows" (-1
):还有一个 return 值,(-2
), 即 "The row fetched is missing".
SET NOCOUNT ON;
DECLARE @Id INT,
@Name sysname,
@Type VARCHAR(5);
-- the Table Variable replaces #Temp2 in the original query
DECLARE @Output TABLE (Id INT NOT NULL, Name sysname, [Type] VARCHAR(5));
-- the CURSOR replaces #Temp in the original query
DECLARE crs CURSOR STATIC LOCAL READ_ONLY FORWARD_ONLY
FOR SELECT [object_id], name, [type]
FROM sys.objects -- dbo.sysobjects for SQL 2000 -- ATable in the original query
ORDER BY [object_id] ASC;
OPEN crs;
FETCH NEXT
FROM crs
INTO @Id, @Name, @Type;
WHILE (@@FETCH_STATUS = 0)
BEGIN
INSERT INTO @Output (Id, Name, [Type])
VALUES (@Id, @Name, @Type);
-- do some processing..
FETCH NEXT -- replaces the DELETE and re-SELECT in the original query
FROM crs
INTO @Id, @Name, @Type;
END;
CLOSE crs;
DEALLOCATE crs;
SELECT Id, Name, [Type]
FROM @Output;
更新
鉴于正在对拆分 INT 的 CSV 的查询进行迭代,生成的查询将类似于以下内容:
SET NOCOUNT ON;
DECLARE @String VARCHAR(1000);
SELECT @String = str FROM [Table]; --with the 1234,1432,1235
DECLARE @Id INT,
@Name NVARCHAR(50),
@Age TINYINT;
DECLARE @Output TABLE (Id INT NOT NULL, Name NVARCHAR(50), Age TINYINT);
DECLARE crs CURSOR STATIC LOCAL READ_ONLY FORWARD_ONLY
FOR SELECT SUBSTRING(',' + @String + ',', Number + 1,
CHARINDEX(',', ',' + @String + ',', Number + 1) - Number -1) AS [ID]
FROM master..spt_values
WHERE Type = 'P'
AND Number <= LEN(',' + @String + ',') - 1
AND SUBSTRING(',' + @String + ',', Number, 1) = ',';
OPEN crs;
FETCH NEXT
FROM crs
INTO @Id;
WHILE (@@FETCH_STATUS = 0)
BEGIN
-- do some processing..
-- Logic to set value of @Name
-- Logic to set value of @Age
INSERT INTO @Output (Id, Name, Age)
VALUES (@Id, @Name, @Age);
FETCH NEXT
FROM crs
INTO @Id;
END;
CLOSE crs;
DEALLOCATE crs;
SELECT Id, Name, Age
FROM @Output;