Насколько я знаю, вы перезаписали, поэтому я думаю, что двигатель сохраняет его в другом пространстве памяти, тогда как b все еще указывает на адрес памяти старого a (который каким-то образом не разрушается).
Вы можете использовать замечательные рекурсивные функции из SQL Server:
Пример таблицы:
CREATE TABLE Testdata
(
SomeID INT,
OtherID INT,
String VARCHAR(MAX)
)
INSERT Testdata SELECT 1, 9, '18,20,22'
INSERT Testdata SELECT 2, 8, '17,19'
INSERT Testdata SELECT 3, 7, '13,19,20'
INSERT Testdata SELECT 4, 6, ''
INSERT Testdata SELECT 9, 11, '1,2,3,4'
Запрос
;WITH tmp(SomeID, OtherID, DataItem, String) AS
(
SELECT
SomeID,
OtherID,
LEFT(String, CHARINDEX(',', String + ',') - 1),
STUFF(String, 1, CHARINDEX(',', String + ','), '')
FROM Testdata
UNION all
SELECT
SomeID,
OtherID,
LEFT(String, CHARINDEX(',', String + ',') - 1),
STUFF(String, 1, CHARINDEX(',', String + ','), '')
FROM tmp
WHERE
String > ''
)
SELECT
SomeID,
OtherID,
DataItem
FROM tmp
ORDER BY SomeID
-- OPTION (maxrecursion 0)
-- normally recursion is limited to 100. If you know you have very long
-- strings, uncomment the option
< hr> Выход
SomeID | OtherID | DataItem
--------+---------+----------
1 | 9 | 18
1 | 9 | 20
1 | 9 | 22
2 | 8 | 17
2 | 8 | 19
3 | 7 | 13
3 | 7 | 19
3 | 7 | 20
4 | 6 |
9 | 11 | 1
9 | 11 | 2
9 | 11 | 3
9 | 11 | 4
Ниже работает на sql server 2008
select *, ROW_NUMBER() OVER(order by items) as row#
from
( select 134 myColumn1, 34 myColumn2, 'd,c,k,e,f,g,h,a' comaSeperatedColumn) myTable
cross apply
SPLIT (rtrim(comaSeperatedColumn), ',') splitedTable -- gives 'items' column
Получит все декартово произведение с столбцами таблицы происхождения плюс «элементы» таблицы split.
Проверьте
SELECT A.OtherID,
Split.a.value('.', 'VARCHAR(100)') AS Data
FROM
(
SELECT OtherID,
CAST ('<M>' + REPLACE(Data, ',', '</M><M>') + '</M>' AS XML) AS Data
FROM Table1
) AS A CROSS APPLY Data.nodes ('/M') AS Split(a);
По состоянию на февраль 2016 года - см. таблицу TALLY Table Example - очень вероятно, чтобы превзойти мой TVF ниже, с февраля 2014 года. Сохраняя исходное сообщение ниже для потомков:
Слишком много повторяющегося кода для моей симпатии в приведенных выше примерах. И мне не нравится производительность CTE и XML. Кроме того, явный Id
, чтобы потребители, имеющие специфику заказа, могли указать предложение ORDER BY
.
CREATE FUNCTION dbo.Split
(
@Line nvarchar(MAX),
@SplitOn nvarchar(5) = ','
)
RETURNS @RtnValue table
(
Id INT NOT NULL IDENTITY(1,1) PRIMARY KEY CLUSTERED,
Data nvarchar(100) NOT NULL
)
AS
BEGIN
IF @Line IS NULL RETURN
DECLARE @split_on_len INT = LEN(@SplitOn)
DECLARE @start_at INT = 1
DECLARE @end_at INT
DECLARE @data_len INT
WHILE 1=1
BEGIN
SET @end_at = CHARINDEX(@SplitOn,@Line,@start_at)
SET @data_len = CASE @end_at WHEN 0 THEN LEN(@Line) ELSE @end_at-@start_at END
INSERT INTO @RtnValue (data) VALUES( SUBSTRING(@Line,@start_at,@data_len) );
IF @end_at = 0 BREAK;
SET @start_at = @end_at + @split_on_len
END
RETURN
END
Приятно видеть, что он был решен в версии 2016 года, но для всех тех, что на этом нет, вот две обобщенные и упрощенные версии вышеприведенных методов.
XML-метод
XML-метод:
create function dbo.splitString(@input Varchar(max), @Splitter VarChar(99)) returns table as
Return
SELECT Split.a.value('.', 'VARCHAR(max)') AS Data FROM
( SELECT CAST ('<M>' + REPLACE(@input, @Splitter, '</M><M>') + '</M>' AS XML) AS Data
) AS A CROSS APPLY Data.nodes ('/M') AS Split(a);
Рекурсивный метод:
create function dbo.splitString(@input Varchar(max), @Splitter VarChar(99)) returns table as
Return
SELECT Split.a.value('.', 'VARCHAR(max)') AS Data FROM
( SELECT CAST ('<M>' + REPLACE(@input, @Splitter, '</M><M>') + '</M>' AS XML) AS Data
) AS A CROSS APPLY Data.nodes ('/M') AS Split(a);
Рекурсивный метод:
create function dbo.splitString(@input Varchar(max), @Splitter Varchar(99)) returns table as
Return
with tmp (DataItem, ix) as
( select @input , CHARINDEX('',@Input) --Recu. start, ignored val to get the types right
union all
select Substring(@input, ix+1,ix2-ix-1), ix2
from (Select *, CHARINDEX(@Splitter,@Input+@Splitter,ix+1) ix2 from tmp) x where ix2<>0
) select DataItem from tmp where ix<>0
Функция в действии
Create table TEST_X (A int, CSV Varchar(100));
Insert into test_x select 1, 'A,B';
Insert into test_x select 2, 'C,D';
Select A,data from TEST_X x cross apply dbo.splitString(x.CSV,',') Y;
Drop table TEST_X
XML-МЕТОД 2: Юникод Дружественный
select t.OtherID,x.Kod
from testData t
cross apply (select Code from dbo.Split(t.Data,',') ) x
CROSS APPLY
, это очень полезно!
– tobriand
3 November 2015 в 14:45
select t.OtherID, x.* from testData t cross apply (select item as Data from dbo.Split(t.Data,',') ) x
– Akbar Kautsar
11 August 2017 в 04:07
DECLARE @id_list VARCHAR(MAX) = '1234,23,56,576,1231,567,122,87876,57553,1216'
DECLARE @table TABLE ( id VARCHAR(50) )
DECLARE @x INT = 0
DECLARE @firstcomma INT = 0
DECLARE @nextcomma INT = 0
SET @x = LEN(@id_list) - LEN(REPLACE(@id_list, ',', '')) + 1 -- number of ids in id_list
WHILE @x > 0
BEGIN
SET @nextcomma = CASE WHEN CHARINDEX(',', @id_list, @firstcomma + 1) = 0
THEN LEN(@id_list) + 1
ELSE CHARINDEX(',', @id_list, @firstcomma + 1)
END
INSERT INTO @table
VALUES ( SUBSTRING(@id_list, @firstcomma + 1, (@nextcomma - @firstcomma) - 1) )
SET @firstcomma = CHARINDEX(',', @id_list, @firstcomma + 1)
SET @x = @x - 1
END
SELECT *
FROM @table
Функция
CREATE FUNCTION dbo.SplitToRows (@column varchar(100), @separator varchar(10))
RETURNS @rtnTable TABLE
(
ID int identity(1,1),
ColumnA varchar(max)
)
AS
BEGIN
DECLARE @position int = 0
DECLARE @endAt int = 0
DECLARE @tempString varchar(100)
set @column = ltrim(rtrim(@column))
WHILE @position<=len(@column)
BEGIN
set @endAt = CHARINDEX(@separator,@column,@position)
if(@endAt=0)
begin
Insert into @rtnTable(ColumnA) Select substring(@column,@position,len(@column)-@position)
break;
end
set @tempString = substring(ltrim(rtrim(@column)),@position,@endAt-@position)
Insert into @rtnTable(ColumnA) select @tempString
set @position=@endAt+1;
END
return
END
Использовать регистр
select * from dbo.SplitToRows('T14; p226.0001; eee; 3554;', ';')
Или просто выбрать с несколькими результирующими наборами
DECLARE @column varchar(max)= '1234; 4748;abcde; 324432'
DECLARE @separator varchar(10) = ';'
DECLARE @position int = 0
DECLARE @endAt int = 0
DECLARE @tempString varchar(100)
set @column = ltrim(rtrim(@column))
WHILE @position<=len(@column)
BEGIN
set @endAt = CHARINDEX(@separator,@column,@position)
if(@endAt=0)
begin
Select substring(@column,@position,len(@column)-@position)
break;
end
set @tempString = substring(ltrim(rtrim(@column)),@position,@endAt-@position)
select @tempString
set @position=@endAt+1;
END
Наконец, ожидание закончилось с SQL Server 2016. Они ввели функцию строки Split, STRING_SPLIT
:
select OtherID, cs.Value --SplitData
from yourtable
cross apply STRING_SPLIT (Data, ',') cs
Все остальные методы разделения строки например, XML, таблица Tally, while loop и т. д. были сдуты этой функцией STRING_SPLIT
.
Вот отличная статья с сопоставлением производительности: Сюрпризы производительности и предположения: STRING_SPLIT .
Для более старых версий использование таблицы tally table здесь - это одна функция разделения строк (наилучший возможный подход)
CREATE FUNCTION [dbo].[DelimitedSplit8K]
(@pString VARCHAR(8000), @pDelimiter CHAR(1))
RETURNS TABLE WITH SCHEMABINDING AS
RETURN
--===== "Inline" CTE Driven "Tally Table" produces values from 0 up to 10,000...
-- enough to cover NVARCHAR(4000)
WITH E1(N) AS (
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
), --10E+1 or 10 rows
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS (--==== This provides the "base" CTE and limits the number of rows right up front
-- for both a performance gain and prevention of accidental "overruns"
SELECT TOP (ISNULL(DATALENGTH(@pString),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
),
cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter)
SELECT 1 UNION ALL
SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(@pString,t.N,1) = @pDelimiter
),
cteLen(N1,L1) AS(--==== Return start and length (for use in substring)
SELECT s.N1,
ISNULL(NULLIF(CHARINDEX(@pDelimiter,@pString,s.N1),0)-s.N1,8000)
FROM cteStart s
)
--===== Do the actual split. The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found.
SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1),
Item = SUBSTRING(@pString, l.N1, l.L1)
FROM cteLen l
;
Ссылка на Tally OH! Улучшенная функция SQL 8K «CSV Splitter»
value
, а не SplitData
.
– Stewart
28 April 2017 в 16:12
;WITH tmp(SomeID, OtherID, DataItem, Data) as (
SELECT SomeID, OtherID, LEFT(Data, CHARINDEX(',',Data+',')-1),
STUFF(Data, 1, CHARINDEX(',',Data+','), '')
FROM Testdata
WHERE Data > ''
)
SELECT SomeID, OtherID, Data
FROM tmp
ORDER BY SomeID
с незначительной модификацией вышеприведенного запроса ...
При использовании этого подхода вы должны убедиться, что ни одно из ваших значений не содержит что-то, что было бы незаконным. XML-user1151923
blockquote>Я всегда использую метод XML. Убедитесь, что вы используете VALID XML. У меня есть две функции для преобразования между действительным XML и текстом. (Я, как правило, вырезаю каретки, как мне обычно не нужны.
CREATE FUNCTION dbo.udf_ConvertTextToXML (@Text varchar(MAX)) RETURNS varchar(MAX) AS BEGIN SET @Text = REPLACE(@Text,CHAR(10),'') SET @Text = REPLACE(@Text,CHAR(13),'') SET @Text = REPLACE(@Text,'<','<') SET @Text = REPLACE(@Text,'&','&') SET @Text = REPLACE(@Text,'>','>') SET @Text = REPLACE(@Text,'''',''') SET @Text = REPLACE(@Text,'"','"') RETURN @Text END CREATE FUNCTION dbo.udf_ConvertTextFromXML (@Text VARCHAR(MAX)) RETURNS VARCHAR(max) AS BEGIN SET @Text = REPLACE(@Text,'<','<') SET @Text = REPLACE(@Text,'&','&') SET @Text = REPLACE(@Text,'>','>') SET @Text = REPLACE(@Text,''','''') SET @Text = REPLACE(@Text,'"','"') RETURN @Text END
SELECT (SELECT '<&> blah' + CHAR(13)+CHAR(10) + 'next line' FOR XML PATH(''))
– Shnugo
19 July 2018 в 09:02
Data
сvarchar(max)
наvarchar(4000)
, например.create table Testdata(SomeID int, OtherID int, Data varchar(4000))
? – ca9163d9 22 February 2012 в 01:58OPTION (maxrecursion 0)
– RichardTheKiwi 14 January 2014 в 22:50