Использование курсора в SQL Server - PullRequest
0 голосов
/ 21 мая 2018

Вот мой код;

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

Что не так для моего кода?Я просто хочу знать, как работает курсор .. Заранее спасибо ..

Ответы [ 3 ]

0 голосов
/ 21 мая 2018

Ваш вопрос начался с правильных примеров данных и кода, но закончился с "Что случилось с моим кодом?"- Ваш текущий код возвращает:

1    a
2    d

Это потому, что вы устанавливаете переменную @str внутри цикла, но затем перезаписываете ее курсором.

Разделение строк в SQL Server должноделать это с помощью встроенного string_split, если вы работаете в 2016 году или новее, и до этой версии с помощью функции CLR Адама Мачаника.Если вы работаете с версией ниже 2016 года и по какой-то причине не можете использовать CLR, вам, вероятно, следует использовать сплиттер XML, как показано в Аарона Бертрана: разделение строк - правильный путь или следующий лучший способ или DelimitedSplit8K .

Джеффа Модена. В SQL курсор обычно является неправильным инструментом для каких-либо действий.SQL лучше всего работает с подходом, основанным на множестве, а не с подходом RBAR (что все, что может сделать курсор).

По сути, курсор просто берет результирующий набор своего оператора select и выплевывает его Row By AgonizingСтрока с fetch next
Это обычно означает ужасную производительность по сравнению с той же операцией, выполняемой в подходе, основанном на множестве.
Это не означает, что вы никогда не должны использовать курсоры, но это то, что я бы назвал последнимкурорт, и только тогда, когда у вас совершенно нет другого выбора.

0 голосов
/ 21 мая 2018

Вы должны стараться избегать курсоров, где это возможно, они являются базовым циклом, который приводит к построчным решениям, которые могут быть очень медленными на больших наборах данных 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)
0 голосов
/ 21 мая 2018

Если вы используете SQL Server 2016, просто используйте STRING_SPLIT:

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