【问题标题】:SQL Limit on "WHERE X IN (...)"“WHERE X IN (...)”的 SQL 限制
【发布时间】:2012-10-09 21:00:07
【问题描述】:

我有一些数据想要从我们的 SQL 服务器中提取出来。

这个旧数据库没有与之关联的任何主键,因此提取数据就像查询 Excel 电子表格(它实际上起源于几年前)。

不过,我需要针对这些数据生成报告。

目前,我获得了给定时间段内不同序列号的列表,然后提取给定序列号的所有记录。对于 1 个月的时间范围,这可以是 1500 到 3000 个序列号。序列号字段的格式为 char(20),即使序列号只有 15 个字符长。

开始更新

  • 每个Serial_Number 在此表中通常有 5 到 15 个条目。
  • 最多有 10 台机器向该表写入数据,因此可能有相同的 Date_Time

结束更新

此过程需要一段时间,但在列表中的不同序列号之间,我可以使用进度条更新 Windows 窗体,以便管理层知道正在发生的事情以及预计需要多长时间。

我总是试图让这个查询运行得更快。

现在,我正在考虑使用 WHERE 子句提取我需要的数据,例如:

SELECT Col1, Col2, Col3
FROM Table1
WHERE Serial_Number IN (
  SELECT DISTINCT Serial_Number
  FROM Table1
  WHERE Date_Time Between @startDate AND @endDate
)

我的问题是:我会遇到什么问题,特别是因为我们在给定的时间范围内有这么多不同的序列号。

当然,您知道管理部门的某个人会在无聊时尝试运行一年的数据!然后,他们将尝试运行自耶稣出生以来的数据,只是因为他们无事可做。

重申问题:WHERE 子句的 IN 方法是否有限制我可以传入的项目数量?

【问题讨论】:

    标签: sql tsql sql-server-2000


    【解决方案1】:

    Table1 中的索引 Serial_Number 和 Date_Time(具有单独的索引,而不是单个复合索引),这对您来说应该表现得相当好,除非表真的非常庞大。

    使用 Serial_Number 上的一个索引和 (Date_Time, Serial_Number) 上的第二个索引可能会加快速度。第二个索引涵盖子查询,允许仅从索引中对其进行回答。

    注意:我建议使用不需要唯一性的索引,而不是主键。

    【讨论】:

    • 一般情况下,每个序列号有多个条目。 Date_Time 参数可能用作索引,但我们有 10 台不同的机器将数据转储到同一个表中。可能有重复。
    • @jp2code 您可以(并且应该)在您需要的列上定义索引,不管它们是否唯一,Larry 所说的,只需添加这些索引就可以让您的查询进行从 1 分钟到喜欢瞬间
    • 重复并不重要。索引不需要是唯一的。索引 (Date_Time, Serial_Number) 可加速嵌套查询,单独使用 (Serial_Number) 可加速主查询。
    • 我猜CREATE INDEX 已经完成了。我已经研究这个数据 5 年了,但它比这要老得多。我将如何检查?
    • @jp2code:您可以使用 SSMS 或任何其他可以显示表格结构的工具进行检查。
    【解决方案2】:

    好吧,在没有索引的幼稚情况下(听起来像是您的情况),您将不得不扫描Table1 中的所有行以在Serial_Number 上执行DISTINCT 无论如何.所以我不确定它会对你有多大帮助。

    我强烈推荐以下内容:

    • 使用执行计划来确定查询中发生了什么,并且
    • 使用该信息添加一些相关索引以加快您的操作。

    就我们在这里看到的情况而言,听起来Date_Time 将是Table1 中聚集索引的良好候选者。

    编辑:

    要像我上面描述的那样创建非唯一聚集索引,您可以使用以下内容:

    CREATE CLUSTERED INDEX IX_Table1_Date_Time
    ON Table1 (Date_Time)
    

    (来自http://msdn.microsoft.com/en-us/library/aa258260(v=sql.80).aspx

    这将重新排序您的表,以便所有行都按 Date_Time 顺序排序。根据您运行的查询的确切类型,进一步使用执行计划将有助于确定可能极大地帮助您提高性能的其他索引。

    【讨论】:

    • 我在上面放了一些更新的文本。如何创建聚集索引?
    • 我查看了我的表格,似乎 Serial_Number 已添加为索引。不过,我可以添加日期。 但是: WHERE 子句的IN 方法可以传递多少个参数有限制吗? (我更新了我的问题以重申这一点)
    • 据我所知,没有限制。您是在谈论硬编码(或生成)您想要的确切参数值的大列表吗?如果列表相当小,那将起作用。如果它很大,您可能会遇到查询解析开销问题。无论如何,进一步澄清,我会尽力提供帮助。
    • 我的SELECT DISTINCT Serial_Number 代码将轻松返回任何给定月份的 1500 个值。但是,没有任何逻辑可以阻止某人在一年或更长时间内尝试运行此查询。
    【解决方案3】:

    老实说,我认为WHERE 子句的编写没有任何好处。

    您使用了昂贵的内部查询,但没有对结果做任何有意义的事情。我什至没有看到你在任何地方的结果中得到Serial_Number。但是,根据您的问题,听起来您确实需要它。

    我认为 Serial_Number 不需要 DISTINCT 关键字,因为在外部查询的结果中不会消除重复项。

    这样做有什么问题?

    SELECT Serial_Number, Col1, Col2, Col3
    FROM Table1
    WHERE Date_Time Between @startDate AND @endDate
    

    这应该与您的原始查询执行相同的操作。但它会消除昂贵的嵌套查询。

    只需在Date_Time 上添加一个索引,它应该可以工作。这也将消除对Serial_Number 上的索引的需要。

    【讨论】:

    • 我的主要问题是我可以在WHERE 子句的IN 方法中放置的项目数量是否有限制。如果我不选择DISTINCT,那么在IN 方法中我将轻松获得10 倍的序列号。 SQL 是否足够聪明,可以消除重复项,还是也会搜索它们?我不知道。
    • 而且,是的。我使用Serial_Number 字段。这将是我返回的专栏之一。我只是使用Col1Col2Col3 来表示通用列名。
    • 最后,仅仅因为在@startDate@endDate之间测试了一个序列号并不意味着这是第一次测试这个项目。我们有一个奖励系统,奖励第一次通过测试标准的项目。如果某件物品在仓库中存放了 6 个月,然后在发货前进行了重新测试,那么似乎应该根据您的查询对员工进行奖励。
    【解决方案4】:

    显然,无法判断WHERE X IN (...) 的最大长度是多少。

    现在,这就是答案。

    如果在稍后的某个时间点,有人出现并发现相反的东西,请发布该答案,我会这样标记。

    谢谢, 乔

    【讨论】:

      猜你喜欢
      • 2016-10-14
      • 2010-11-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-12
      相关资源
      最近更新 更多