逗号分隔的查询结果,供SQL中的IN子句使用

时间:2014-01-21 15:06:41

标签: sql sql-server tsql

我希望返回一系列数值结果(即列的结果)并将这些逗号分开。因此,输出可以由另一个查询中的IN子句使用。因此,当表数据如下时,我正在寻找 10,20,30,40,50,60 作为输出:

ID   Value
1    10
2    20
3    30
4    40
5    50
6    60

最聪明的方法是什么?以下查询将以下列方式使用此输出字符串:

select * from Table where Value IN (10,20,30,40,50,60)

我尝试创建一个获取select输出的变量,但我只得到一个值。即

declare @Value_List varchar(799);
select @Value_List = Value from Table select @Value_List;

但是我只返回一个值,更不用说列表了。

理由是这是一个调试脚本,我们需要手动完成一系列检查,因此理想情况下要将查询分解为单个语句,以便于使用和清晰。

4 个答案:

答案 0 :(得分:8)

要做你想做的事,你需要使用动态SQL。但是你能更好地解释为什么你想这样做吗?看起来要简单得多:

SELECT * 
FROM Table 
WHERE Value IN (SELECT Value FROM YourTable WHERE ID <= 6)

答案 1 :(得分:6)

1)假设您的值列表没有像Lamak概述的更简单的方法解决(即您根本不需要列表),这里是一种方法使用动态SQL

-- Declare & get the list of values from a query.
DECLARE @values varchar(2000);
SET @values = '';
SELECT @values = @values + CAST(Value AS varchar(5)) + ','
FROM tbl_A; -- hopefully some WHERE criteria here to make this interesting

-- Trim the trailing comma.
SET @values = SUBSTRING(@values, 1, Len(@values) - 1)

---- DEBUG: Confirm the list of values.
--SELECT @values As 'Values'
--/*
--Values
-------------------------------------------------------------------------------
--10,20,30,40,50,60
--
--(1 row(s) affected)
--*/

-- Dynamically use the list of values in an IN clause.
DECLARE @sql as nvarchar(max);
SET @sql = 'SELECT Value FROM tbl_A WHERE Value IN (' + @values + ')';

EXEC sp_executesql @sql;
/*
Value
-----------
10
20
30
40
50
60

(6 row(s) affected)
*/

SQLBook.com更深入地解释了这种方法。

2) 然而,当你可以使用一个子查询来获取IN子句中的值列表时,子查询会更聪明 - 例如:

SELECT a.Value
FROM tbl_A a
WHERE
    a.Value IN
        (SELECT b.Value
         FROM tbl_B b
         /* hopefully some WHERE criteria here to make this interesting */);

3) 作为逗号分隔列表的更直接替代方案,请考虑Table-Valued Parameters。它们可以简单,易读,优雅(根据您对Lamak的问题评论) - 即聪明。

考虑一个存储过程,它接受ID的“列表”作为ID的TVP(即集合):

/*
--------------------------------------------------------------------------------------
    IntTableType for int TVPs (i.e. "the TVP")
--------------------------------------------------------------------------------------
*/

CREATE TYPE [dbo].[IntTableType] AS TABLE
(
    Value int
)
GO


/*
--------------------------------------------------------------------------------------
    Gets a set of Foos by ID. (i.e. "the stored proc")
--------------------------------------------------------------------------------------
*/

CREATE PROCEDURE [dbo].[uspGetFoos]
    @FooIdTable dbo.IntTableType readonly
AS
BEGIN
    SET NOCOUNT ON;

    SELECT f.ID, f.Column1, f.Column2 -- etcetera
    FROM dbo.Foo f
    WHERE f.ID IN (SELECT fi.Value FROM @FooIdTable);

    ---- or --
    --
    --SELECT f.ID, f.Column1, f.Column2 -- etcetera
    --FROM dbo.Foo f
    --JOIN @FooIdTable fi ON fi.Value = f.ID;
END
GO

然后,您可以在一个查询中获取ID的“列表”(即设置),并在您描述的另一个查询中使用它:

DECLARE @fooIds dbo.IntTableType;
INSERT INTO @fooIds (Value)
SELECT IntColumn
FROM dbo.Whatever
WHERE 1 = 1; -- whatever

EXEC dbo.uspGetFoos @FooIdTable = @fooIds;

当然,您也可以类似地使用表变量或临时表来表示ID(前者仅限于单个存储过程,函数或批处理);但TVP为您提供了一种将表格(即设置)数据作为输入参数的一流方法。

答案 2 :(得分:0)

假设您的分隔值不像您的示例那样是线性的,您可以创建一个从分隔字符串返回表的函数。我复制了这个功能 here

CREATE FUNCTION [dbo].[func_Split] 
    (   
    @DelimitedString    varchar(8000),
    @Delimiter              varchar(100) 
    )
RETURNS @tblArray TABLE
    (
    ElementID   int IDENTITY(1,1),  -- Array index
    Element     varchar(1000)               -- Array element contents
    )
AS
BEGIN

    -- Local Variable Declarations
    -- ---------------------------
    DECLARE @Index      smallint,
                    @Start      smallint,
                    @DelSize    smallint

    SET @DelSize = LEN(@Delimiter)

    -- Loop through source string and add elements to destination table array
    -- ----------------------------------------------------------------------
    WHILE LEN(@DelimitedString) > 0
    BEGIN

        SET @Index = CHARINDEX(@Delimiter, @DelimitedString)

        IF @Index = 0
            BEGIN

                INSERT INTO
                    @tblArray 
                    (Element)
                VALUES
                    (LTRIM(RTRIM(@DelimitedString)))

                BREAK
            END
        ELSE
            BEGIN

                INSERT INTO
                    @tblArray 
                    (Element)
                VALUES
                    (LTRIM(RTRIM(SUBSTRING(@DelimitedString, 1,@Index - 1))))

                SET @Start = @Index + @DelSize
                SET @DelimitedString = SUBSTRING(@DelimitedString, @Start , LEN(@DelimitedString) - @Start + 1)

            END
    END

    RETURN
END

你的选择将是

select * 
from Table 
where Value IN (select Element from func_split('10,20,30,40,50,60', ','))

DEMO

答案 3 :(得分:-1)

这应该连接

DECLARE @result VARCHAR(MAX)

SELECT @result = COALESCE(@result+' ,','')+Value FROM Table

SELECT @result