3

我正在寻找从 SQL Server 获取 long 列表的最快方法。

据我所知,下面的代码是您通常可以运行的最快的代码,而减慢它的是 dr.Read() 中发生的所有事情,以及在某种程度上为每一行调用 dr.GetInt64。

var ids = new long[count];
using (var dr = new SqlCommand(string.Format(@"SELECT TOP 10000 ID FROM Data", count),
    conn).ExecuteReader(CommandBehavior.SequentialAccess))    
{
    while (dr.Read())
    {
        ids[i++] = dr.GetInt64(0);                        
    }
}

查询花费的时间可以忽略不计,因此时间花在数据阅读器中的解析和类型验证上。对于 100,000 条记录,大约需要 25 毫秒,这与迭代数组中的 100,000 个项目所需的 0.20 毫秒相比非常慢。

因为我只要求一个 long 列表,所以我想知道是否可以将它们作为单字节数组获取。我所追求的是这样的:

var bytes = (byte[]) new SqlCommand("(I don't know)", conn).ExecuteScalar();                
Buffer.BlockCopy(bytes, 0, ids, 0, 10000);

这将大大减少解析时间。

有人可以告诉我这种方法是否可行吗?

更新:

至少这些方法并不快:

CLR 聚合

可以定义用 .NET 编写的自定义聚合函数。我试图制作一个非常简单的,什么都不做的(使用 SqlUserDefinedAggregate(Format.Native) 使其尽可能快)。这将查询时间增加到 60 毫秒,因此它永远不会更快。

查询如下:

SELECT dbo.ByteIt(ID) FROM (SELECT TOP 100000 ID FROM Data) T

连接 varbinary(max)

可以使用纯 SQL 构建字节数组。那很慢。

DECLARE @n varbinary(max)
SET @n = 0;
SELECT TOP 10000 @n = @n + cast(id as varbinary(8)) FROM Data;
SELECT @n; 

为什么它可能永远不值得付出努力

我能想到的最快的原生聚合是 COUNT。

SELECT COUNT(ID) FROM (SELECT TOP 100000 ID FROM Data) T

这需要 10 毫秒,并且必须是考虑每个值的任何方法的绝对下限。我认为这种性能提升并不值得付出努力。

可悲的是,我认为我的问题的答案是“可以完成但不能更快。与 25 毫秒一起生活”。

4

2 回答 2

0

您现在选择值的方式确实是最佳的;表格数据流协议针对将结果从 SQL Server 传输回客户端进行了优化。您最好的方法是使用您现在拥有的客户端工具(`SqlConnection' 等)。

这里有两种替代方法,但我不推荐它们,我会解释原因。

与往常一样,YMMV 和您应该测试、测试、测试。

您将调用上的GetBytes方法以顺序读取字节并将它们转换为实例(可能通过方法)。SqlDataReaderInt64BitConverter.ToInt64

请注意,为了做到这一点,您必须将调用传递CommandBehavior.SequentialAccess,以便从服务器流式传输大二进制值。ExecuteSqlCommand

也就是说,您现在必须构建大型二进制数组才能在字段中传回。这就是这种方法被挂断的地方,IMO。基本上,你必须做一个枢轴(不是字面的枢轴,而是类似的东西)获取一组bigint值并从中创建一个大的二进制值。这本质上不是一个集合操作,您可能必须在存储过程中包含一些循环代码来创建这个值。

考虑到这一点,您从将大二进制值流式传输到客户端所获得的任何收益似乎都将被您要旋转的 CPU 周期所抵消(并且可能会变得更糟),您将尝试构建那么大的列表中的二进制值bigint

可能可以通过一个CLR 存储过程来缓解其中的一些问题,该过程将获取列表bigint然后为您创建字节数组,但在这一点上,这可能是矫枉过正。

CLR 存储过程在执行过程操作时会更快(也就是说,它不是基于集合的,而 T-SQL 更适合)。读取的数量最初是相同的(毕竟,您必须获取数据来创建二进制字符串)但您可能会耗尽内存(取决于您的集合的大小,因为您必须将它连接到一个值)以及 CPU(由于连接)甚至在您将第一个字节发送回客户端之前,所有这些都需要时间。

于 2012-08-03T15:22:36.427 回答
0

由于您使用的是 SQL Server,因此您可以这样做:

SELECT STUFF((SELECT ',' + ID
                     FROM Data
                     FOR XML PATH('') 
                     ), 1, 1, '')

您将获得一个以逗号分隔的 ID 列表。这是基于我从这篇文章中学到的东西 - http://sqlandme.com/2011/04/27/tsql-concatenate-rows-using-for-xml-path/

请注意,我没有对这种方法进行任何时序测试。我喜欢它,因为它相当简单。

于 2013-10-24T01:37:59.970 回答