【问题标题】:SQL Delete Where Not InSQL 删除不存在的地方
【发布时间】:2009-02-03 23:43:41
【问题描述】:

我有一个这样的关系映射表:

attributeid bigint
产品大整数

为了清理不再使用的关系,我想删除 productid = x 且 attributeid 不在 (@includedIds) 中的所有记录,如下例所示:

@attributetypeid bigint, 
@productid bigint,
@includedids varchar(MAX)  


DELETE FROM reltable 
WHERE productid = @productid AND 
attributetypeid = @attributetypeid AND 
attributeid NOT IN (@includedids);

当使用包含超过 1 个 id 的 includedids 参数运行 SQL 时 - 像这样:25,26 - 我得到一个 SqlException 说:

将数据类型 varchar 转换为 bigint 时出错。

这当然是由于 , 在那个 varchar(max) 参数...

我应该如何构造我的删除语句以使其工作?

【问题讨论】:

    标签: sql sql-server


    【解决方案1】:
      SET QUOTED_IDENTIFIER ON
      GO
      CREATE FUNCTION [dbo].[ListToTable] (
      /*
      FUNCTION ListToTable
      Usage: select entry from listtotable('abc,def,ghi') order by entry desc
      PURPOSE: Takes a comma-delimited list as a parameter and returns the values of that list into a table variable.
      */
      @mylist varchar(8000)
      )
      RETURNS @ListTable TABLE (
      seqid int not null,
      entry varchar(255) not null)
    
      AS
    
      BEGIN
          DECLARE 
              @this varchar(255), 
              @rest varchar(8000),
              @pos int,
              @seqid int
    
          SET @this = ' '
          SET @seqid = 1
          SET @rest = @mylist
          SET @pos = PATINDEX('%,%', @rest)
          WHILE (@pos > 0)
          BEGIN
              set @this=substring(@rest,1,@pos-1)
              set @rest=substring(@rest,@pos+1,len(@rest)-@pos)
              INSERT INTO @ListTable (seqid,entry)  VALUES (@seqid,@this)
              SET @pos= PATINDEX('%,%', @rest)
              SET @seqid=@seqid+1
          END
          set @this=@rest
          INSERT INTO @ListTable (seqid,entry) VALUES (@seqid,@this)
          RETURN 
      END
    

    在您的 SQL Server 数据库中运行该脚本以创建函数 ListToTable。现在,您可以像这样重写您的查询:

    @attributetypeid bigint, 
    @productid bigint,
    @includedids varchar(MAX)  
    
    
    DELETE FROM reltable 
    WHERE productid = @productid AND 
    attributetypeid = @attributetypeid AND 
    attributeid NOT IN (SELECT entry FROM ListToTable(@includedids));
    

    @includedids 是您提供的逗号分隔列表。我在处理列表时一直使用这个函数。请记住,此功能不一定会清理您的输入,它只是在逗号分隔列表中查找字符数据并将每个元素放入记录中。希望这会有所帮助。

    【讨论】:

    • 旁注:此函数未针对 SQL Server 2005 进行优化,因为我已将 varchar 大小限制为 8000 以与 SQL Server 2000 兼容。如果您需要最大长度,只需查找/替换 8000对于脚本中的 MAX。
    • 仅供参考,您可以使用 ntext 作为与 sql 2000 向后兼容的参数,请参阅:sommarskog.se/arrays-in-sql-2000.html
    • @sambo99:这是个好建议。
    【解决方案2】:

    Joel Spolsky 在这里回答了一个非常相似的问题:Parameterize an SQL IN clause

    您可以尝试类似的方法,确保将您的 attributetypeid 转换为 varchar。

    【讨论】:

      【解决方案3】:

      您不能将列表作为参数传递 (AFAIK)。

      你能重写 sql 来使用子查询吗,像这样:

      delete from reltable
      WHERE productid = @productid AND 
      attributetypeid = @attributetypeid AND 
      attributeid NOT IN (select id from ... where ... );
      

      ?

      【讨论】:

        【解决方案4】:

        可以将该逗号分隔的列表发送到用户定义的函数,该函数会将其作为简单表返回。然后您的 NOT IN 可以查询该表。 如果您需要我可以提供的 fn.. 自从我大量使用 sql 以来已经大约 5 年了,我将不得不掸掉我大脑的那个部分..

        【讨论】:

          【解决方案5】:

          Erland 有definitive guide 用于处理SQL 2005 中的表列表,SQL 2008 为您提供table based params

          在旁注中,我会避​​免大型列表的 NOT IN 模式,因为它不会缩放,而是考虑使用左连接。

          【讨论】:

          • @sambo99:Erland 的文章读起来很棒。感谢您的链接,这绝对有助于早上:-)
          猜你喜欢
          • 2021-10-11
          • 2019-01-08
          • 1970-01-01
          • 1970-01-01
          • 2018-05-04
          • 2021-09-16
          • 2020-11-28
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多