1

I need to perform a sequence of SQL queries in C#. The queries look similar to these:

...
select top 1 [time],[value] from [table] where [time]<='T0' and [param]='1' order by [time] desc
select top 1 [time],[value] from [table] where [time]>='T0' and [param]='1' order by [time] asc
select top 1 [time],[value] from [table] where [time]<='T1' and [param]='1' order by [time] desc
select top 1 [time],[value] from [table] where [time]>='T1' and [param]='1' order by [time] asc
select top 1 [time],[value] from [table] where [time]<='T2' and [param]='1' order by [time] desc
select top 1 [time],[value] from [table] where [time]>='T2' and [param]='1' order by [time] asc
select top 1 [time],[value] from [table] where [time]<='T3' and [param]='1' order by [time] desc
select top 1 [time],[value] from [table] where [time]>='T3' and [param]='1' order by [time] asc
...

As you can see, I request pairs of (time, value)-tuples for each time T in (T0, T1, T2, ...) where on query of each pair is the tuple "right before" and the other "right after" a certain point in time T. Each single request takes less than 1 millisecond (according to SQL Profiler in SMSS 2012 Express).

In my program (C#.NET) I perform a sequence of those queries using SqlDataReader. However, each query takes approximately 12-20 milliseconds which is far beyond what I expected and which is just not acceptable for my purposes. It seems to me that the overhead of the SqlDataReader is the problem, isn't it?

To code looks similar to this:

foreach (int x = 0; x < screen.width; ++x)
{
    time T = pixel2time(x);

    string cmd = "select top 1 [time],[value] from [table] where [time]<='" + T.ToString() + "' and [param]='1' order by [time] desc";
    SqlCommand scmd = new SqlCommand(cmd, con);

    // The time from here ...
    SqlDataReader reader = scmd.ExecuterReader();
    // ... to here takes about 12-20 milliseconds
    // the same query in SQL Profiler takes 
    // "0 milliseconds"

    if (reader.Read())
    {
        ...
    }
}

(I am plotting a time-value sequence and request for each pixel representing a certain time T on the x-axis the time-value tuple "right before" T and "right after" T to determine the y-value of this pixel. Hence, depending on the screen/window width I may have about 1000 pixels, with conequently 2 x 1000 queries, each taking ~12 milliseconds = 24 seconds. This is far too much for plotting a graph. Further, I have a sequence of 10,000,000 million entries in the database with indexing etc which should give an access time for each query of O(log n), so the database with a query time of less than one millisecond is fine. The problem is just the .NET framework, (or maybe networking?) and the issue that I can't find a more efficient solution.)

How can I solve this performance problem?


I tried/considered the following approaches:

(1) Combining the sequence to a single query to be executed by a single SqlDataReader using "union" statement didn't worked. I guess this is because of some kind incompatibility of "union" with the "order by" statement. Do you know more about this?

Edit: (Update)

select top 1 [time],[value] from [table] where [time]<='T0' and [param]='1' order by [time] desc
union all
select top 1 [time],[value] from [table] where [time]>='T0' and [param]='1' order by [time] asc

Gives the error 'Msg 156, Level 15, State 1, Line 2 Incorrect syntax near the keyword 'union'.'

Each separate query works fine. Do I have a syntax error? Thanks.

(2) I am sure that a stored procedure does not give any benefit since the time for executing a single query takes less than one millisecond in SMSS.

4

3 回答 3

1

order by使用联合是正确的方法,但是您需要使用子查询,因为整个联合只有一个,并且您应该使用union all而不是union这样它就不会删除重复项:

select [time],[value] from (select top 1 [time],[value] from [table] where [time]<='T0' and [param]='1' order by [time] desc) x
union all
select [time],[value] from (select top 1 [time],[value] from [table] where [time]>='T0' and [param]='1' order by [time] asc) x
union all
select [time],[value] from (select top 1 [time],[value] from [table] where [time]<='T1' and [param]='1' order by [time] desc) x
union all
select [time],[value] from (select top 1 [time],[value] from [table] where [time]>='T1' and [param]='1' order by [time] asc) x
union all
select [time],[value] from (select top 1 [time],[value] from [table] where [time]<='T2' and [param]='1' order by [time] desc) x
union all
select [time],[value] from (select top 1 [time],[value] from [table] where [time]>='T2' and [param]='1' order by [time] asc) x
union all
select [time],[value] from (select top 1 [time],[value] from [table] where [time]<='1453511000' and [param]='1' order by [time] desc) x
union all
select [time],[value] from (select top 1 [time],[value] from [table] where [time]>='1453511000' and [param]='1' order by [time] asc) x
于 2013-09-14T08:50:35.473 回答
1

我可以在这里建议的是尝试在某个时间创建 n 个线程并并行进行所有查询我相信您会提高查询的性能。因为这不是数据读取器问题,它取决于例如:-网络数据包 - 编组和解组结果等......我希望这可以帮助你

于 2013-09-14T08:42:40.127 回答
0

主要问题是在循环内运行查询。

  • 找到您需要的最大和最小时间值。
  • 一次查询所有积分 - SELECT time,value FROM [table] WHERE time BETWEEN [smallest]AND[largest]...
  • 循环遍历结果并计算出要为结果绘制的像素 (x) 值。

你真的需要为非常像素或时间值绘制一个点吗?如果您想坚持逐个像素的方法,那么如上所述获得所有结果会更快,并通过使用 linq 查询来获取您正在绘制的时间的值来获取点。

于 2013-09-14T08:56:56.140 回答