Проблемы с выражением TSQL IN - PullRequest
3 голосов
/ 11 февраля 2010

Из того, что я понял о выражении IN, это должно сработать:

DECLARE @list varchar(255)

SET @list = '''Item1'',''Item2'''

SELECT
   *
FROM
   Table
WHERE
   Item IN (@list)

И он должен выбрать эти элементы в @list. Элементы существуют в таблице. Если я выполню запрос отдельно для Item1 и Item2 (Item = Item1, затем Item = Item2), эти отдельные запросы будут работать. Только не с IN. Кто-нибудь может просветить меня? Представьте, что это просто, просто трудно найти полезную информацию по этой команде.

EDIT:

В настоящее время я делаю это с помощью динамических хранимых процедур, где я строю запрос в строке и выполняю. Например, эта процедура работает:

ALTER PROCEDURE [dbo].[TestSproc]
@list varchar(4000)
AS
BEGIN

DECLARE @sql varchar(4000)

SET @sql =
'
SELECT
COUNT(*)
FROM
    Items
WHERE
    Item IN (' + @list + ') '

EXEC (@sql)

Однако эта процедура не работает. Возвращает 0 строк. Если я запускаю его вручную для Item1, затем Item2, он возвращает оба значения, как и ожидалось:

ALTER PROCEDURE [dbo].[TestSproc]
    @list varchar(4000)
AS
BEGIN

SELECT
    COUNT(*)
FROM
    Items
WHERE
    Item IN (@list) 

Я использую одну и ту же команду для вызова обеих процедур:

EXEC    [dbo].[TestSproc]
    @list = N'''Item1'',''Item2'''

Я пытался подвести итог в своем первоначальном вопросе, но я думаю, что это могло отбросить людей с базы. Может быть, это поможет прояснить мою проблему (исключая любые глупые опечатки, которые я сделал).

Ответы [ 9 ]

3 голосов
/ 11 февраля 2010

Ключевое слово IN не работает со списком в строке.Он работает со списком значений, возвращаемых в запросе (или с дискретным набором значений, таких как ('Item1','Item2'), как упоминает DaveE в своем комментарии).Вы можете изменить @list следующим образом:

DECALRE @list TABLE (
    value varchar(MAX))

INSERT INTO @list (value)
VALUES ('Item1')

INSERT INTO @list (value)
VALUES ('Item2')

SELECT * FROM Table
WHERE Item IN (SELECT value FROM @list)

Другой вариант - создать UDF с табличным значением, который принимает список значений через запятую и возвращает таблицу.Я бы сказал против этого, хотя этот стиль UDF, как правило, плохой исполнитель.

2 голосов
/ 11 февраля 2010

Единственный способ заставить эту работу - использовать динамический sql, что является плохой идеей. Лучше взять значения и поместить их во временную таблицу и присоединиться к ней.

1 голос
/ 11 февраля 2010
0 голосов
/ 11 февраля 2010

это лучший источник:

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

создайте функцию разделения и используйте ее следующим образом:

SELECT
    *
    FROM YourTable  y
    INNER JOIN dbo.splitFunction(@Parameter) s ON y.ID=s.Value

Я предпочитаю подход таблицы чисел

Для того, чтобы этот метод работал, вам нужно выполнить настройку по одному расписанию:

SELECT TOP 10000 IDENTITY(int,1,1) AS Number
    INTO Numbers
    FROM sys.objects s1
    CROSS JOIN sys.objects s2
ALTER TABLE Numbers ADD CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number)

После настройки таблицы чисел создайте эту функцию:

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 TABLE
AS
RETURN 
(

    ----------------
    --SINGLE QUERY-- --this will not return empty rows
    ----------------
    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!=''

);
GO 

Теперь вы можете легко разбить строку CSV на таблицу и присоединиться к ней:

select * from dbo.FN_ListToTable(',','1,2,3,,,4,5,6777,,,')

ВЫВОД:

ListValue
-----------------------
1
2
3
4
5
6777

(6 row(s) affected)

Вы можете передать строку CSV в процедуру и обработать только строки с заданными идентификаторами:

SELECT
    y.*
    FROM YourTable y
        INNER JOIN dbo.FN_ListToTable(',',@GivenCSV) s ON y.ID=s.ListValue

Это будет использовать индекс по y.ID

0 голосов
/ 11 февраля 2010

Я использую пользовательскую табличную функцию, которую можно использовать повторно. Это пригодится ... особенно с параметрами хранимых процедур и SSRS:

CREATE FUNCTION [dbo].[fn_Split]
(
  @ItemList NVARCHAR(4000),
  @delimiter CHAR(1)
)
  RETURNS @itemtable TABLE (Item NVARCHAR(50) )

AS

BEGIN
IF @delimiter IS NULL
BEGIN 
set @delimiter = ','
END

DECLARE @tempItemList NVARCHAR(4000)
SET @tempItemList = @ItemList

DECLARE @i INT
DECLARE @Item NVARCHAR(4000)

SET @tempItemList = REPLACE (@tempItemList, @delimiter + ' ', @delimiter)
SET @i = CHARINDEX(@delimiter, @tempItemList)

WHILE (LEN(@tempItemList) > 0)
BEGIN
IF @i = 0
SET @Item = @tempItemList
ELSE
SET @Item = LEFT(@tempItemList, @i - 1)

INSERT INTO @itemtable(Item) VALUES(@Item)

IF @i = 0
SET @tempItemList = ''
ELSE
SET @tempItemList = RIGHT(@tempItemList, LEN(@tempItemList) - @i)

SET @i = CHARINDEX(@delimiter, @tempItemList)
END
RETURN
END

Тогда вы можете использовать его так:

DECLARE @list varchar(255)     

SET @list = '''Item1'',''Item2'''     

SELECT     *     
FROM     Table     
WHERE  Item IN (select * from dbo.fn_Split(@list,null))  //Right HERE 
0 голосов
/ 11 февраля 2010

НЕТ , я думаю, вы все неправильно поняли

Вы должны взглянуть на

Или даже

--Split
DECLARE @textXML XML
DECLARE @data NVARCHAR(MAX), 
        @delimiter NVARCHAR(5)

SELECT  @data = 'A,B,C',
        @delimiter = ','

SELECT    @textXML = CAST('<d>' + REPLACE(@data, @delimiter, '</d><d>') + '</d>' AS XML)
SELECT  T.split.value('.', 'nvarchar(max)') AS data
FROM    @textXML.nodes('/d') T(split)

Также взглянем на

0 голосов
/ 11 февраля 2010

Тогда вы собрались неправильно.

Выражение IN не понимает список значений в виде строки. Все, что он видит, это одна строка. Потому что это то, что вы передаете.

Вы должны передать фактический список значений, что означает одно отдельное выражение SQL на значение, разделенное реальными запятыми.

SELECT *
FROM   Table
WHERE  Item IN ('Item1', 'Item2')

Или вы можете сделать что-то в духе этого.

DECLARE @list TABLE (value varchar(50))
INSERT @list VALUES ('Item1')
INSERT @list VALUES ('Item2')

SELECT *
FROM   [Table] t INNER JOIN @list l ON l.value = t.item
0 голосов
/ 11 февраля 2010

@list, который вы используете, является объединенной строкой, которая обрабатывается как одно значение, которое должно совпадать дословно.

Используйте это вместо:

DECLARE @list TABLE (value VARCHAR(255))

INSERT
INTO    @list
VALUES  ('Item1')

INSERT
INTO    @list
VALUES  ('Item2')

SELECT  *
FROM    Table
WHERE   Item IN
        (
        SELECT  value
        FROM    @list
        )
0 голосов
/ 11 февраля 2010

Предложение in() ожидает дискретный набор значений, а не строку. Для этого вам нужно динамически создать весь SQL-запрос в виде строки и затем выполнить эту строку.

Попробуйте что-то вроде этого:

declare @list varchar(255);
set @list = '''Item1'',''Item2''';

declare @query = varchar(max);
set @query = 'SELECT
   *
FROM
   Table
WHERE
   Item IN (' + @list + ')';

exec(@query);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...