C# 在将大量ID发送到SQL server进行筛选时,是否有方法提高性能?

C# 在将大量ID发送到SQL server进行筛选时,是否有方法提高性能?,c#,sql-server,performance,primary-key,bulk,C#,Sql Server,Performance,Primary Key,Bulk,我使用以下C代码将ID列表发送到SQL Server 2012。它过滤mytable的列ID,并返回前50个匹配ID 实际上,执行查询大约需要180毫秒。数据库是本地的。我想知道是否有一些方法可以提高性能。我注意到性能与发送到SQL server的ID数直接相关,而不是与表中的实际记录数直接相关。如果我只发送1000条记录,速度非常快(

我使用以下C代码将ID列表发送到SQL Server 2012。它过滤
mytable
的列ID,并返回前50个匹配ID

实际上,执行查询大约需要180毫秒。数据库是本地的。我想知道是否有一些方法可以提高性能。我注意到性能与发送到SQL server的ID数直接相关,而不是与表中的实际记录数直接相关。如果我只发送1000条记录,速度非常快(<1ms)。也许还有另一种更有效的方式发送这些ID

用户定义的表
int\u list\u type
mytable
定义如下:

CREATE TABLE mytable (Id int NOT NULL PRIMARY KEY CLUSTERED)
CREATE TYPE int_list_type AS TABLE(Id int NOT NULL PRIMARY KEY CLUSTERED)
C#代码:


您可以尝试将ID列表作为逗号分隔的字符串列表传递,然后在SQL中查找所有where ID in(ListOfIds)

我没有机会测试这个,但它解决了一个类似的问题,我在过去。请让我知道它是否有任何区别(好或坏)

+1用于使用表值类型并将其作为参数传入。这是一个如何使用表值类型的教科书示例。 不幸的是,正如您所指出的,在传入非常大的数据数组时,仍然会遇到性能问题

您可以尝试使用XML传递值:在处理较大数组时,XML解析器在您的环境中的性能可能更高,请注意警告,保持名称空间简单或完全忽略它们,或者对于较小的数组100到1000,性能比表值类型差得多,在较大的阵列中,您可能会看到更好的性能

问题,您是否可以重新构建此解决方案,以便ID列表已经存在于数据库中?或者卸载对ID列表的摄取,以便在准备查询时首先执行此操作

我在应用程序中通过允许用户手动“标记”行、运行脚本或选择一些预编译的逻辑来选择ID来实现这一点。 我将这些ID存储在标记表中(用户可以保存标记列表以在其他会话中重用)

既然ID列表已经在DB中,我们只需将ID加入到选择列表中即可。查询的执行速度并不比表值类型或解析xml、json或字符串的任何变体快,但我们绕过了通常成本最高的解析步骤。现在数据在数据库中,SQL Server更容易优化和缓存查询执行计划

注意:当以表值类型、表变量或临时表的形式发送要在查询中执行的数据列表时,必须在SQL temp DB中创建此数据列表


您可能会发现一些奇特的解决方案,包括配置环境以支持此场景,但是如果您可以更改流程以确保选择列表已经在数据库中,那么大部分繁重的工作将为您完成。然后,您可以使用索引和其他传统的DBA维护来进一步优化查询性能。

旁注:使用
TOP x
而不使用
ORDER BY
caluse意味着您获得了x条记录,但并不意味着您获得了第一条x条记录-数据库表本质上是无序的,因此,如果没有
orderby
子句,就无法保证
SELECT
语句返回的行的顺序。您是否检查了什么是实际的“瓶颈”数据库,或者您在
GetSqlDataRecords
中的循环?要获得更准确的度量,您需要在连接后启动秒表。open()语句。打开连接总是很慢。理想情况下,您需要缓存该连接或使用连接池。此外,在输出经过的时间之前,请停止秒表。写入控制台也很慢,所以在执行此操作之前需要停止。@jason.kaisersmith是的,它可以提高精度,但在OP中说,这些不是瓶颈。您可以尝试
从MyTable t内部连接@ids lt ON t.Id=lt.Id选项中选择前50个t.Id(重新编译)
,这样就可以考虑表变量中的行数。我以前试过这个方法,但速度非常慢。我在SSMS profiler下运行了查询,花费了大部分时间的是解析查询(有意义)。
static void Main()
{       
    List<int> idsToSend = Enumerable.Range(0, 200000).ToList();
    List<int> idsResult = new List<int>();

    Stopwatch sw = Stopwatch.StartNew();
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        connection.Open();

        SqlCommand command = new SqlCommand(@" SELECT TOP 50 t.Id FROM MyTable t
                                                INNER JOIN @ids lt ON t.Id = lt.Id", 
                                               connection);

        command.Parameters.Add(new SqlParameter("@ids", SqlDbType.Structured)
        {
            TypeName = "int_list_type",
            Direction = ParameterDirection.Input,
            Value = GetSqlDataRecords(idsToSend)
        });

        SqlDataReader reader = command.ExecuteReader();
        while (reader.Read())
        {
            idsResult.Add(reader.GetInt32(0));
        }
    }
    Console.WriteLine(sw.Elasped);
}

private static IEnumerable<SqlDataRecord> GetSqlDataRecords(IEnumerable<int> values)
{
    SqlMetaData[] metaData = { new SqlMetaData("Id", SqlDbType.Int) };

    foreach (int value in values)
    {
        SqlDataRecord rec = new SqlDataRecord(metaData);
        rec.SetInt32(0, value);
        yield return rec;
    }
}
Stopwatch sw = Stopwatch.StartNew();
GetSqlDataRecords(listOfIfs).ToList();
Console.WriteLine(sw.Elapsed);