在WHERE子句中使用“IN”,其中集合中的项目数非常大

时间:2009-02-10 12:55:51

标签: sql sql-server ms-access

我有一种情况,我需要对一组非常大的行进行更新,我只能通过它们的ID来识别(因为目标记录是由用户选择的,除了它的集合之外没有任何共同之处记录用户想要修改的内容)。所有这些记录都在更新相同的属性,所以我想进行一次UPDATE调用。

在UPDATE语句中使用“WHERE IN(1,2,3,4,..... 10000)”是不是更糟糕的做法还是有更好的方法来进行此更新?

为每条记录使用单独的更新语句并将它们粘贴到单个事务中会更有意义吗?现在我正在使用SQL Server和Access,但如果可能的话,我希望在任何类型的关系数据库中听到更广泛的最佳实践解决方案。

10 个答案:

答案 0 :(得分:15)

另一种方法是将这些数字存储在临时表中,并在连接中使用它来进行更新。如果能够执行单个更新语句肯定比每个记录执行一个语句更好。

答案 1 :(得分:8)

我会一直使用

WHERE id IN (1,2,3,4,.....10000)

除非您的in子句愚蠢大,否则不应该从用户输入中发生。

编辑:例如,Rails在幕后做了很多次

在单个事务中单独执行更新语句肯定不会更好。

答案 2 :(得分:4)

如何生成IN子句?

如果有另一个SELECT语句生成这些值,你可以简单地将其插入UPDATE,如下所示:

UPDATE TARGET_TABLE T
SET
  SOME_VALUE = 'Whatever'
WHERE T.ID_NUMBER IN(
                    SELECT ID_NUMBER  --this SELECT generates your ID #s.
                    FROM SOURCE_TABLE
                    WHERE SOME_CONDITIONS
                    )

在某些RDBMses中,使用EXISTS语法可以获得更好的性能,如下所示:

UPDATE TARGET_TABLE T
SET
  SOME_VALUE = 'Whatever'
WHERE EXISTS (
             SELECT ID_NUMBER  --this SELECT generates your ID #s.
             FROM SOURCE_TABLE S
             WHERE SOME_CONDITIONS
               AND S.ID_NUMBER =  T.ID_NUMBER
             )

答案 3 :(得分:2)

在Oracle中,您可以将一个值限制在IN子句中。 所以你最好使用OR,x = 1或x = 2 ......据我所知,这些并不受限制。

答案 4 :(得分:2)

我会使用table-variable / temp-table;将值插入此中,然后连接到它。然后您可以多次使用同一组。如果您(例如)将ID为CSV的传递为varchar,则此方法尤其有用。作为SQL Server示例:

DECLARE @ids TABLE (id int NOT NULL)

INSERT @ids
SELECT value
FROM dbo.SplitCsv(@arg) // need to define separately

UPDATE t
SET    t. // etc
FROM   [TABLE] t
INNER JOIN @ids #i ON #i.id = t.id

答案 5 :(得分:2)

如果不知道“非常大”的身份证号码是多少,我就冒险猜测。 ;-)

由于您使用Access作为数据库,因此ID的数量不能 。假设我们谈论的是少于10,000个数字,我们应该知道容器的限制来保存ID(前端使用的语言是什么?),我坚持一个UPDATE语句;如果这是最可读和最容易在以后执行维护。否则,我会使用一些聪明的逻辑将它们分成多个语句。比如将语句分成多个语句,每个语句有一个,十个,一百个,一个...... ID。

然后,我将它留给DB优化器尽可能高效地执行语句。我可能会对查询/查询进行“解释”,以确保没有任何愚蠢的事情发生。

但根据我的经验,将这种优化留给数据库管理器本身通常是可以的。花费最多时间的一件事通常是与数据库的实际连接,因此如果您可以在同一连接中执行所有查询,则通常没有问题。确保在开始查看并等待任何结果集返回之前发送所有UPDATE语句。 : - )

答案 6 :(得分:1)

一般来说,有几件事需要考虑。

  1. 解析数据库中缓存的语句。每个语句在IN子句中具有不同数量的项,必须单独解析。你正在使用绑定变量而不是文字,对吧?
  2. 某些数据库对IN子句中的项目数有限制。对于Oracle,它是1000。
  3. 更新锁定记录时。如果您有多个单独的更新语句,则可能会出现死锁。这意味着您必须注意发布更新的顺序。
  4. 即使对于非常快速的语句,数据库的往返延迟也很高。这意味着一次操作大量记录通常会更好,以节省旅行时间。
  5. 我们最近更改了系统以限制in-clauses的大小并始终使用绑定变量,因为这减少了不同SQL语句的数量,从而提高了性能。基本上我们生成我们的SQL语句并在子句超过一定大小时执行多个语句。我们不会对更新这样做,所以我们不必担心锁定。你会的。

    使用临时表可能无法提高性能,因为您必须使用ID填充临时表。实验和性能测试可以在这里告诉你答案。

    单个IN子句非常易于理解和维护。这可能是你首先要担心的。如果您发现查询的性能很差,您可能希望尝试不同的策略并查看它是否有帮助,但不要过早优化。 IN子句在语义上是正确的,所以如果它没有被破坏则不管它。

答案 7 :(得分:1)

如果您使用的是Oracle,我建议使用表函数,类似于Marc Gravell的帖子。

-- first create a user-defined collection type, a table of numbers
create or replace type tbl_foo as table of number;

declare
  temp_foo tbl_foo;
begin
  -- this could be passed in as a parameter, for simplicity I am hardcoding it
  temp_foo := tbl_foo(7369, 7788);

  -- here I use a table function to treat my temp_foo variable as a table, 
  -- and I join it to the emp table as an alternative to a massive "IN" clause
  select e.*
    from emp e,
         table(temp_foo) foo
   where e.empno = foo.column_value;
end;

答案 8 :(得分:0)

我不知道IN列表中的值类型。如果它们是1到10,000的大部分值,您可以处理它们以获得类似的内容:

WHERE MyID BETWEEN 1 AND 10000 AND MyID NOT IN (3,7,4656,987)

或者,如果NOT IN列表仍然很长,则处理列表并生成一堆BETWEEN语句:

WHERE MyID BETWEEN 1 AND 343 AND MyID BETWEEN 344 AND 400 ...

等等。

最后,如果使用直通查询,则不必担心Jet将如何处理IN子句。您不能在代码中执行此操作,但您可以将已保存的QueryDef定义为直通,并在运行时更改代码中的WHERE子句以使用IN列表。然后它全部传递给SQL Server,SQL Server将决定如何处理它。

答案 9 :(得分:0)

有多种方法可以在where条件中容纳大量值

  1. 使用临时表

    将这些值插入到具有单列的临时表中。

    在该特定列上创建UNIQUE INDEX。

    使用新创建的临时表将所需表INNER JOIN

  2. 在SQL Server中使用类似数组的功能

    SQL确实支持类似数组的功能

    检查this链接以获取完整的文档。

示例语法:

Create TABLE #IDs (id int NOT NULL)
DECLARE @x varchar(max) = '' 
DECLARE @xParam XML;
SELECT @xParam = CAST('<i>' + REPLACE(@x, ',', '</i><i>') + '</i>' AS XML)
INSERT into #IDs
SELECT x.i.value('.','NVARCHAR(100)') as key FROM @xParam .nodes('//i') x(i)
CREATE UNIQUE INDEX IX_#IDs ON #IDs (ID ASC) 

使用

查询
SELECT A.Name, A.Age from Table A 

ID.id = A.Key上的INNER JOIN #IDs