Проблема синтаксиса SQL с использованием ключевого слова IN с динамическими данными - PullRequest
3 голосов
/ 16 декабря 2010

Я использую SQL Server 2000 и имею странную проблему с блоком кода SQL (ниже):
Пользователь может ввести «GM» в качестве возможного параметра или «F». Если пользователь вводит «GM» в качестве параметра в сохраненной строке запроса процедуры, мне нужно, чтобы AutoDivision включал GMC, CAD, CHE, SAT

declare @AutoDivision as varchar(15) 
set @AutoDivision = 'GM' 

if @AutoDivision = 'GM' 
            Begin 
                Select @AutoDivision = '''Cad'', ''GMC'', ''Sat'', ''Che'''
            End 

            SELECT 
                 oh.OrderNumber, lg.[lgh_number]
            FROM 
                [dbo].[OrderHeader] oh (NOLOCK)
            INNER JOIN 
                [dbo].[DistrctHeader] lg (NOLOCK) ON oh.[inv_number] = lg.[inv_number]
            INNER JOIN
                [dbo].[DealerCompany] c (NOLOCK) ON c.cmp_id = LEFT(oh.[ordernumber],3)
            INNER JOIN
                [dbo].[divisionXREF] x (NOLOCK) ON x.Division = c.comp_revtype
            WHERE
                oh.ord_number = '113-889257'
                AND x.AutoDivision IN (@AutoDivision)
                --AND x.AutoDivision IN ('Cad', 'Sat', 'GMC', 'Che')
                AND lg.[lgh_outstatus] IN ('AVAIL', 'PLAN', 'DISP', 'STRTD', 'PEND','COMP')  

Однако, когда я запускаю приведенный ниже код, я не получаю никаких записей. Когда я раскомментирую строку кода
--AND x.AutoDivision IN ('Cad', 'Sat', 'GMC', 'Che')

это работает (мне возвращают запись).

Когда я делаю print 'AND x.AutoDivision IN (' + cast(@AutoDivision as varchar) + ')'
Я вернусь AND x.AutoDivision IN ('Cad', 'GMC', 'Sat', 'Che')

Что мне здесь не хватает?

Ответы [ 5 ]

10 голосов
/ 16 декабря 2010

Нельзя использовать одну переменную для представления списка разделенных запятыми параметров IN в SQL - Oracle, MySQL, SQL Server ... не имеет значения.

Чтобы этот метод переменной работал, вам нужно использовать динамический SQL, чтобы вы могли сначала создать запрос в виде строки (используя конкатенацию для получения параметров IN из вашей переменной), а затем выполнить запрос:

DECLARE @cmd VARCHAR(1000)

SET @cmd = 'SELECT oh.OrderNumber, 
                   lg.[lgh_number]
              FROM [dbo].[OrderHeader] oh (NOLOCK)
              JOIN [dbo].[DistrctHeader] lg (NOLOCK) ON oh.[inv_number] = lg.[inv_number]
              JOIN [dbo].[DealerCompany] c (NOLOCK) ON c.cmp_id = LEFT(oh.[ordernumber],3)
              JOIN [dbo].[divisionXREF] x (NOLOCK) ON x.Division = c.comp_revtype
             WHERE oh.ord_number = '113-889257'
               AND x.AutoDivision IN ('+ @AutoDivision +')
               AND lg.[lgh_outstatus] IN (''AVL'', ''PLN'', ''DSP'', ''STD'', ''PND'',''CMP'')  '

EXEC(@cmd)

Я рекомендую прочитать Проклятие и благословения динамического SQL перед внедрением решения динамического SQL.

Таблица значений функции

Табличная функция позволит вам делать то, что вы хотите, без использования динамического SQL - подробнее в этой статье .

5 голосов
/ 16 декабря 2010

Чтобы избежать этого, вы можете создать временную таблицу, заполнить ее, а затем использовать

IN (SELECT myField from #myTable)
3 голосов
/ 16 декабря 2010

Даже если вы объединяете то, что выглядит как несколько разных аргументов, ваше предложение IN фактически проверяет его как одну строку (вы объявили его как varchar), и неудивительно, что ни одна запись не соответствует этому предикату.

Похоже, вы пытаетесь смешать динамический SQL и стандартный запрос. Это не сработает. Ваш запрос должен быть либо динамически создан, а затем специально выполнен, либо предложение IN должно содержать отдельные аргументы, которые могут быть выполнены подзапросом.

Вы можете сделать что-то вроде:

... AND x.AutoDivision IN 
(SELECT Division WHERE Corp = 'GM') ...

OR

... AND x.AutoDivision IN 
(SELECT 'Cad' UNION SELECT 'GMC' UNION SELECT 'Sat' UNION SELECT 'Che') ...

Попробуйте это:

        SELECT 
             oh.OrderNumber, lg.[lgh_number]
        FROM 
            [dbo].[OrderHeader] oh (NOLOCK)
        INNER JOIN 
            [dbo].[DistrctHeader] lg (NOLOCK) ON oh.[inv_number] = lg.[inv_number]
        INNER JOIN
            [dbo].[DealerCompany] c (NOLOCK) ON c.cmp_id = LEFT(oh.[ordernumber],3)
        INNER JOIN
            [dbo].[divisionXREF] x (NOLOCK) ON x.Division = c.comp_revtype
        WHERE
            oh.ord_number = '113-889257'
            AND x.AutoDivision IN 
           (SELECT 'Cad' UNION SELECT 'GMC' UNION SELECT 'Sat' UNION SELECT 'Che')
            AND lg.[lgh_outstatus] IN ('AVL', 'PLN', 'DSP', 'STD', 'PND','CMP') 

Это встроенный подзапрос, объединяющий произвольные строки в результирующий набор. (Возьмите с собой немного соли. Я далек от интерфейса Sql Server.)

1 голос
/ 16 декабря 2010
   AND x.AutoDivision IN (@AutoDivision)
        --AND x.AutoDivision IN ('Cad', 'Sat', 'GMC', 'Che')

Можете ли вы уточнить эти две строки кода, они не делают то же самое?

0 голосов
/ 17 сентября 2014
CREATE FUNCTION [dbo].[Split]
(
    @String NVARCHAR(4000),
    @Delimiter NCHAR(1)
)
RETURNS TABLE 
AS
RETURN 
(
    WITH Split(stpos,endpos) 
    AS(
        SELECT 0 AS stpos, CHARINDEX(@Delimiter,@String) AS endpos
        UNION ALL
        SELECT endpos+1, CHARINDEX(@Delimiter,@String,endpos+1)
            FROM Split
            WHERE endpos > 0
    )
    SELECT 'Id' = ROW_NUMBER() OVER (ORDER BY (SELECT 1)),
        'Data' = SUBSTRING(@String,stpos,COALESCE(NULLIF(endpos,0),LEN(@String)+1)-stpos)
    FROM Split
)
GO

DECLARE @AutoDivision varchar(50) = NULL
SET @AutoDivision ='Cad, Sat, GMC, Che'

SELECT * FROM divisionXREF P INNER JOIN dbo.SPLIT(ISNULL(@AutoDivision,''),',') as T ON P.lgh_outstatus = CASE T.strval WHEN '' THEN P.PG_CODE ELSE T.strval END
...