从 id 不在 id 列表中的表中选择

Posted

技术标签:

【中文标题】从 id 不在 id 列表中的表中选择【英文标题】:Select from table where ids are not in list of ids 【发布时间】:2020-02-11 08:27:15 【问题描述】:

我正在尝试从 primary_no (VARCHAR(20)) 不包含 @IDS 变量中的任何字符串的表中进行选择,但它不起作用,我尝试使用 CTE,不存在并且不进去。他们都不工作。查询仍然选择所有数据,即使是 @IDS 变量中具有 primary_no 的数据。

ALTER PROCEDURE [dbo].[LinkProc]
    @IDS VARCHAR(MAX)
/*
DECLARE @IDS VARCHAR(MAX)
SET @IDS = '
''00000447'',
''0000300'',
''2900071'',
''2900192''
'
EXEC LinkProc @IDS = @IDS    
*/
AS
    WITH cte (id) AS
    (
        SELECT *  
        FROM dbo.splitstring(@IDS)
    )
    SELECT * 
    FROM link_tb 
    WHERE grp_id = 4
      AND status_cd = 'A' 
      AND primary_no NOT IN (SELECT id FROM cte)

我也试过这个,但没有运气:

SELECT * 
FROM link_tb lt 
WHERE grp_id = 4 
  AND status_cd = 'A' 
  AND NOT EXISTS (SELECT id FROM cte c WHERE lt.primary_no = c.id)

调用(SELECT id FROM cte)的结果集:

'00000447'
'0000300'
'2900071'
'2900192'

我找到了解决这个问题的方法,我在下面回答。

【问题讨论】:

我们手动创建了这个拆分字符串函数。所以当我调用它时 (SELECT * FROM dbo.splitstring(@IDS) 它确实给了我一个拆分字符串的列表。 【参考方案1】:

免责声明:事实证明,从 chat 来看,OP 使用的不是 2014 年,而是 2005 年(完全不受支持,并且已经使用了多年)。因此,使用表类型参数的答案将不起作用,因为该功能不存在。

不过,我已将答案留在那里,以供未来有类似问题的用户使用。


不要使用分隔列表,而是使用表格类型参数

CREATE TYPE dbo.PrimaryList AS TABLE (primary_no varchar(20) NOT NULL);
GO

ALTER PROC dbo.LinkProc @IDs dbo.PrimaryList READONLY AS
BEGIN

    SELECT *
    FROM dbo.link_tb l
         LEFT JOIN @IDs I ON l.primary_no = I.primary_no
    WHERE grp_id = 4
      AND status_cd = 'A' 
      AND I.primary_no IS NULL;

END;
GO

然后您可以这样调用 SP:

DECLARE @IDs dbo.PrimaryList;
INSERT INTO @IDs
VALUES('00000447'),
      ('0000300'),
      ('2900071'),
      ('2900192');

EXEC dbo.LinkProc @IDs;

编辑: 至于为什么你所拥有的东西不起作用,那是因为你引用了你的价值观。你正在做的是相当于:

EXEC dbo.LinkProc @IDs = '''00000447''';

primary_no 的值不会是 '00000447',它只会是 00000447。如果您必须传递一个分隔列表(我建议不要这样做,如果您的函数使用WHILE,我不会感到惊讶,如果您需要删除它),然后不要引用值:

EXEC dbo.LinkProc @IDs = '00000447,0000300,2900071,2900192';

【讨论】:

我传入的@IDS 来自另一个表,IDS 的列表可能很长。有没有办法在不对值进行硬编码的情况下做到这一点? 是的,使用 INSERT INTO...SELECT...FROM 声明 @hypnagogia ,而不是 INSERT INTO...VALUES... 声明 如果你可以有一个很长的列表,那么使用表值参数是一个更好的主意,@hypnagogia。使用IN 的语句的性能可能会因(非常)长的列表而降低。 我在尝试实施您的解决方案时遇到了错误。我可能写错了。但是对于您提到的 INSERT INTO...SELECT...FROM,我不会从查询中的表中进行选择,而是将@IDS 列表作为参数发送。 这就是我在上面所做的,@hypnagogia:EXEC dbo.LinkProc @IDs;【参考方案2】:

这是我的问题的解决方案。 IDS 被解析成一个表,我从那里选择。我的主要问题是我设置了格式不正确的@IDS,字符串中有换行符。把它们放在一起没有空格就可以了。

ALTER PROCEDURE [dbo].[LinkProc]

    @IDS VARCHAR(MAX)

/*

DECLARE @IDS VARCHAR(MAX)

SET @IDS = '00000447,0000300,2900071,2900192'

EXEC LinkProc @IDS = @IDS

*/

AS

SELECT * 
FROM link_tb 
WHERE grp_id = 4 
    AND status_cd = 'A' 
    AND primary_no NOT IN (SELECT param_value FROM dbo.PARSE_PARAM_LIST(@IDS, ','))

【讨论】:

以上是关于从 id 不在 id 列表中的表中选择的主要内容,如果未能解决你的问题,请参考以下文章

选择不在使用多个连接语句创建的表中的行

如何从 SQL CE 中的表中构建逗号分隔列表?

如何为不在第 2 列中的第 1 列选择 SQL

如何从 SQLite 中的表中选择最新的 100 个不同条目?

如何从 EF 中的表中仅选择某些字段

SQL 选择由 Ids 标识并由另一个表中的特定类型过滤的行列表中的所有第一项