質問

の複製
動的 SQL カンマ区切り値クエリ
Like および In を使用したパラメータ化されたクエリ

SQL Server ストアド プロシージャがあり、そこに varchar カンマ区切りの値がいっぱい IN 関数。例えば:

DECLARE @Ids varchar(50);
SET @Ids = '1,2,3,5,4,6,7,98,234';

SELECT * 
FROM sometable 
WHERE tableid IN (@Ids);

もちろんこれは機能しません。エラーが発生します:

varchar 値 '1,2,3,5,4,6,7,98,234' をデータ型 int に変換するときに変換が失敗しました。

動的 SQL の構築に頼らずにこれ (または比較的類似したこと) を実現するにはどうすればよいでしょうか?

役に立ちましたか?

解決

文字列を分割するためにループ機能を使用しないでください!

、以下の私の機能はありませんループと非常に高速な文字列を、分割します!

あなたが私の機能を使用する前に、あなたは「ヘルパー」テーブルを設定する必要があり、あなただけのデータベースごとに、この1回実行する必要があります:

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

ループをしませんし、非常に高速である、あなたの文字列を、分割するために、この機能を使用します:

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

あなたが参加中のテーブルとして、この機能を使用することができます:

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

ここではあなたの例です。

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

他のヒント

あなたは私のような怠け者なら、あなただけのこれを行うことができますもちろんます:

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

テーブルなし 関数なし ループなし

DBA が 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)')

これらは@KMの答えと同じパフォーマンスを持っているようですが、はるかに単純だと思います。

あなたは、テーブルを返す関数を作成することができます。

ので、あなたの文は、

のようなものになるだろう
select * from someable 
 join Splitfunction(@ids) as splits on sometable.id = splits.id

ここでまねた関数です。

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

これは非常に一般的な質問です。缶詰答え、いくつかの素敵なテクニックます:

http://www.sommarskog.se/arrays-in-sql- 2005.htmlする

これは完璧に動作します!以下の答えはあまりにも複雑です。このようなダイナミックを見てはいけません。次のようにお店の手続きを設定します。

(@id as varchar(50))
as

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

の動的SQLを使用しなければ、入力変数を取得し、一時テーブルにデータを配置し、それに参加するために、スプリット機能を使用する必要があります。

私はこのようなWITHを使用して提案することができます:

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;

おかげで、私はそれを使用して、あなたの機能のため........................ これは私の例です。

**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
あなたは、SQL Server 2008またはそれ以降を使用する場合は、

、テーブル値パラメータを使用します。例えばます:

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

コンマVARCHARを分離し、他のテーブルと結合内であることができるテーブルを返す解析その下のような表関数を作成します。

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

=============================================== == 今、あなたのコードで作成した表関数の上に消費し、関数の作成は、同じサーバー上のほかのデータベースにまたがって使用することができ、データベース内の1つの時間の活動である。

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

私は非常に簡単な解決策は、以下のことができると思います:

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)+',%';

私は前にこれを行う方法を示すために、ストアドプロシージャを書きました。 あなたは基本的に文字列を処理する必要があります。 私はここにコードを投稿しようとしましたが、フォーマットはすべて変になっています。

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

そのしばらくして私は暫定としてXMLを使用して、過去にこれを行っています。

私はこのために任意の信用を取ることはできませんが、私は、私がこのアイデアを得たところ、私はもはや知って怖います:

-- 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)

私は、ユーザーKMと同じ考えを持っています。しかし、余分なテーブル番号は必要ありません。ただ、この機能だけます。

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

そして、ここではテストです。

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

結果:

   ListValue
______________________
   a
   bb
   c

これを試して:

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

最後の例で述べたように、 このリンク

-- 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 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;

その後でそれを試してみてください。

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

あなたはそれを試してみてください、あまりにも他の状況でREGEXP_SUBSTRこのIN-句を使用することができます。

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.

上記errorrは、次のコードを使用して、SQL Server 2014年に固定された

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)')

ベストとシンプルなアプローチます。

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 

ここで見つけるフィドル作業フィドルする

私が見つけた最も簡単な方法は、FIND_IN_SET

を使用していました
FIND_IN_SET(column_name, values)

values=(1,2,3)

SELECT name WHERE FIND_IN_SET(id, values)
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top