Übergeben einen varchar voller kommagetrennte Werte in einem SQL Server in der Funktion
-
22-08-2019 - |
Frage
Duplizieren von
Dynamische SQL Comma Delimited Wertabfrage
parametrisierte Abfragen mit Like und In
Ich habe eine SQL Server gespeicherte Prozedur, wo Ich mag würde ein varchar
getrennte Werte voller Komma passieren zu einer IN
Funktion. Zum Beispiel:
DECLARE @Ids varchar(50);
SET @Ids = '1,2,3,5,4,6,7,98,234';
SELECT *
FROM sometable
WHERE tableid IN (@Ids);
Das funktioniert natürlich nicht. Ich erhalte die Fehlermeldung:
Fehler bei der Konvertierung, wenn die Varchar-Wert-Umwandlungs '1,2,3,5,4,6,7,98,234' in den Datentyp int.
Wie kann ich das erreichen (oder etwas relativ ähnlich) ohne den Aufbau dynamische SQL zurückgreifen?
Lösung
Verwenden Sie keine Funktion, die eine Zeichenfolge aufteilen Schleifen! , meine Funktion unten wird eine Zeichenfolge geteilt sehr schnell, ohne Looping!
Bevor Sie meine Funktion zu verwenden, benötigen Sie einen „Helfer“ Tisch einzurichten, müssen Sie nur pro Datenbank dieses ein Mal tun:
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
Mit dieser Funktion können Sie Ihren String aufgeteilt werden, die nicht in einer Schleife tut und ist sehr schnell:
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
Sie diese Funktion als Tabelle können in einem Join:
SELECT
Col1, COl2, Col3...
FROM YourTable
INNER JOIN FN_ListToTable(',',@YourString) s ON YourTable.ID = s.ListValue
Hier ist dein Beispiel:
Select * from sometable where tableid in(SELECT ListValue FROM dbo.FN_ListToTable(',',@Ids) s)
Andere Tipps
Natürlich, wenn Sie wie ich faul sind, können Sie nur dies tun:
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
Nein Tabelle keine Funktion keine Schleife
Aufbauend auf der Idee der Liste in eine Tabelle Parsen unsere DBA vorgeschlagen 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)')
Dies scheint die gleiche Leistung wie @ KM Antwort zu haben, aber, wie ich glaube, sehr viel einfacher.
Sie können eine Funktion erstellen, die eine Tabelle zurückgibt.
so Ihre Aussage wäre so etwas wie
seinselect * from someable
join Splitfunction(@ids) as splits on sometable.id = splits.id
Hier ist eine simular Funktion.
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
Es ist eine sehr häufig gestellte Frage. Canned Antwort, einige schöne Techniken:
Das funktioniert perfekt! Die folgenden Antworten sind zu kompliziert. Schaut nicht auf diese als dynamisch. Richten Sie Ihr Geschäft wie folgt vorgegangen:
(@id as varchar(50))
as
Declare @query as nvarchar(max)
set @query ='
select * from table
where id in('+@id+')'
EXECUTE sp_executesql @query
Ohne Verwendung dynamischer SQL, Sie haben die Eingangsgröße zu nehmen und eine Split-Funktion verwenden, um die Daten in eine temporäre Tabelle zu setzen und dann zu, dass zu verbinden.
ich vorschlagen kann WITH
wie folgt aus:
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;
Danke für Ihre Funktion habe ich es ........................ Das ist mein Beispiel
**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
Wenn Sie SQL Server 2008 oder höher verwenden, verwenden Tabellenwertparameter; zum Beispiel:
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
eine Tabellenfunktion wie Erstellen unter der parsen Komma getrennt varchar und gibt eine Tabelle, die mit anderen Tabellen inneren verbunden sein kann.
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
=============================================== == Jetzt oben erstellten Tabellenfunktion in Ihrem Code verbraucht, Erstellung von Funktion ist eine Zeitaktivität in der Datenbank, die auch auf demselben Server über Datenbanken verwendet werden kann.
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
Ich denke, eine sehr einfache Lösung folgende werden:
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)+',%';
Ich habe eine gespeicherte Prozedur geschrieben zu zeigen, wie dies vor zu tun. Sie haben grundsätzlich die Zeichenfolge zu verarbeiten. Ich habe versucht, den Code hier posten, aber die Formatierung haben alle verrückt.
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
Es ist schon eine Weile her, aber ich habe dies als Zwischen unter Verwendung von XML in der Vergangenheit getan hat.
ich nicht nehmen kann dies bei einem Kredit, aber ich fürchte, ich weiß nicht mehr, wo ich diese Idee kam aus:
-- 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)
Ich habe gleiche Idee mit Benutzer KM. aber brauchen nicht extra Tisch Nummer. Genau diese Funktion nur.
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
Und hier ist der Test:
SELECT ListValue FROM dbo.FN_ListToTable('/','a/////bb/c')
Ergebnis:
ListValue
______________________
a
bb
c
Versuchen Sie folgendes:
SELECT ProductId, Name, Tags
FROM Product
WHERE '1,2,3,' LIKE '%' + CAST(ProductId AS VARCHAR(20)) + ',%';
Man könnte es wie folgt tun:
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;
Dann versuchen Sie es mit
Begin
UDP_SETBOOKMARK ('1,2,3,4,5', 'Test');
End;
Sie können diese IN-Klausel mit REGEXP_SUBSTR in anderen Situationen verwenden, ist es nur versuchen.
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.
Die obige errorr wurde in SQL Server 2014 festgelegt durch folgende Schnipsel mit
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)')
Am besten und einfachen Ansatz.
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)
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
Arbeits Geige finden hier Fiddle
Der einfachste Weg, ich war gefunden FIND_IN_SET verwenden
FIND_IN_SET(column_name, values)
values=(1,2,3)
SELECT name WHERE FIND_IN_SET(id, values)