Sql 按ID选择多行,是否有比中的WHERE更快的方法

Sql 按ID选择多行,是否有比中的WHERE更快的方法,sql,sql-server-2008-r2,Sql,Sql Server 2008 R2,我有一个SQL表,我想按ID选择多行。例如,我想从我的表中获取ID为1、5和9的行 我一直在使用类似于以下内容的WHERE IN语句进行此操作: SELECT [Id] FROM [MyTable] WHERE [Id] IN (1,5,9) 但是,对于“in”子句中的大量项目,这是相当缓慢的 下面是使用where in从包含1000000行的表中选择行的一些性能数据 Querying for 1 random keys (where in) took 0ms Querying for 100

我有一个SQL表,我想按ID选择多行。例如,我想从我的表中获取ID为1、5和9的行

我一直在使用类似于以下内容的WHERE IN语句进行此操作:

SELECT [Id]
FROM [MyTable]
WHERE [Id] IN (1,5,9)
但是,对于“in”子句中的大量项目,这是相当缓慢的

下面是使用where in从包含1000000行的表中选择行的一些性能数据

Querying for 1 random keys (where in) took 0ms
Querying for 1000 random keys (where in) took 46ms
Querying for 2000 random keys (where in) took 94ms
Querying for 3000 random keys (where in) took 249ms
Querying for 4000 random keys (where in) took 316ms
Querying for 5000 random keys (where in) took 391ms
Querying for 6000 random keys (where in) took 466ms
Querying for 7000 random keys (where in) took 552ms
Querying for 8000 random keys (where in) took 644ms
Querying for 9000 random keys (where in) took 743ms
Querying for 10000 random keys (where in) took 853ms
有没有比使用WHERE-IN更快的方法

我们无法连接,因为这是在断开连接的系统之间

我听过一个例子,但根据我的研究,MSSQL没有内存中的表选项,即使如此,它在插入临时表时是否会像WHERE in一样进行完全相同的索引扫描

编辑:

此表具有作为主键的ID,因此具有默认的主键索引,cf

CREATE TABLE [dbo].[Entities](
    [Id] [int] IDENTITY(1,1) NOT NULL,
 CONSTRAINT [PK_dbo.Entities] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
执行计划

下面是生成这些性能结果的控制台应用程序的要点

编辑2 我创建了一个函数,该函数从逗号分隔的字符串创建一个临时表,然后连接到该表。它的速度更快,但我认为主要是因为使用where-in解析查询的问题

Querying for 1 random keys took 1ms
Querying for 1000 random keys took 34ms
Querying for 2000 random keys took 69ms
Querying for 3000 random keys took 111ms
Querying for 4000 random keys took 143ms
Querying for 5000 random keys took 182ms
Querying for 6000 random keys took 224ms
Querying for 7000 random keys took 271ms
Querying for 8000 random keys took 315ms
Querying for 9000 random keys took 361ms
Querying for 10000 random keys took 411ms

我猜如果您使用主键索引的内存表连接表,例如:

declare @tbl table (ids int primary key)
您可以用所需的id填充此表,并预执行优化的内部联接


问题可能在于填补这一空缺所需的时间。我想您可以为此使用一个链接服务器,或者使用BCP实用程序填充一个临时表,然后将其删除。

首先,我认为声称您的数据暗示了
O(n log(n))
是一种延伸。(顺便说一句,您进行了性能测试,这太棒了。)以下是每个值的时间:

1000    0.046
2000    0.047
3000    0.083
4000    0.079
5000    0.078
6000    0.078
7000    0.079
8000    0.081
9000    0.083
10000   0.085
虽然随着时间的推移略有增加,但从2000年到3000年的跳跃要显著得多。如果这是可复制的,我的问题是为什么会出现这样的不连续性

对我来说,这是关于
O(n)
O(n log(n))
的更多建议。但是,理论值的经验估计很难近似。因此,确切的限制并不那么重要

我希望性能是
O(n)
(其中
n
是实际值,而不是某些估计中的位长度)。我的理解是中的
的行为就像一组巨大的
s。大多数记录没有通过测试,所以他们必须进行所有的比较。因此,
O(n)

下一个问题是id字段上是否有索引。在这种情况下,您可以在
O(n log(n))时间(
log(n)
用于遍历索引,而
n`用于为每个值执行此操作)中获得匹配ID集。这更糟,但我们忽略了原始表大小的因素。这应该是一场大胜利

正如Andre所建议的,您可以加载一个表并连接到一个临时表。我会省去索引,因为在较大的表上使用索引可能会更好。这将使您
O(n log(n))
——对原始表的大小没有(显著)依赖性。或者,您可以省去索引,让
O(n*m)
其中
m
是原始表的大小。我认为在临时表上构建的任何索引都会让您返回到
O(n log(n))
性能(假设数据没有预排序)

将所有内容都放在查询中有一个类似的、未说明的问题——解析查询。随着字符串变长,这将花费更长的时间


简言之,我推荐您进行性能测量,但不推荐您得出关于算法复杂性的结论。我认为你的数据不能支持你的结论。此外,查询的处理比您建议的要复杂一些,并且您忽略了较大表的大小,这可能会产生主要影响。而且,我很好奇2000到3000行之间发生了什么。

好的,我定义了一个表类型,然后将该类型直接传递到查询中并连接到它,从而使它运行得非常快

在SQL中

CREATE TYPE [dbo].[IntTable] AS TABLE(
    [value] [int] NULL
)
编码

DataTable dataTable = new DataTable("mythang");
dataTable.Columns.Add("value", typeof(Int32));

toSelect.ToList().ForEach(selectItem => dataTable.Rows.Add(selectItem));

using (SqlCommand command = new SqlCommand(
    @"SELECT * 
    FROM [dbo].[Entities] e 
    INNER JOIN @ids on e.id = value", con))
{
    var parameter = command.Parameters.AddWithValue("@ids", dataTable);
    parameter.SqlDbType = System.Data.SqlDbType.Structured;
    parameter.TypeName = "IntTable";

    using (SqlDataReader reader = command.ExecuteReader())
    {
        while (reader.Read())
        {
            results.Add(reader.GetInt32(0));
        }
    }
}
这将产生以下结果

Querying for 1 random keys (passed in table value) took 2ms
Querying for 1000 random keys (passed in table value) took 3ms
Querying for 2000 random keys (passed in table value) took 4ms
Querying for 3000 random keys (passed in table value) took 6ms
Querying for 4000 random keys (passed in table value) took 8ms
Querying for 5000 random keys (passed in table value) took 9ms
Querying for 6000 random keys (passed in table value) took 11ms
Querying for 7000 random keys (passed in table value) took 13ms
Querying for 8000 random keys (passed in table value) took 17ms
Querying for 9000 random keys (passed in table value) took 16ms
Querying for 10000 random keys (passed in table value) took 18ms

你确实有Id索引,对吗?正如Dale M.所建议的,Id索引几乎是你需要的第一件事。第二,查看查询计划并验证它是否只涉及索引,而不是底层表,或者更糟糕的是,是否对底层表进行了表扫描。我支持上面的两条注释,但是很难说您要做什么。也许如果你能提供一个更广阔的图景,人们将能够提出更具体的建议。@dalem是的,它应该被编入索引,请参见edits@TimoGeusch我在执行计划中添加了100%的索引,请对此进行分析,并获取此方法性能数据的一些统计信息或使用表值参数,然后,不必构建表变量并将行集插入其中。如果索引搜索仍然是瓶颈,那么在这两种情况下都不太可能有什么不同,但是TVP应该有优势。我认为你对算法复杂性的看法是绝对正确的,现在我看到了每行数,(我真的只是基于复杂性的猜测,似乎是非线性的),我将在问题中更新这一点。我认为出现峰值的原因可能与围绕查询规划的优化有关(请参阅:)较大的表中有1000000行(实际上这是个问题)做得不错。在你发布这篇文章之前,我以为你在用T-SQL测试它。这是使用BCP的一个很好的替代方案。祝你好运!在真正的应用程序中,根据表的宽度,瓶颈可能是随机ID搜索生成的随机IO。SQL Server可能会在TVP上插入排序,然后再将其连接到实体以最小化随机性。如果在TVP(在ID上)中定义集群PK,则可以避免该排序的开销。我还将试验数据压缩技术