由于WHERE子句中的条目超过100,000个,导致SQL Server错误8632


16

我的问题(或至少是错误消息)与查询处理器用尽内部资源非常相似-极长的sql查询

我的客户正在使用SQL选择查询,其中包含一个正好有100,000个条目的子句。

查询失败,出现错误8632和错误消息

内部错误:已达到表达式服务限制。请在查询中查找可能复杂的表达式,然后尝试简化它们。)

我非常奇怪地抛出此错误消息,恰好在100,000个条目处,所以我想知道这是否是可配置的值。是这种情况,如果是的话,如何将该值增加到更高的值?

MSDN上,建议重新编写查询,但我想避免这种情况。

同时,我发现我正在谈论的条目列表包含自然数,其中有些看起来似乎是连续的(例如(1,2,3,6,7,8,9,10,12, 13,15,16,17,18,19,20)。

这使得SQL的子句类似于:

where entry in (1,2,3,6,7,8,9,10,12,13,15,16,17,18,19,20)

我可以将其转换为:

where (entry between 1 and 3) OR
      (entry between 6 and 10) OR
      (entry between 12 and 13) OR
      (entry between 15 and 20)

可以通过以下方式将其缩短:

where entry in (1,...,3,6,...,10,12,13,15,...,20)

...或类似的东西?(我知道这是一个长期的尝试,但这会使软件更新更容易且更具可读性)

供您参考:where子句中的数据是在另一个表上完成的计算结果:首先在该表的开头读取并过滤该表的条目,然后再进行一些额外的处理(使用SQL),这种额外处理的结果是更多的过滤,其结果在where子句中使用。由于不可能用SQL编写完整的过滤,因此使用了上述方法。显然,子句的内容可能在每个处理过程中都发生变化,因此需要动态解决方案。


7
回应您的编辑:不,WHERE IN不支持这种范围语法。另外,它WHERE () OR () OR ()不能为AND。但是要使用布伦特的建议,您实际上不必更改整个查询,您可以这样做WHERE IN (SELECT myID FROM #biglist)。并且#biglist可以是真实的(永久的)表,也可以是您即时创建的临时表。
BradC

请请发布整个查询以及您在外部计算的内容,这可能确实是您完全可以在SQL中完成的事情。如果您担心隐私或其他原因,请重命名字段名称。
迈克,

Answers:


67

要搜索100,000个以上的值,请将它们放在临时表中,而不是每个要搜索的值一行。然后,将您的查询加入该临时表以进行过滤。

超过100,000个值的不是参数-它是一个表。而不是考虑提高限制,请考虑Swart的“百分之十规则”:如果您要达到SQL Server限制的10%,则可能会遇到困难。


1
评论不作进一步讨论;此对话已转移至聊天
保罗·怀特

10

如果您仍然要更改应用程序,请考虑

(a)将TVP用于整个值集-您将创建一个 DataTable在C#中,然后使用StructuredType,将其传递到存储过程中,如我在此处演示的。(希望十万个条目是不正常的,因为无论使用哪种方法,可伸缩性都可能成为问题。)

CREATE TYPE dbo.optionA AS TABLE(value int PRIMARY KEY);
GO

CREATE PROCEDURE dbo.procedure_optionA
  @t dbo.optionA READONLY
AS
BEGIN
  SET NOCOUNT ON;
  SELECT <cols> FROM dbo.<table> AS t
    INNER JOIN @t AS tvp
    ON t.entry = tvp.value;
END
GO

要么

(b)使用TVP传递范围的上限和下限,并编写稍有不同的联接(感谢@ypercube)。

  CREATE TYPE dbo.optionB AS TABLE
  (
    LowerBound int,
    UpperBound int,
    PRIMARY KEY (LowerBound, UpperBound)
  );
  GO

  CREATE PROCEDURE dbo.procedure_optionB
    @t dbo.OptionB READONLY
  AS
  BEGIN
    SET NOCOUNT ON;
    SELECT <cols> FROM dbo.<table> AS t
      INNER JOIN @t AS tvp
      ON  t.entry >= tvp.LowerBound 
      AND t.entry <= tvp.UpperBound;
  END
  GO


4

关于缩短查询条件,我只收取2美分:-

如果您能够entry预先确定所有可能的值,那么对查询进行补充是否可行?

之前

where entry in (1,2,3,6,7,8,9,10,12,13,15,16,17,18,19,20)

where entry not in (4,5,11,14)

0

在没有实际能够看到查询的情况下很难弄清楚您要完成的工作,但是假设您的查询只需要两张表,一张带有数据,一张带有您要避免的值:

  • 使用 where entry in (<list of 100.000 values>)从效率和维护的角度来看,都非常糟糕。
  • 使用where entry in (select whatever from table)几乎没有以前那么可怕。但是,取决于表的特质和SQL引擎,尽管有角膜癌,它的执行效果也可能不错。(在Oracle中可能会好的,在MySQL中永远不会,不会记得MSSQL)。
  • 使用PLSQL实现这一点简直太可怕了。
  • 我认为(根本不了解查询),应按以下方式重写查询:

    • 如果这些100.000值始终相同,而不依赖于查询的其余部分,则应将这些值上载到表(table_fixed_values)中,并使用

      SELECT * -- whatever you might need
      FROM
         table_a A
         LEFT JOIN table_fixed_values ON A.entry=B.entry
      WHERE B.entry IS NOT NULL
    • 如果这100.000个值不相同,则必须有某种逻辑来提取这100.000个值,这些逻辑应嵌入ON上一个查询的中。


3
为什么LEFT JOIN table_fixed_values ON A.entry=B.entry WHERE B.entry IS NOT NULL而不是等效,更简单,更容易阅读INNER JOIN table_fixed_values ON A.entry=B.entry
ypercubeᵀᴹ

@ypercubeᵀᴹ完全正确,INNER JOIN更有意义。
glezo
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.