从输入列表中查找表中不存在的ID

时间:2012-04-23 06:52:12

标签: c# .net sql-server

我有一个带有id列表的变量(例如:121,131,142,145)。其中,我想查找一个表中不存在的id列表。此查询的输出应该是不存在的id列表。如何使用单个msssql查询实现。如果id 142和145不存在于表中,则查询应返回这两个值。是否可以使用IN子句

2 个答案:

答案 0 :(得分:3)

你需要一个分隔字符串到表转换udf,如下所示:

CREATE FUNCTION [dbo].[fnc_List2Table] (
    @list      varchar(8000),
    @delimiter varchar(16) = ','
) RETURNS @result TABLE (
    ListIndex int,
    Value varchar(8000)
)
AS
BEGIN
    SET @list = LTRIM(RTRIM(ISNULL(@list, SPACE(0))))
    DECLARE @n int, @i int, @delimiterIndex int, @nextItem varchar(8000)
    SET @n = LEN('#' + @delimiter + '#') - 2
    SET @i = 0
    WHILE @list <> SPACE(0)
    BEGIN
        SET @i = @i + 1
        SET @delimiterIndex = CHARINDEX(@delimiter, @list)
        IF @delimiterIndex = 0
        BEGIN
            SET @nextItem = LTRIM(RTRIM(@list))
            SET @list = SPACE(0)
        END
        ELSE
        BEGIN
            SET @nextItem = LTRIM(RTRIM(LEFT(@list, @delimiterIndex-1)))
            SET @list = RIGHT(@list, LEN(@list)-(@delimiterIndex+(@n-1)))
        END
        INSERT INTO @result (ListIndex, Value)
        VALUES (@i, @nextItem)
    END
    RETURN
END

然后您可以使用左连接轻松检查ID:

declare @ids varchar(50)
set @ids = '121,131,142,145'

select F.Value
from fnc_List2Table(@ids,',') F 
left join YourTable A on F.Value = A.ID
where A.ID is null

答案 1 :(得分:0)

这是另一个实现,它是来自http://www.sqlservercentral.com/articles/Tally+Table/72993/

的修改版本
    CREATE FUNCTION dbo.DelimitedSplit8K
    --===== Define I/O parameters
        (@pString VARCHAR(8000), @pDelimiter CHAR(1))
    RETURNS TABLE WITH SCHEMABINDING AS
    RETURN
    --===== "Inline" CTE Driven "Tally Table" produces values from 0 up to 10,000...
        -- enough to cover VARCHAR(8000)
    WITH E1(N) AS (
                    SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
                    SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
                    SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
                ),                          --10E+1 or 10 rows
        E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
        E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
    cteTally(N) AS (--==== This provides the "zero base" and limits the number of rows right up front
                        -- for both a performance gain and prevention of accidental "overruns"
                    SELECT 0 UNION ALL
                    SELECT TOP (DATALENGTH(ISNULL(@pString,1))) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
                ),
    cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter)
                    SELECT t.N+1
                    FROM cteTally t
                    WHERE (SUBSTRING(@pString,t.N,1) = @pDelimiter OR t.N = 0) 
                )
    --===== Do the actual split. The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found.
    SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY s.N1),
        Item = SUBSTRING(@pString,s.N1,ISNULL(NULLIF(CHARINDEX(@pDelimiter,@pString,s.N1),0)-s.N1,8000))
    FROM cteStart s;

udf保留原样来自sqlservercentral文章

select a.id from
 (-- simulate the id table using union queries
  select 1 id union all
  select 2 id union all
  select 3 id union all
  select 4 id union all
  select 5 id union all
  select 6 id union all
  select 7 id union all
  select 8 id union all
  select 9 id union all
  select 10 id union all
  select 11 id 
)a 
 LEFT JOIN -- Do the magic
 (SELECT item id FROM DelimitedSplit8K(  '1,2,4,5',','))b on a.id = b.Id
  WHERE b.Id is null;

我使用了左连接并过滤了空值而不是IN语句