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 レコードの場合、配列内の 100,000 項目を反復するのにかかる 0.20 ミリ秒と比較して、約 25 ミリ秒かかります。

私は 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 の連結 (最大)

純粋な 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

The way you are selecting values now is really optimal; the Tabular Data Stream protocol is optimized for transferring results from SQL Server back to a clien. Your best approach is to use the client tools you have now (`SqlConnection‘ et al).

Here are two alternate approaches, but I don't recommend them and I'll explain why.

As always, YMMV and you should test, test, test.

You would call the GetBytes method on the SqlDataReader class to read the bytes sequentially and convert them into Int64 instances (probably through the BitConverter.ToInt64 method).

Note, in order to do this, you'd have to pass CommandBehavior.SequentialAccess to the call to Execute on the SqlCommand class so that the large binary value is streamed from the server.

That said, you'd now have to construct the large binary array to pass back in a field. This is where this approach gets hung up on, IMO. Basically, you would have to do a pivot (not a literal pivot, but something similar) taking the set of bigint values and creating a large binary value from it. This is not inherently a set operation, and you're probably going to have to have some loop code in your sproc to create this value.

With that in mind, it would seem that any gain that you get from streaming a large binary value to the client is going to be negated (and possibly made worse) by the CPU cycles that you're going to spin trying to construct that large binary value from the list of bigint.

You might be able to mitigate some of that by having a CLR stored procedure which will take the list of bigint and then create the byte array for you, but at that point, it might be overkill.

The CLR stored procedure would be faster at performing procedural operations (which this is, it's not set-based, which T-SQL is better suited for). The number of reads would initially be the same (after all, you'll have to get the data to create the binary string with) but your probably run up memory (dependent on the size of your set, since you have to concatenate it into one value) as well as CPU (due to the concatenation) before you even send your first byte back to the client, all of which takes time.

于 2012-08-03T15:22:36.427 に答える