【发布时间】:2019-09-10 09:36:45
【问题描述】:
作为问题的标题,我遇到了与从 MS-SQL 读取数据有关的问题。 我使用 DataReader 编写了一个 C# 代码,用于从查询中检索数据返回并将数据保存到 CVS。它非常简单,我很确定这段代码没有问题。
我有两个问题。第一个查询是内部连接 4 个表的结果。 第二个查询使用 Row_Num 和 select 语句中的子查询。它们都返回 24 列。 第一个查询返回 321k 条记录。第二个查询返回 2300 条记录。 我使用的是 SQL Server 2008。
问题是当我使用第一个查询运行时,它运行得很快。 321k 条记录保存到 CVS 文件,需要 8 分钟才能完成。但是使用第二个查询,将 2300 条记录保存到文件需要 26 小时。我输入了登录代码,发现一些记录需要 40 - 50 分钟才能读取数据,我不知道为什么。
请看一下并给我一些建议。
下面是我的代码和我做了什么
using (var connection = new SqlConnection(connectionstring))
using (var command = new SqlCommand(secondQuery, connection))
{
connection.Open();
try
{
command.CommandTimeout = 0;
command.CommandType = CommandType.Text;
var reader = command.ExecuteReader();
using (var memoryStream = ExtendedStream.CreateMemoryStream())
using (TextWriter writer = new StreamWriter(memoryStream, Encoding.UTF8, 65536)) {
while (reader.Read())
{
// using Nlog to log start time begin read data and log what happen on code
// read property value and save to file
// using Nlog to log end time read data
}
}
reader.Dispose();
reader.Close();
}
catch (SqlException ex)
{
throw ex;
}
finally
{
command.Dispose();
connection.Dispose();
connection.Close();
}
}
第二次查询
WITH TEMP AS
(
SELECT ROW_NUMBER() OVER (PARTITION BY SId,PointID ORDER BY UTCSysTime) Record_No, * FROM
(SELECT
*
FROM
Table_A WITH (NOLOCK)
WHERE
StatusID IN (1, 2)
AND [UTCSysTime] >= @StartTime
AND [UTCSysTime] <= @EndTime
UNION
SELECT
E.*
FROM
Table_A E WITH (NOLOCK)
INNER JOIN (SELECT
SId,
PointID,
StatusID,
UTCSysTime = MIN(UTCSysTime)
FROM
Table_A WITH (NOLOCK)
WHERE
StatusID IN (1, 2)
AND [UTCSysTime] > @EndTime
GROUP BY
SId,
PointID,
StatusID
) E2
ON E.SId = E2.SId
AND E.PointID = E2.PointID
AND E.StatusID = E2.StatusID
AND E.UTCSysTime = E2.UTCSysTime
) T
)
SELECT
*,
[PointShortName] = CONVERT(VARCHAR(255), (SELECT TOP 1
ShortName
FROM
Table_B _T
WITH (NOLOCK)
WHERE
_T.PointID = Table_T.PointID
)),
[Description_Point] = CONVERT(VARCHAR(2000), (SELECT TOP 1
_T.Name
FROM
Table_Point _T
WITH (NOLOCK)
WHERE
_T.PointID = Table_T.PointID
AND _T.DescID = 1
)),
[Description_1] = CONVERT(VARCHAR(255), (SELECT TOP 1
Name
FROM
Table_C
WITH (NOLOCK)
WHERE
DescID = 1
AND CatID = [Table_T].[Cat1]
)),
[Description_2] = CONVERT(VARCHAR(255), (SELECT TOP 1
Name
FROM
Table_C
WITH (NOLOCK)
WHERE
DescID = 1
AND CatID = [Table_T].[Cat2]
)),
[Description_3] = CONVERT(VARCHAR(255), (SELECT TOP 1
Name
FROM
Table_C
WITH (NOLOCK)
WHERE
DescID = 1
AND CatID = [Table_T].[Cat3]
)),
[Description_4] = CONVERT(VARCHAR(255), (SELECT TOP 1
Name
FROM
Table_C
WITH (NOLOCK)
WHERE
DescID = 1
AND CatID = [Table_T].[Cat4]
))
FROM
(SELECT
*
FROM
(SELECT
a.*
,UTCSysTime_End = ISNULL(CASE WHEN b.StatusID = 1
THEN b.UTCSysTime
END,
CASE WHEN c.StatusID = 1
THEN c.UTCSysTime
END)
FROM TEMP a LEFT OUTER JOIN TEMP b ON a.Record_no = b.Record_no - 1 and a.SId = b.SId and a.PointID = b.PointID
LEFT OUTER JOIN TEMP c ON a.Record_no = c.Record_no - 2 and a.SId = c.SId and a.PointID = c.PointID) E
WHERE
E.StatusID = 2
AND [UTCSysTime] >= @StartTime
AND [UTCSysTime] <= @EndTime
) [Table_T]
WHERE
[UTCSysTime] > @StartTime
AND [UTCSysTime] <= @EndTime
我尝试在 SSMS 上运行查询,它们都运行良好。我尝试使用 SQL Profiler,但我的帐户没有权限。所以我把日志放到源代码中。我看到一些记录需要 50-60 分钟才能读取和写入文件。
日志是:
2019-09-09 07:48:40.5242 |调试 |结束写入行号 226 |例外:
2019-09-09 07:48:40.5522 |调试 |开始写第 227 行 |例外:
2019-09-09 07:48:40.5802 |调试 |结束写入行号 227 |例外:
2019-09-09 07:48:40.6072 |调试 |开始写第 228 行 |例外:
2019-09-09 07:48:40.6352 |调试 |结束写入行号 228 |例外:
2019-09-09 07:48:40.6632 |调试 |开始写第 229 行 |例外:
2019-09-09 08:20:50.3977 |调试 |结束写入行号 229 |例外:
2019-09-09 08:20:50.4337 |调试 |开始写第 230 行 |例外:
2019-09-09 08:20:50.4667 |调试 |结束写入行号 230 |例外:
2019-09-09 08:20:50.5047 |调试 |开始写第 231 行 |例外:
2019-09-09 08:20:50.5387 |调试 |结束写入行号 231 |例外:
【问题讨论】:
-
不知道您的选择语句,我只能建议检查 SQL Profiler 和 SSMS 执行计划
-
你有子选择和row_num?看起来该查询可以使用一些索引/统计信息(对于 row_num)并且应该消除子查询。
-
"...and Select in select method" 在内部,第二个查询中的 SQL Server 正在执行:1 选择主数据块,对 row_num 进行表扫描,以及 2300 子查询的额外选择语句(“选择中的选择”)。如果您在第二条 SQL 语句方面需要帮助,请编辑您的问题并添加它。检查minimal reproducible example
-
尝试使用
reader.GetOrdinal("CloumnName")对抗reader[i]可能会对性能产生巨大影响。 -
it take 26 hours to save 2300 records to files生成的文件有多大?
标签: c# sql datareader sqlperformance