简体   繁体   English

SQL Server中的游标用法

[英]Cursor Usage in SQL Server

Here is my code; 这是我的代码;

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

I want to list values row by row as below; 我想按行列出值,如下所示;

1-A
1-A
1-A
2-D
2-E
2-F

Whats wrong for my code? 我的代码有什么问题? I just want to know how cursor does work..Thanks in advance.. 我只想知道游标是如何工作的。

Your question started good with proper sample data and code, but ended bad with "Whats wrong for my code?" 您的问题从正确的示例数据和代码开始就很好,但以“我的代码有什么问题?”而结束 - Your current code returns: -您当前的代码返回:

1    a
2    d

This is because you are setting the @str variable inside the loop, but then overwrite it with the cursor. 这是因为您在循环内设置了@str变量,但是随后用光标将其覆盖。

Splitting strings in SQL Server should be done using built in string_split if you are working on 2016 or higher, and prior to that version by Adam Machanic's CLR function. 如果您正在使用2016或更高版本,并且在该版本之前使用Adam Machanic的CLR函数, string_split使用内置的string_split在SQL Server中拆分字符串。 If you are working on a version lower than 2016 and can't use CLR for some reason, you should probably use an XML splitter, as shown in Aaron Bertrand's Split strings the right way – or the next best way , or Jeff Moden's DelimitedSplit8K . 如果您使用的版本低于2016,并且由于某种原因不能使用CLR,则可能应该使用XML拆分器,如正确的方式(如Aaron Bertrand的Split字符串所示)或第二种最佳方式 ,或者是Jeff Moden的DelimitedSplit8K

In SQL, a cursor is usually the wrong tool to do anything. 在SQL中,游标通常是做任何事情的错误工具。 SQL works best with a set based approach, not with a RBAR approach (which is all that a cursor can do). SQL使用基于集合的方法效果最好,而不是使用RBAR方法(游标可以做的所有事情)。

Basically, a cursor just takes the resultset of it's select statement and spit it out Row By Agonizing Row with fetch next 基本上,光标只是需要它的ResultSet的select语句,并吐出来连续被折腾行fetch next
This usually means terrible performance compared to the same operation done in a set based approach. 与基于集合的方法中执行的相同操作相比,这通常意味着糟糕的性能。
This does not mean that you should never use cursors, but it is what I would refer to as a last resort, and only when you absolutely have no other choice. 这并不意味着您永远不要使用游标,而是我将其称为万不得已的方法,并且仅当您绝对没有其他选择时。

If using SQL Server 2016, just use STRING_SPLIT: 如果使用SQL Server 2016,则只需使用STRING_SPLIT:

select Id, cs.Value 
from Splitter
cross apply STRING_SPLIT (Vals, ',') cs

You should try and avoid cursors where possible, they are a basic loop that leads to Row-by-Row solutions that can be very slow on large SQL datasets. 您应该尽量避免使用游标,因为游标是一个基本的循环,会导致逐行解决方案在大型SQL数据集上可能非常慢。 Cursors also reserve space in the Buffer (memory) and can steal resources from other processes. 游标还可以在缓冲区(内存)中保留空间,并且可以从其他进程中窃取资源。 If you have to loop you should use a WHILE construct. 如果必须循环,则应使用WHILE构造。

SQL offers other SET based solutions that can replace a CURSOR solution. SQL提供了其他基于SET的解决方案,可以代替CURSOR解决方案。

Your goal may more efficiently be achieved with a Recursive CTE. 使用递归CTE可以更有效地实现您的目标。

Here's an example of a Recursive CTE that can replace the need for your cursor: 这是一个递归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 

If your [Vals] column is always in a set format then you could use CROSS APPLY with VALUES for a more efficient solution. 如果[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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM