这是我的代码;
CREATE TABLE Splitter ( Id INT ,Vals VARCHAR(10) ) INSERT INTO Splitter VALUES (1,'A,B,C'), (2,'D,E,F') DECLARE @Id INT DECLARE @Str VARCHAR(10) DECLARE @Tbl TABLE(Id int, Vals varchar(10)) DECLARE MyCursor CURSOR FOR select Id, Vals from splitter OPEN MyCursor FETCH NEXT FROM MyCursor INTO @Id, @Str WHILE @@FETCH_STATUS = 0 BEGIN insert into @Tbl select @Id, substring(@Str,charindex(',',@Str)-1,1) set @Str=substring(@Str,charindex(',',@Str)+1,len(@Str)) FETCH NEXT FROM MyCursor INTO @Id,@Str END CLOSE MyCursor DEALLOCATE MyCursor
我想逐行列出值,如下所示;
1-A
1-A
1-A
2-D
2-E
2-F
我的代码有什么问题?我只想知道光标是如何工作的......提前感谢..
答案 0 :(得分:4)
你的问题在适当的样本数据和代码开始时很好,但结果很糟糕"我的代码有什么问题?" - 您当前的代码返回:
1 a
2 d
这是因为你在循环中设置了@str
变量,然后用光标覆盖它。
如果您使用2016或更高版本,并且在该版本之前使用Adam Machanic的CLR功能,则应使用内置string_split
分割SQL Server中的字符串。如果您的工作版本低于2016且由于某种原因无法使用CLR,则应该使用XML分割器,如Aaron Bertrand的Split strings the right way – or the next best way或Jeff Moden&#39所示。 ; s DelimitedSplit8K。
在SQL中,游标通常是执行任何操作的错误工具。 SQL最适合使用基于集合的方法,而不是使用RBAR方法(这是游标可以执行的所有操作)。
基本上,光标只是获取它的select语句的结果集,然后用fetch next
吐出行按Agonizing Row
与基于集合的方法中的相同操作相比,这通常意味着可怕的性能
这并不意味着你永远不应该使用游标,但这是我所说的最后手段,并且只有当你绝对没有其他选择时。
答案 1 :(得分:2)
如果使用SQL Server 2016,只需使用STRING_SPLIT:
select Id, cs.Value
from Splitter
cross apply STRING_SPLIT (Vals, ',') cs
答案 2 :(得分:2)
你应该尽可能地避免使用游标,它们是一个基本的循环,导致逐行解决方案在大型SQL数据集上可能会非常慢。游标还在缓冲区(内存)中保留空间,并可以从其他进程中窃取资源。如果必须循环,则应使用WHILE构造。
SQL提供了其他基于SET的解决方案,可以取代CURSOR解决方案。
使用递归CTE可以更有效地实现目标。
这是一个递归CTE的例子,它可以取代光标的需要:
CREATE TABLE #Splitter
(
Id INT
,Vals VARCHAR(10)
)
INSERT INTO #Splitter VALUES
(1,'A,B,C'),
(2,'D,E,F')
;WITH cte AS
(
--Anchor point, the first Value from each ID
SELECT
Id
,LEFT(Vals,CHARINDEX(',',Vals)-1) AS Val
,RIGHT(Vals,LEN(Vals)-CHARINDEX(',',Vals)) AS Remainder
FROM #Splitter
WHERE
Vals IS NOT NULL AND CHARINDEX(',',Vals)>0
UNION ALL
--Central Recursion for each value
SELECT
Id,LEFT(Remainder,CHARINDEX(',',Remainder)-1)
,RIGHT(Remainder,LEN(Remainder)-CHARINDEX(',',Remainder))
FROM cte
WHERE Remainder IS NOT NULL AND CHARINDEX(',',Remainder)>0
UNION ALL
--Error prevention, handling the end of the string
SELECT
Id,Remainder,null
FROM cte
WHERE Remainder IS NOT NULL AND CHARINDEX(',',Remainder)=0
)
SELECT ID,VAL FROM cte
如果您的[Vals]列始终采用设定格式,那么您可以使用CROSS APPLY和VALUES来获得更有效的解决方案。
SELECT
ID
,v.Val
FROM
#Splitter S
CROSS APPLY (VALUES (left(S.Vals,1)),(SUBSTRING(Vals,3,1)),(RIGHT(Vals,1))) v(Val)