1

У меня есть таблица с 4 столбцами, одна из которых представляет собой строку с запятыми. Я использовал два подхода к щелевой и расширить этот список в виде таблицы:Почему XML-метод SPLITing, разделенный запятой, занимает больше времени, чем обычный метод цикла CURSOR или WHILE.

Подхожу> Использование XML:

declare @tbl TABLE (
    id1 int, 
    id2 int, 
    id3  int, 
    str1  varchar(255) 
    ) 

INSERT INTO @tbl 
SELECT p.n.value('@id1','INT'), p.n.value('@releaseId','INT'), p.n.value('@id3','INT'), '', p.n.value('.','nvarchar(150)') 
FROM (SELECT CAST(('<r id1="'+CAST(id1 AS varchar(20))+'" id2="'+CAST(id2 AS varchar(20))+'" id3="'+CAST(id3 AS varchar(20))+'">'+REPLACE(CAST(str1 AS nvarchar(MAX)), ',', '</r><r id1="'+CAST(id1 AS varchar(20))+'" id2="'+CAST(id2 AS varchar(20))+'" id3="'+CAST(id3 AS varchar(20))+'">')+'</r>') AS XML) AS x 
    FROM SomeTable WITH(NOLOCK) WHERE CAST(str1 AS nvarchar(MAX)) <> '') R 
CROSS APPLY R.x.nodes('/r') p(n) 

select * from @tbl 

II подход> Использование CURSOR и WHILE метода LOOP

CREATE FUNCTION dbo.fnSplit(
     @sInputList VARCHAR(MAX) -- List of delimited items 
     , @sDelimiter VARCHAR(8000) = ',' -- delimiter that separates items 
    ) RETURNS @List TABLE (item VARCHAR(100)) 

    BEGIN 
    DECLARE @sItem VARCHAR(8000) 
    WHILE CHARINDEX(@sDelimiter,@sInputList,0) <> 0 
    BEGIN 
    SELECT 
     @sItem=RTRIM(LTRIM(SUBSTRING(@sInputList,1,CHARINDEX(@sDelimiter,@sInputList,0)-1))), 
     @sInputList=RTRIM(LTRIM(SUBSTRING(@sInputList,CHARINDEX(@sDelimiter,@sInputList,0)+LEN(@sDelimiter),LEN(@sInputList)))) 

    IF LEN(@sItem) > 0 
     INSERT INTO @List SELECT @sItem 
    END 

    IF LEN(@sInputList) > 0 
    INSERT INTO @List SELECT @sInputList -- Put the last item in 
    RETURN 
    END 
    GO 

------------------------------------------- 
    DECLARE @id1 INT 
    DECLARE @id2  INT 
    DECLARE @id3  INT 
    DECLARE @str1 nvarchar(MAX) 

    declare @tbl TABLE (
      id1 int, 
      id2 int, 
      id3  int, 
      str1  varchar(255) 
      ) 

    DECLARE getList CURSOR FOR 
    SELECT Id1, Id2, Id3, str1 
    FROM someTable 
    OPEN getaddUpdList 
    FETCH NEXT FROM getList INTO @id1, @id2, @id3, @str1 
    WHILE @@FETCH_STATUS = 0 
    BEGIN 
     INSERT INTO @tbl 
     SELECT @id1, @id2, @id3, '', _ID 
     FROM fnSplit(@str1, ',') 
     FETCH NEXT FROM getList INTO @id1, @id2, @id3, @sp, @str1 
    END 
    CLOSE getList 
    DEALLOCATE getList 

    select * from @tbl 

Как то, что я читал онлайн, везде, CURSORS или WHILE, следует избегать. Но здесь, в этом случае, когда я пытаюсь использовать оба вышеупомянутых метода, подход I занимает около 15-20 секунд, в то время как подход II занимает менее секунды. Я не мог понять, почему так. Я пробовал это как в SQL SERVER 2008, так и в 2012 году, но результаты одинаковы.

Любая помощь в понимании такого непредсказуемого поведения будут оценены ..

ответ

0

Этот вопрос давно, но, возможно, вы пришли вокруг снова. Простая точка заключается в том, что вы неправильно поняли подход разделения строки через XML. Посмотрите на этот пример:

declare @tbl TABLE (
    id1 int, 
    id2 int, 
    id3  int, 
    str1  varchar(255) 
    ) 

INSERT INTO @tbl VALUES 
(1,2,3,'This,is,text,1') 
,(10,20,30,'This,is,text,2') 
,(100,200,300,'This,is,text,3') 
,(1000,2000,3000,'This,is,text,4'); 


WITH Casted AS 
(
    SELECT id1 
      ,id2 
      ,id3 
      ,CAST('<x>' + REPLACE(str1,',','</x><x>') + '</x>' AS XML) AS AsXml 
    FROM @tbl 
) 
SELECT id1,id2,id3 
     ,The.Node.value('.','varchar(max)') AS splitvalue 
FROM Casted 
CROSS APPLY Casted.AsXml.nodes('/x') AS The(Node) 

Хитрость заключается в том, чтобы заменить delimmiter символ (ы) с концом и начальным тегом. Начальный тег в начале и конце тега в конце пусть строка будет частичным XML. CROSS APPLY вызывает функцию nodes(). Функция value() выбирает значение текущего (и в данном случае единственного) узла.

Смежные вопросы