给定主键选择大量行

时间:2009-01-18 15:45:52

标签: sql sql-server

我需要从一个更大的表中选择大量的行,这些表是在自动增量标识列上键入的。我有我想要选择的每一行的主键值,但它可能非常大。

通常,但并非总是如此,正在选择的行是连续的,因此我实现了一种机制,将select转换为一组范围子句,用于所有聚集在一起的条目([PrimaryKey] BETWEEN 151 AND 217 ),以及使用IN子句选择所有隔离条目的回退方法。

最后我得到了类似的东西

SELECT * FROM Table WHERE ([PrimaryKey] BETWEEN 151 AND 217) OR ([PrimaryKey] BETWEEN 314 AND 378) OR ...
OR [PrimaryKey] IN (1,3,7,14,147...)

这适用于我主要使用大范围的情况,但随着查询变大而崩溃。我刚遇到一个退化的案例,我有大量的“成对”条目,这两个条目在我放弃它之前尝试描述执行计划的时间超过15分钟,为2个条目生成BETWEEN语句。

我想到的第一件事就是我可以改变开始生成范围的阈值,而不是单个值超过2(10或许10?),但我想知道是否有更好的解决方案那里。

6 个答案:

答案 0 :(得分:7)

使用您要选择的值创建Temp表,并执行从主表到临时表的连接。这样你几乎没有限制。

答案 1 :(得分:2)

我可以理解你的机制的意图。然而,在实践中,优化者中存在指数和信念通常会更好。

如果要在WHERE子句中创建十几个条件,查询引擎需要检查每个条件,直到找到匹配为止。

同样,创建多个查询并将它们联合起来会意味着多次对表进行索引扫描或索引查找。

然而,当你有一个大的列表时,IN子句可能会变得非常慢。在这种情况下,使用连接通常更快。根据经验,这始终是我的首选。

然而,BETWEEN的使用对于更大的范围是特别有效的。考虑到这一点,使用UNION机制可能是有益的,第一个recorset使用JOIN,其余使用BETWEEN,前提是BETWEEN的范围很大。

重要的考虑因素是准备此类查询的时间。如果SQL Server必须使用T-SQL生成动态查询,那么将会有两个问题。生成查询的时间和解析它的时间然后生成执行计划。第一个将主导大型列表,并且可能比使用BETWEEN节省更多时间。

如果客户端生成动态查询,您可能会认为您正在将一些负载从服务器转移到客户端。虽然我仍然怀疑这些好处是多么重要。

屁股,除非我看到非常明显的性能提升,否则我会坚持加入。主要原因是工程原因;
- 发展的时间 - 代码的可靠性(聪明的技巧简单总是更可靠)
- 代码的可维护性(后续的维护者会理解这个技巧吗?)

如果您测试JOIN和BETWEEN的各种组合,无论是否有UNION等,我都会非常有兴趣看到您的表现结果。

答案 2 :(得分:1)

正如用户ocdecio正确建议的那样,您可以加入一个包含所有ID的临时表。您还可以尝试将OR分成不同的 UNION 部分:

SELECT * FROM Table WHERE [PrimaryKey] BETWEEN 151 AND 217
UNION
SELECT * FROM Table WHERE [PrimaryKey] BETWEEN 314 AND 378
UNION
...
UNION
SELECT * FROM Table WHERE [PrimaryKey] IN (1,3,7,14,147...)

答案 3 :(得分:0)

这与我最近遇到的问题类似。我需要从表中选择那些与大型主键列表相交的行。问题是如何以快速和有效的形式有效地将大量密钥发送到SQL服务器。

对于这种情况,最有效的是XML数据类型。如果创建一个采用XML类型参数的存储过程,则可以将输入预格式化为XML片段。举个例子,假设XML片段看起来像这样:

<a>
  <b>1</b>
  <b>3</b>
  <b>7</b>
  <b>14</b>
  <b>147</b>
</a>

我给了元素短名称(“a”和“b”),因为较长的名称意味着要从客户端传输到SQL服务器的字节数更多。以下是如何选择“b”元素的所有内容作为记录集:

declare @x xml
set @x = '<a><b>1</b><b>3</b><b>7</b><b>14</b><b>147</b></a>'
select t.item.value('.', 'int') from @x.nodes('//a/b') as t(item)

虽然语法含糊不清,但可以像表一样查询XML类型。现在你应该看看它的发展方向。如果我们可以像表一样查询XML类型,我们可以将它与另一个表相交:

select * from MyTable where ID in 
  (select t.item.value('.', 'int') from @x.nodes('//a/b') as t(item))

或使用联接

;with cte as
  (select ID = t.item.value('.', 'int') from @x.nodes('//a/b') as t(item)) 
select * from MyTable inner join cte on MyTable.ID = cte.ID

您需要运行这两个版本才能查看哪些版本的数据更快。我发现JOIN对我的数据运行得更快。这是一个存储过程,它将XML类型作为输入并回送我们选择的行:

create procedure MyProc @x xml as
begin
  set nocount on
  ;with cte as
    (select ID = t.item.value('.', 'int') from @x.nodes('//a/b') as t(item)) 
  select * from MyTable inner join cte on Table.ID = cte.ID
end

新存储过程的示例调用:

exec MyProc '<a><b>1</b><b>3</b><b>7</b><b>14</b><b>147</b></a>'

我还发现为输入片段添加XML模式有助于略微加快存储过程。我不会在这里详细介绍XML模式,但我们的想法是事先告诉SQL XML片段的样子。以下是我们输入模式的方法:

create xml schema collection MyInputSchema as
  '<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <xsd:element name="a">
      <xsd:complexType>
        <xsd:complexContent>
          <xsd:restriction base="xsd:anyType">
            <xsd:sequence>
              <xsd:element name="b" type="xsd:integer" maxOccurs="unbounded" />
            </xsd:sequence>
          </xsd:restriction>
        </xsd:complexContent>
      </xsd:complexType>
    </xsd:element>
  </xsd:schema>'

现在我们可以将此架构与存储过程的输入相关联,如下所示:

create procedure MyProc @x xml(MyInputSchema) as
begin
  set nocount on
  ;with cte as
    (select ID = t.item.value('.', 'int') from @x.nodes('//a/b') as t(item)) 
  select * from MyTable inner join cte on Table.ID = cte.ID
end

完成所有这些后,我能够从客户端计算机向SQL服务器发送一个包含43,016个字符的XML片段,并快速返回结果集。我在10个线程上测试了1,000个请求,总共10,000个请求。结果是每秒处理72个请求。当然,您的millage将根据您的硬件和软件而有所不同。

注意:此代码适用于SQL 2005,也适用于2008年。

答案 4 :(得分:0)

如何确定要选择的主键值列表?我想知道我们是否应该进一步查看这个'上游' - 是否有一些早期的查询或查询你运行到达密钥列表?如果是这样,也许你可以从中进行连接并完全跳过键查找。

答案 5 :(得分:0)

我的第一个想法是看看你如何拉入键列表。是来自其他地方的查询吗?如果是这样,你试过吗

SELECT * FROM Table WHERE [PrimaryKey] in
(
    SELECT PrimaryKey from SomeOtherTable where Condition = 'met'
)

或者如果你的条件在同一张表中,那就更简单了

SELECT * FROM Table WHERE condition = 'met'

当然,如果您的密钥都来自其他来源(可能是您的应用程序),那么我个人会坚持使用之前建议的方法之一,或者咬一口并使用一个大的IN子句。

顺便说一句 - 抱歉,如果我教任何人吸蛋,那就是我的经验是,有时候最简单的解决方案会被忽视。