Sto cercando di dividere '1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 ...' (virgola delimitata) in un tavolo o variabile di tabella.Equivalente della funzione di divisione in T-SQL?
Qualcuno ha una funzione che restituisce uno di seguito?
Sto cercando di dividere '1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 ...' (virgola delimitata) in un tavolo o variabile di tabella.Equivalente della funzione di divisione in T-SQL?
Qualcuno ha una funzione che restituisce uno di seguito?
Qui è un po 'soluzione antiquata:
/*
Splits string into parts delimitered with specified character.
*/
CREATE FUNCTION [dbo].[SDF_SplitString]
(
@sString nvarchar(2048),
@cDelimiter nchar(1)
)
RETURNS @tParts TABLE (part nvarchar(2048))
AS
BEGIN
if @sString is null return
declare @iStart int,
@iPos int
if substring(@sString, 1, 1) = @cDelimiter
begin
set @iStart = 2
insert into @tParts
values(null)
end
else
set @iStart = 1
while 1=1
begin
set @iPos = charindex(@cDelimiter, @sString, @iStart)
if @iPos = 0
set @iPos = len(@sString)+1
if @iPos - @iStart > 0
insert into @tParts
values (substring(@sString, @iStart, @[email protected]))
else
insert into @tParts
values(null)
set @iStart = @iPos+1
if @iStart > len(@sString)
break
end
RETURN
END
In SQL Server 2008 è possibile ottenere lo stesso con il codice di .NET. Forse funzionerebbe più velocemente, ma sicuramente questo approccio è più facile da gestire.
Grazie, vorrei anche sapere. C'è un errore qui? Ho scritto questo codice forse 6 anni fa e funzionava bene da quando. – XOR
Sono d'accordo. Questa è un'ottima soluzione quando non si vuole (o semplicemente non si può) partecipare alla creazione dei parametri del tipo di tabella, come nel mio esempio. Gli amministratori di database hanno bloccato questa funzione e non lo consentono. Grazie XOR! – dscarr
http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=50648
Una selezione di metodi diversi
sono tentato di spremere in mia soluzione preferita. La tabella risultante sarà composta da 2 colonne: PosIdx per la posizione del numero intero trovato; e valore in numero intero.
create function FnSplitToTableInt
(
@param nvarchar(4000)
)
returns table as
return
with Numbers(Number) as
(
select 1
union all
select Number + 1 from Numbers where Number < 4000
),
Found as
(
select
Number as PosIdx,
convert(int, ltrim(rtrim(convert(nvarchar(4000),
substring(@param, Number,
charindex(N',' collate Latin1_General_BIN,
@param + N',', Number) - Number))))) as Value
from
Numbers
where
Number <= len(@param)
and substring(N',' + @param, Number, 1) = N',' collate Latin1_General_BIN
)
select
PosIdx,
case when isnumeric(Value) = 1
then convert(int, Value)
else convert(int, null) end as Value
from
Found
Funziona utilizzando CTE ricorsiva come elenco di posizioni, da 1 a 100 per impostazione predefinita. Se avete bisogno di lavorare con la stringa di più di 100, è sufficiente chiamare questa funzione con 'opzione (MAXRECURSION 4000)' come la seguente:
select * from FnSplitToTableInt
(
'9, 8, 7, 6, 5, 4, 3, 2, 1, 0, ' +
'9, 8, 7, 6, 5, 4, 3, 2, 1, 0, ' +
'9, 8, 7, 6, 5, 4, 3, 2, 1, 0, ' +
'9, 8, 7, 6, 5, 4, 3, 2, 1, 0, ' +
'9, 8, 7, 6, 5, 4, 3, 2, 1, 0'
)
option (maxrecursion 4000)
+1 per aver menzionato l'opzione maxrecursion. Ovviamente la ricorsione pesante dovrebbe essere utilizzata con attenzione in un ambiente di produzione, ma è ottimo per l'utilizzo di CTE per eseguire pesanti attività di importazione o conversione dei dati. –
Prova questo
DECLARE @xml xml, @str varchar(100), @delimiter varchar(10)
SET @str = '1,2,3,4,5,6,7,8,9,10,11,12,13,14,15'
SET @delimiter = ','
SET @xml = cast(('<X>'+replace(@str, @delimiter, '</X><X>')+'</X>') as xml)
SELECT C.value('.', 'varchar(10)') as value FROM @xml.nodes('X') as X(C)
O
DECLARE @str varchar(100), @delimiter varchar(10)
SET @str = '1,2,3,4,5,6,7,8,9,10,11,12,13,14,15'
SET @delimiter = ','
;WITH cte AS
(
SELECT 0 a, 1 b
UNION ALL
SELECT b, CHARINDEX(@delimiter, @str, b) + LEN(@delimiter)
FROM CTE
WHERE b > a
)
SELECT SUBSTRING(@str, a,
CASE WHEN b > LEN(@delimiter)
THEN b - a - LEN(@delimiter)
ELSE LEN(@str) - a + 1 END) value
FROM cte WHERE a > 0
Qui ci sono molti altri modi per fare lo stesso How to split comma delimited string?
Nota per chiunque cerchi uno splitter di stringa generale: la prima soluzione fornita qui non è uno splitter di stringa generale: è sicuro solo se si è sicuri che l'input non contenga mai '<', '>' o '&' (ad esempio, input è una sequenza di numeri interi). Uno qualsiasi dei tre caratteri precedenti causerà un errore di analisi invece del risultato previsto. – miroxlav
Evento con i problemi menzionati da miroxlav (che dovrebbe essere risolvibile con qualche pensiero), questa sicuramente una delle soluzioni più creative che ho trovato (la prima)! Molto bella! –
La riga 'SELECT b, CHARINDEX (@delimiter, @str, b) + LEN (@delimiter)' dovrebbe essere effettivamente 'SELECT b, CHARINDEX (@delimiter, @str, b + 1) + LEN (@delimiter)' . Il ** b + 1 ** fa una grande differenza. Testato qui con lo spazio come delimitatore, non ha funzionato senza questa correzione. – JwJosefy
Questo è molto simile a .NET, per quelli di voi che hanno familiarità con questa funzione:
CREATE FUNCTION dbo.[String.Split]
(
@Text VARCHAR(MAX),
@Delimiter VARCHAR(100),
@Index INT
)
RETURNS VARCHAR(MAX)
AS BEGIN
DECLARE @A TABLE (ID INT IDENTITY, V VARCHAR(MAX));
DECLARE @R VARCHAR(MAX);
WITH CTE AS
(
SELECT 0 A, 1 B
UNION ALL
SELECT B, CONVERT(INT,CHARINDEX(@Delimiter, @Text, B) + LEN(@Delimiter))
FROM CTE
WHERE B > A
)
INSERT @A(V)
SELECT SUBSTRING(@Text,A,CASE WHEN B > LEN(@Delimiter) THEN B-A-LEN(@Delimiter) ELSE LEN(@Text) - A + 1 END) VALUE
FROM CTE WHERE A >0
SELECT @R
= V
FROM @A
WHERE ID = @Index + 1
RETURN @R
END
SELECT dbo.[String.Split]('121,2,3,0',',',1) -- gives '2'
Questo ha funzionato per me dopo aver aggiunto GO dopo END – clairestreb
This blog è venuto con una buona soluzione che utilizza XML in T-SQL.
Questa è la funzione che mi è venuta in base a quel blog (nome funzione di cambio e il risultato cast di tipo per ogni esigenza):
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[SplitIntoBigints]
(@List varchar(MAX), @Splitter char)
RETURNS TABLE
AS
RETURN
(
WITH SplittedXML AS(
SELECT CAST('<v>' + REPLACE(@List, @Splitter, '</v><v>') + '</v>' AS XML) AS Splitted
)
SELECT x.v.value('.', 'bigint') AS Value
FROM SplittedXML
CROSS APPLY Splitted.nodes('//v') x(v)
)
GO
qui è la funzione split che u chiesto
CREATE FUNCTION [dbo].[split](
@delimited NVARCHAR(MAX),
@delimiter NVARCHAR(100)
) RETURNS @t TABLE (id INT IDENTITY(1,1), val NVARCHAR(MAX))
AS
BEGIN
DECLARE @xml XML
SET @xml = N'<t>' + REPLACE(@delimited,@delimiter,'</t><t>') + '</t>'
INSERT INTO @t(val)
SELECT r.value('.','varchar(MAX)') as item
FROM @xml.nodes('/t') as records(r)
RETURN
END
eseguire la funzione in questo
select * from dbo.split('1,2,3,4,5,6,7,8,9,10,11,12,13,14,15',',')
Così semplice, ha funzionato come un fascino –
CREATE Function [dbo].[CsvToInt] (@Array varchar(4000))
returns @IntTable table
(IntValue int)
AS
begin
declare @separator char(1)
set @separator = ','
declare @separator_position int
declare @array_value varchar(4000)
set @array = @array + ','
while patindex('%,%' , @array) <> 0
begin
select @separator_position = patindex('%,%' , @array)
select @array_value = left(@array, @separator_position - 1)
Insert @IntTable
Values (Cast(@array_value as int))
select @array = stuff(@array, 1, @separator_position, '')
end
Questa è un'altra versione che in realtà non ha alcuna restrizione (es: caratteri speciali quando si utilizza l'approccio xml, numero di record nell'approccio CTE) e viene eseguita molto più velocemente in base a un test su record 10M + con lunghezza della stringa di origine media di 4000. Speranza questo potrebbe aiutare.
Create function [dbo].[udf_split] (
@ListString nvarchar(max),
@Delimiter nvarchar(1000),
@IncludeEmpty bit)
Returns @ListTable TABLE (ID int, ListValue nvarchar(1000))
AS
BEGIN
Declare @CurrentPosition int, @NextPosition int, @Item nvarchar(max), @ID int, @L int
Select @ID = 1,
@L = len(replace(@Delimiter,' ','^')),
@ListString = @ListString + @Delimiter,
@CurrentPosition = 1
Select @NextPosition = Charindex(@Delimiter, @ListString, @CurrentPosition)
While @NextPosition > 0 Begin
Set @Item = LTRIM(RTRIM(SUBSTRING(@ListString, @CurrentPosition, @[email protected])))
If @IncludeEmpty=1 or LEN(@Item)>0 Begin
Insert Into @ListTable (ID, ListValue) Values (@ID, @Item)
Set @ID = @ID+1
End
Set @CurrentPosition = @[email protected]
Set @NextPosition = Charindex(@Delimiter, @ListString, @CurrentPosition)
End
RETURN
END
/* *Object: UserDefinedFunction [dbo].[Split] Script Date: 10/04/2013 18:18:38* */
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER FUNCTION [dbo].[Split]
(@List varchar(8000),@SplitOn Nvarchar(5))
RETURNS @RtnValue table
(Id int identity(1,1),Value nvarchar(100))
AS
BEGIN
Set @List = Replace(@List,'''','')
While (Charindex(@SplitOn,@List)>0)
Begin
Insert Into @RtnValue (value)
Select
Value = ltrim(rtrim(Substring(@List,1,Charindex(@SplitOn,@List)-1)))
Set @List = Substring(@List,Charindex(@SplitOn,@List)+len(@SplitOn),len(@List))
End
Insert Into @RtnValue (Value)
Select Value = ltrim(rtrim(@List))
Return
END
go
Select *
From [Clv].[Split] ('1,2,3,3,3,3,',',')
GO
DECLARE
@InputString NVARCHAR(MAX) = 'token1,token2,token3,token4,token5'
, @delimiter varchar(10) = ','
DECLARE @xml AS XML = CAST(('<X>'+REPLACE(@InputString,@delimiter ,'</X><X>')+'</X>') AS XML)
SELECT C.value('.', 'varchar(10)') AS value
FROM @xml.nodes('X') as X(C)
Fonte di questa risposta: http://sqlhint.com/sqlserver/how-to/best-split-function-tsql-delimited
Mentre questo può teoricamente rispondere alla domanda, [sarebbe preferibile] (http://meta.stackexchange.com/q/8259) per includere qui le parti essenziali della risposta e fornire il link per riferimento. –
@Xavi: ok, ho incluso le parti essenziali della risposta. Grazie per il tuo suggerimento. –
Lei scrive questa funzione in SQL Server, dopo che il problema sarà risolto.
http://csharpdotnetsol.blogspot.in/2013/12/csv-function-in-sql-server-for-divide.html
Non copiare solo i collegamenti ... Non è un segno di buone domande ... Devi spiegare in dettaglio –
CREATE FUNCTION Split
(
@delimited nvarchar(max),
@delimiter nvarchar(100)
) RETURNS @t TABLE
(
-- Id column can be commented out, not required for sql splitting string
id int identity(1,1), -- I use this column for numbering splitted parts
val nvarchar(max)
)
AS
BEGIN
declare @xml xml
set @xml = N'<root><r>' + replace(@delimited,@delimiter,'</r><r>') + '</r></root>'
insert into @t(val)
select
r.value('.','varchar(max)') as item
from @xml.nodes('//root/r') as records(r)
RETURN
END
GO
utilizzo
Select * from dbo.Split(N'1,2,3,4,6',',')
Hai codificato questo SQL Server 2008, ma futuri visitatori a questa domanda (utilizzando SQL Server 2016+) sarà probabilmente desiderare di conoscere STRING_SPLIT
.
Con questa nuova funzione built-in è possibile ora solo usare
SELECT TRY_CAST(value AS INT)
FROM STRING_SPLIT ('1,2,3,4,5,6,7,8,9,10,11,12,13,14,15', ',')
Alcune restrizioni di questa funzione e alcuni risultati promettenti del test delle prestazioni è in this blog post by Aaron Bertrand.
Utilizzando tavolo bottino qui è funzione di stringa uno split (miglior approccio possibile) di Jeff Moden
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
;
Riferito da Tally OH! An Improved SQL 8K “CSV Splitter” Function
Recentemente ho eseguito uno studio minore confronto tra gli approcci più comuni questo problema, che può valere una lettura: http://www.sqlperformance.com/2012/07/t-sql-queries/split-strings e http://www.sqlperformance.com/2012/08/t- sql-queries/splitting-string-follow-up –
possibile duplicato o f [Split string in SQL] (http://stackoverflow.com/questions/2647/split-string-in-sql) – Luv
Erland Sommarskog ha mantenuto la risposta autorevole a questa domanda negli ultimi 12 anni: [http://www.sommarskog.se/arrays-in-sql.html](http://www.sommarskog.se/arrays-in-sql.html) Non vale la pena di riprodurre tutte le opzioni qui su StackOverflow, basta visitare la sua pagina e imparerai tutto quello che avresti voluto sapere. – Portman