usar una separar separado por funcion delimitada consulta comas columnas charindex caracter cadena buscar sql sql-server tsql sql-in

separar - Pasar un varchar lleno de valores delimitados por comas a una función SQL Server IN



split sql (22)

Cree una función de tabla como la de abajo, que descompone en coma y separa varchar y devuelve una tabla que puede combinarse internamente con otras tablas.

CREATE FUNCTION [dbo].[fn_SplitList] ( @inString varchar(MAX) = '''', @inDelimiter char(1) = '','' -- Keep the delimiter to 100 chars or less. Generally a delimiter will be 1-2 chars only. ) RETURNS @tbl_Return table ( Unit varchar(1000) COLLATE Latin1_General_BIN ) AS BEGIN INSERT INTO @tbl_Return SELECT DISTINCT LTRIM(RTRIM(piece.value(''./text()[1]'', ''varchar(1000)''))) COLLATE DATABASE_DEFAULT AS Unit FROM ( -- -- Replace any delimiters in the string with the "X" tag. -- SELECT CAST((''<X>'' + REPLACE(s0.prsString, s0.prsSplitDelimit, ''</X><X>'') + ''</X>'') AS xml).query(''.'') AS units FROM ( -- -- Convert the string and delimiter into XML. -- SELECT (SELECT @inString FOR XML PATH('''')) AS prsString, (SELECT @inDelimiter FOR XML PATH('''')) AS prsSplitDelimit ) AS s0 ) AS s1 CROSS APPLY units.nodes(''X'') x(piece) RETURN END

============================================== Ahora consuma la función de tabla creada anteriormente en su código, la creación de funciones es una actividad única en su base de datos que se puede usar en todas las bases de datos y en el mismo servidor.

DECLARE @Ids varchar(50); SET @Ids = ''1,2,3,5,4,6,7,98,234''; SELECT * FROM sometable AS st INNER JOIN fn_SplitList(@ids, '','') AS sl ON sl.unit = st.tableid

Duplicado de
Consulta de valor delimitado por comas SQL dinámicas
Consultas parametrizadas con Like y In

Tengo un SQL Server Stored Procedure donde me gustaría pasar un varchar lleno de valores delimitados por comas a una función IN . Por ejemplo:

DECLARE @Ids varchar(50); SET @Ids = ''1,2,3,5,4,6,7,98,234''; SELECT * FROM sometable WHERE tableid IN (@Ids);

Esto no funciona, por supuesto. Me sale el error:

La conversión falló al convertir el valor varchar ''1,2,3,5,4,6,7,98,234'' al tipo de datos int.

¿Cómo puedo lograr esto (o algo relativamente similar) sin recurrir a la creación de SQL dinámico?


Creo que una solución muy simple podría ser siguiente:

DECLARE @Ids varchar(50); SET @Ids = ''1,2,3,5,4,6,7,98,234''; SELECT * FROM sometable WHERE '',''+@Ids+'','' LIKE ''%,''+CONVERT(VARCHAR(50),tableid)+'',%'';



Esto funciona perfectamente! Las respuestas a continuación son muy complicadas. No mires esto como dinámico. Configure su procedimiento de tienda de la siguiente manera:

(@id as varchar(50)) as Declare @query as nvarchar(max) set @query ='' select * from table where id in(''+@id+'')'' EXECUTE sp_executesql @query


Gracias, por su función, lo utilicé ........................ Este es mi EJEMPLO

**UPDATE [RD].[PurchaseOrderHeader] SET [DispatchCycleNumber] =''10'' WHERE OrderNumber in(select * FROM XA.fn_SplitOrderIDs(@InvoiceNumberList))** CREATE FUNCTION [XA].[fn_SplitOrderIDs] ( @OrderList varchar(500) ) RETURNS @ParsedList table ( OrderID int ) AS BEGIN DECLARE @OrderID varchar(10), @Pos int SET @OrderList = LTRIM(RTRIM(@OrderList))+ '','' SET @Pos = CHARINDEX('','', @OrderList, 1) IF REPLACE(@OrderList, '','', '''') <> '''' BEGIN WHILE @Pos > 0 BEGIN SET @OrderID = LTRIM(RTRIM(LEFT(@OrderList, @Pos - 1))) IF @OrderID <> '''' BEGIN INSERT INTO @ParsedList (OrderID) VALUES (CAST(@OrderID AS int)) --Use Appropriate conversion END SET @OrderList = RIGHT(@OrderList, LEN(@OrderList) - @Pos) SET @Pos = CHARINDEX('','', @OrderList, 1) END END RETURN END


Ha pasado un tiempo, pero he hecho esto en el pasado usando XML como medida provisional.

No puedo atribuir ningún crédito por esto, pero me temo que ya no sé de dónde saqué esta idea:

-- declare the variables needed DECLARE @xml as xml,@str as varchar(100),@delimiter as varchar(10) -- The string you want to split SET @str=''A,B,C,D,E,Bert,Ernie,1,2,3,4,5'' -- What you want to split on. Can be a single character or a string SET @delimiter ='','' -- Convert it to an XML document SET @xml = cast((''<X>''+replace(@str,@delimiter ,''</X><X>'')+''</X>'') as xml) -- Select back from the XML SELECT N.value(''.'', ''varchar(10)'') as value FROM @xml.nodes(''X'') as T(N)


He escrito un procedimiento almacenado para mostrar cómo hacerlo antes. Básicamente, debes procesar la cadena. Traté de publicar el código aquí, pero el formato se volvió complicado.

IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = object_id(N''[dbo].[uspSplitTextList]'') AND OBJECTPROPERTY(id, N''IsProcedure'') = 1) DROP PROCEDURE [dbo].[uspSplitTextList] GO SET QUOTED_IDENTIFIER ON GO SET ANSI_NULLS ON GO /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -- uspSplitTextList -- -- Description: -- splits a separated list of text items and returns the text items -- -- Arguments: -- @list_text - list of text items -- @Delimiter - delimiter -- -- Notes: -- 02/22/2006 - WSR : use DATALENGTH instead of LEN throughout because LEN doesn''t count trailing blanks -- -- History: -- 02/22/2006 - WSR : revised algorithm to account for items crossing 8000 character boundary -- 09/18/2006 - WSR : added to this project -- CREATE PROCEDURE uspSplitTextList @list_text text, @Delimiter varchar(3) AS SET NOCOUNT ON DECLARE @InputLen integer -- input text length DECLARE @TextPos integer -- current position within input text DECLARE @Chunk varchar(8000) -- chunk within input text DECLARE @ChunkPos integer -- current position within chunk DECLARE @DelimPos integer -- position of delimiter DECLARE @ChunkLen integer -- chunk length DECLARE @DelimLen integer -- delimiter length DECLARE @ItemBegPos integer -- item starting position in text DECLARE @ItemOrder integer -- item order in list DECLARE @DelimChar varchar(1) -- first character of delimiter (simple delimiter) -- create table to hold list items -- actually their positions because we may want to scrub this list eliminating bad entries before substring is applied CREATE TABLE #list_items ( item_order integer, item_begpos integer, item_endpos integer ) -- process list IF @list_text IS NOT NULL BEGIN -- initialize SET @InputLen = DATALENGTH(@list_text) SET @TextPos = 1 SET @DelimChar = SUBSTRING(@Delimiter, 1, 1) SET @DelimLen = DATALENGTH(@Delimiter) SET @ItemBegPos = 1 SET @ItemOrder = 1 SET @ChunkLen = 1 -- cycle through input processing chunks WHILE @TextPos <= @InputLen AND @ChunkLen <> 0 BEGIN -- get current chunk SET @Chunk = SUBSTRING(@list_text, @TextPos, 8000) -- setup initial variable values SET @ChunkPos = 1 SET @ChunkLen = DATALENGTH(@Chunk) SET @DelimPos = CHARINDEX(@DelimChar, @Chunk, @ChunkPos) -- loop over the chunk, until the last delimiter WHILE @ChunkPos <= @ChunkLen AND @DelimPos <> 0 BEGIN -- see if this is a full delimiter IF SUBSTRING(@list_text, (@TextPos + @DelimPos - 1), @DelimLen) = @Delimiter BEGIN -- insert position INSERT INTO #list_items (item_order, item_begpos, item_endpos) VALUES (@ItemOrder, @ItemBegPos, (@TextPos + @DelimPos - 1) - 1) -- adjust positions SET @ItemOrder = @ItemOrder + 1 SET @ItemBegPos = (@TextPos + @DelimPos - 1) + @DelimLen SET @ChunkPos = @DelimPos + @DelimLen END ELSE BEGIN -- adjust positions SET @ChunkPos = @DelimPos + 1 END -- find next delimiter SET @DelimPos = CHARINDEX(@DelimChar, @Chunk, @ChunkPos) END -- adjust positions SET @TextPos = @TextPos + @ChunkLen END -- handle last item IF @ItemBegPos <= @InputLen BEGIN -- insert position INSERT INTO #list_items (item_order, item_begpos, item_endpos) VALUES (@ItemOrder, @ItemBegPos, @InputLen) END -- delete the bad items DELETE FROM #list_items WHERE item_endpos < item_begpos -- return list items SELECT SUBSTRING(@list_text, item_begpos, (item_endpos - item_begpos + 1)) AS item_text, item_order, item_begpos, item_endpos FROM #list_items ORDER BY item_order END DROP TABLE #list_items RETURN /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ GO SET QUOTED_IDENTIFIER OFF GO SET ANSI_NULLS ON GO


La forma más sencilla que encontré fue usar FIND_IN_SET

FIND_IN_SET (column_name, values)

valores = (1,2,3)

SELECCIONE el nombre WHERE FIND_IN_SET (id, valores)


Mejor y simple enfoque.

DECLARE @AccumulateKeywordCopy NVARCHAR(2000),@IDDupCopy NVARCHAR(50); SET @AccumulateKeywordCopy =''''; SET @IDDupCopy =''''; SET @IDDup = (SELECT CONVERT(VARCHAR(MAX), <columnName>) FROM <tableName> WHERE <clause>) SET @AccumulateKeywordCopy = '',''+@AccumulateKeyword+'',''; SET @IDDupCopy = '',''+@IDDup +'',''; SET @IDDupCheck = CHARINDEX(@IDDupCopy,@AccumulateKeywordCopy)


Podrías hacerlo así:

create or replace PROCEDURE UDP_SETBOOKMARK ( P_USERID IN VARCHAR2 , P_BOOKMARK IN VARCHAR2 ) AS BEGIN UPDATE T_ER_Bewertung SET LESEZEICHEN = P_BOOKMARK WHERE STAMM_ID in( select regexp_substr(P_USERID,''[^,]+'', 1, level) from dual connect by regexp_substr(P_USERID, ''[^,]+'', 1, level) is not null ) and ER_ID = (select max(ER_ID) from T_ER_Bewertung_Kopie); commit; END UDP_SETBOOKMARK;

Entonces pruébalo con

Begin UDP_SETBOOKMARK (''1,2,3,4,5'', ''Test''); End;

Puedes usar esta cláusula IN con regexp_substr en otras situaciones también, solo inténtalo.


Por supuesto, si eres flojo como yo, podrías hacer esto:

Declare @Ids varchar(50) Set @Ids = '',1,2,3,5,4,6,7,98,234,'' Select * from sometable where Charindex('',''+cast(tableid as varchar(8000))+'','', @Ids) > 0


Prueba esto:

SELECT ProductId, Name, Tags FROM Product WHERE ''1,2,3,'' LIKE ''%'' + CAST(ProductId AS VARCHAR(20)) + '',%'';

Como se dijo en el último ejemplo de este enlace


Puede crear una función que devuelva una tabla.

por lo que su declaración sería algo así como

select * from someable join Splitfunction(@ids) as splits on sometable.id = splits.id

Aquí hay una función simular.

CREATE FUNCTION [dbo].[FUNC_SplitOrderIDs] ( @OrderList varchar(500) ) RETURNS @ParsedList table ( OrderID int ) AS BEGIN DECLARE @OrderID varchar(10), @Pos int SET @OrderList = LTRIM(RTRIM(@OrderList))+ '','' SET @Pos = CHARINDEX('','', @OrderList, 1) IF REPLACE(@OrderList, '','', '''') <> '''' BEGIN WHILE @Pos > 0 BEGIN SET @OrderID = LTRIM(RTRIM(LEFT(@OrderList, @Pos - 1))) IF @OrderID <> '''' BEGIN INSERT INTO @ParsedList (OrderID) VALUES (CAST(@OrderID AS int)) --Use Appropriate conversion END SET @OrderList = RIGHT(@OrderList, LEN(@OrderList) - @Pos) SET @Pos = CHARINDEX('','', @OrderList, 1) END END RETURN END


Puedo sugerir usar WITH así:

DECLARE @Delim char(1) = '',''; SET @Ids = @Ids + @Delim; WITH CTE(i, ls, id) AS ( SELECT 1, CHARINDEX(@Delim, @Ids, 1), SUBSTRING(@Ids, 1, CHARINDEX(@Delim, @Ids, 1) - 1) UNION ALL SELECT i + 1, CHARINDEX(@Delim, @Ids, ls + 1), SUBSTRING(@Ids, ls + 1, CHARINDEX(@Delim, @Ids, ls + 1) - CHARINDEX(@Delim, @Ids, ls) - 1) FROM CTE WHERE CHARINDEX(@Delim, @Ids, ls + 1) > 1 ) SELECT t.* FROM yourTable t INNER JOIN CTE c ON t.id = c.id;


Si usa SQL Server 2008 o una versión superior, use los parámetros de la tabla valorada; por ejemplo:

CREATE PROCEDURE [dbo].[GetAccounts](@accountIds nvarchar) AS BEGIN SELECT * FROM accountsTable WHERE accountId IN (select * from @accountIds) END CREATE TYPE intListTableType AS TABLE (n int NOT NULL) DECLARE @tvp intListTableType -- inserts each id to one row in the tvp table INSERT @tvp(n) VALUES (16509),(16685),(46173),(42925),(46167),(5511) EXEC GetAccounts @tvp


Sin usar SQL dinámico, debe tomar la variable de entrada y usar una función dividida para poner los datos en una tabla temporal y luego unirse a eso.


Tengo la misma idea con el usuario KM. pero no necesita Número de tabla adicional. Solo esta función.

CREATE FUNCTION [dbo].[FN_ListToTable] ( @SplitOn char(1) --REQUIRED, the character to split the @List string on ,@List varchar(8000) --REQUIRED, the list to split apart ) RETURNS @ParsedList table ( ListValue varchar(500) ) AS BEGIN DECLARE @number int = 0 DECLARE @childString varchar(502) = '''' DECLARE @lengthChildString int = 0 DECLARE @processString varchar(502) = @SplitOn + @List + @SplitOn WHILE @number < LEN(@processString) BEGIN SET @number = @number + 1 SET @lengthChildString = CHARINDEX(@SplitOn, @processString, @number + 1) - @number - 1 IF @lengthChildString > 0 BEGIN SET @childString = LTRIM(RTRIM(SUBSTRING(@processString, @number + 1, @lengthChildString))) IF @childString IS NOT NULL AND @childString != '''' BEGIN INSERT INTO @ParsedList(ListValue) VALUES (@childString) SET @number = @number + @lengthChildString - 1 END END END RETURN END

Y aquí está la prueba:

SELECT ListValue FROM dbo.FN_ListToTable(''/'',''a/////bb/c'')

Resultado:

ListValue ______________________ a bb c


¡No use una función que haga bucles para dividir una cadena! , mi función a continuación dividirá una cadena muy rápido, ¡sin bucle!

Antes de usar mi función, necesita configurar una tabla de "ayuda", solo necesita hacer esto una vez por base de datos:

CREATE TABLE Numbers (Number int NOT NULL, CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number ASC)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] DECLARE @x int SET @x=0 WHILE @x<8000 BEGIN SET @x=@x+1 INSERT INTO Numbers VALUES (@x) END

usa esta función para dividir tu cadena, que no se repite y es muy rápida:

CREATE FUNCTION [dbo].[FN_ListToTable] ( @SplitOn char(1) --REQUIRED, the character to split the @List string on ,@List varchar(8000) --REQUIRED, the list to split apart ) RETURNS @ParsedList table ( ListValue varchar(500) ) AS BEGIN /** Takes the given @List string and splits it apart based on the given @SplitOn character. A table is returned, one row per split item, with a column name "ListValue". This function workes for fixed or variable lenght items. Empty and null items will not be included in the results set. Returns a table, one row per item in the list, with a column name "ListValue" EXAMPLE: ---------- SELECT * FROM dbo.FN_ListToTable('','',''1,12,123,1234,54321,6,A,*,|||,,,,B'') returns: ListValue ----------- 1 12 123 1234 54321 6 A * ||| B (10 row(s) affected) **/ ---------------- --SINGLE QUERY-- --this will not return empty rows ---------------- INSERT INTO @ParsedList (ListValue) SELECT ListValue FROM (SELECT LTRIM(RTRIM(SUBSTRING(List2, number+1, CHARINDEX(@SplitOn, List2, number+1)-number - 1))) AS ListValue FROM ( SELECT @SplitOn + @List + @SplitOn AS List2 ) AS dt INNER JOIN Numbers n ON n.Number < LEN(dt.List2) WHERE SUBSTRING(List2, number, 1) = @SplitOn ) dt2 WHERE ListValue IS NOT NULL AND ListValue!='''' RETURN END --Function FN_ListToTable

puede usar esta función como una tabla en una unión:

SELECT Col1, COl2, Col3... FROM YourTable INNER JOIN FN_ListToTable('','',@YourString) s ON YourTable.ID = s.ListValue

Aquí está su ejemplo:

Select * from sometable where tableid in(SELECT ListValue FROM dbo.FN_ListToTable('','',@Ids) s)


Sin tabla Sin función Sin lazo

Partiendo de la idea de analizar su lista en una tabla, nuestro DBA sugirió usar XML.

Declare @Ids varchar(50) Set @Ids = ‘1,2,3,5,4,6,7,98,234’ DECLARE @XML XML SET @XML = CAST(''<i>'' + REPLACE(@Ids, '','', ''</i><i>'') + ''</i>'' AS XML) SELECT * FROM SomeTable INNER JOIN @XML.nodes(''i'') x(i) ON SomeTable .Id = x.i.value(''.'', ''VARCHAR(MAX)'')

Estos parecen tener el mismo rendimiento que la respuesta de @ KM pero, creo, mucho más simple.


-- select * from dbo.Split_ID(''77,106'') ALTER FUNCTION dbo.Split_ID(@String varchar(8000)) returns @temptable TABLE (ID varchar(8000)) as begin declare @idx int declare @slice varchar(8000) declare @Delimiter char(1) set @Delimiter ='','' select @idx = 1 if len(@String)<1 or @String is null return while @idx!= 0 begin set @idx = charindex(@Delimiter,@String) if @idx!=0 set @slice = left(@String,@idx - 1) else set @slice = @String if(len(@slice)>0) insert into @temptable(ID) values(@slice) set @String = right(@String,len(@String) - @idx) if len(@String) = 0 break end return end


CREATE TABLE t ( id INT, col1 VARCHAR(50) ) INSERT INTO t VALUES (1, ''param1'') INSERT INTO t VALUES (2, ''param2'') INSERT INTO t VALUES (3, ''param3'') INSERT INTO t VALUES (4, ''param4'') INSERT INTO t VALUES (5, ''param5'') DECLARE @params VARCHAR(100) SET @params = '',param1,param2,param3,'' SELECT * FROM t WHERE Charindex('','' + Cast(col1 AS VARCHAR(8000)) + '','', @params) > 0

violín que funciona aquí Fiddle


Error 493: The column ''i'' that was returned from the nodes() method cannot be used directly. It can only be used with one of the four XML data type methods, exist(), nodes(), query(), and value(), or in IS NULL and IS NOT NULL checks.

El error anterior se corrigió en SQL Server 2014 utilizando el siguiente fragmento

Declare @Ids varchar(50) Set @Ids = ''1,2,3,5,4,6,7,98,234'' DECLARE @XML XML SET @XML = CAST(''<i>'' + REPLACE(@Ids, '','', ''</i><i>'') + ''</i>'' AS XML) SELECT SomeTable.* FROM SomeTable cross apply @XML.nodes(''i'') x(i) where SomeTable .Id = x.i.value(''.'', ''VARCHAR(MAX)'')