将SQL Server中的数字流列表作为单字节数组

时间:2012-08-03 14:11:27

标签: .net sql-server

我正在寻找从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毫秒相比非常慢。

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

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毫秒一起生活”。

2 个答案:

答案 0 :(得分:0)

现在选择价值观的方式确实是最优的; Tabular Data Stream protocol已经过优化,可以将结果从SQL Server传回客户端。您最好的方法是使用您现在拥有的客户端工具(`SqlConnection'等)。

以下是两种替代方法,但我不推荐它们,我会解释原因。

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

您可以致电GetBytes method上的SqlDataReader class按顺序读取字节并将其转换为Int64 instances(可能通过BitConverter.ToInt64 method)。

请注意,为了执行此操作,您必须将CommandBehavior.SequentialAccess传递给call to Execute上的SqlCommand class,以便从服务器传输大二进制值。

那就是说,你现在必须构造大二进制数组以传回一个字段。这就是这种方法被挂起的地方,IMO。基本上,您必须使用bigint值集合并从中创建一个大的二进制值来进行数据透视(不是文字枢轴,但类似的东西)。这本身并不是一个set操作,你可能需要在你的sproc中有一些循环代码来创建这个值。

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

可能能够通过CLR stored procedure来取消其中的一部分,{{3}}将获取bigint的列表,然后为您创建字节数组,但是这点可能有点过头了。

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

答案 1 :(得分: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/

请注意,我没有对此方法进行任何计时测试。我喜欢它,因为它很简单。