如何将大型 SQL Server 表拉入 C# 进行分析

Posted

技术标签:

【中文标题】如何将大型 SQL Server 表拉入 C# 进行分析【英文标题】:How to pull large SQL server tables into C# for analysis 【发布时间】:2015-03-31 23:11:08 【问题描述】:

我需要一些关于如何最好地解决这个问题的建议。我继承了一个从现有 SQL Server 数据库构建报告实用程序的项目。该数据库包含一个“原始数据”表,其中转储了每个生产数据点。报告需要提供几个数据列的平均值和Cpk。

“原始数据”表包含 25 列,包括 ID 列、LotID 列和 TimeStamp 列,以及 5 列包含测量数据。总体而言,该表似乎有 20 多万条记录,本质上是一个巨大的平面文件。

我需要做的是提供两种搜索方法;按日期范围和批次 ID。一个批次 ID 可容纳多达 200 万条记录。

我开始开发一个使用简单 SELECT 查询的 C# 应用程序。

SELECT * 
FROM tblRawData 
WHERE [LotID] = "XXXX"

然后使用SqlDataAdapter.Fill 函数填充DataTable。我还尝试了SqlDataReader 并循环将结果填充到DataTable 中。

我看到的最大问题是 90% 以上的内存使用(大部分在 SQL Server 进程上),偶尔会出现内存不足警告,并且每个查询都需要几分钟才能运行。

我不是 SQL Server 专家,我正在寻求有关这种方法是否合理的建议,或者我是否应该尝试做一些不同的事情?

【问题讨论】:

您需要在系统中显示那么多记录吗?或者你只需​​要做一些简单的计算? 不要返回所有记录。返回一个迭代器,可以按顺序循环遍历记录,一次只加载一个到内存中。这就是 DataReader 的用途。 这并不是 C# 的设计初衷。 SQL 非常擅长通过其内置函数创建数据聚合,因此您不会将数百万行返回给客户端进行处理。创建一些视图或存储过程来为您处理。 【参考方案1】:

2000 万条记录通常不被视为“大量数据”,除非您的服务器速度较慢或您的数据集中有 text/blob/(n)varchar(max) 数据类型 - 如果可能,您应该避免这种情况。为了澄清 varchar(8000)/nvarchar(4000) 或更少,数据类型是可以的,因为它们不会被视为 blob 样式存储(性能存储要慢得多)。

有几种方法可以优化您的方法:

    不要“选择 *”。只拉回您需要的字段,这将减少“通过网络”从 SQL 服务器提取数据并将其移至 C# 应用程序的时间。 在 SQL 服务器上进行处理。 SQL 服务器的性能往往很高,尽管并不总是像 C# 一样高。如果您的应用程序只需要答案,请考虑使用内置的 AVG() 函数进行平均。虽然我从未做过 Cpk,但也可能有一种方法可以使用 do that in SQL。此外,您可以使用 BETWEEN 关键字设置日期范围。 合理使用INDEXing。不幸的是,正确的索引几乎是一门艺术。本质上,使用尽可能少的索引。始终拥有一个主聚集索引,然后针对重要数据聚合的非聚集索引。索引减慢了 INSERT、UPDATE 和 DELETE 操作,同时提高了 SELECT 的性能(有时)。在您的情况下,您可能需要 LotID 上的索引,或者 LotID 和时间戳/日期字段的组合。 分块您的数据。如果可行的话,一次只拉出合理数量的行。在许多情况下,这是不可行的,但可以选择将其保持打开状态。您可以在循环中分块数据,或者将数据拉入单独的结构中,例如内存中的临时表(表示为@tableName)或服务器上的临时表(表示为#tableName)。每个都有优点和缺点。服务器上的临时表可能更适合您的问题,因为它们不会占用太多内存。 如果您使用的是较新版本的 SQL Server Management Studio,则内置了查询分析器/优化器。其他主要工具通常也具有此功能。它可以告诉您所有时间都被发送到哪里,并经常建议使用一个索引。

因此,如果您必须将大量数据提取到 C# 中,您希望仅在索引字段上进行 SELECT,并且只提取可能的最小数据集。

根据我的经验,将数据拉入 C# 的所有形式都很快。这包括 SqlDataAdapter、SQLDataReader,甚至 Entity Framework 的 ORM。但是,如果您要拉回的数据集非常庞大,那么您肯定会在较小的盒子上耗尽内存,并且您将不得不等待将所有数据移出磁盘 - 磁盘速度成为一个重要的瓶颈在性能方面,除了任何网络延迟。如果您有权访问 SQL 服务器框的资源管理器,则可以实时查看。

【讨论】:

感谢您的详细解释!我将 LotID 和 TimeStamp 字段设置为非聚集索引,并看到选择查询的边际收益。然后我尝试仅在我需要的数据字段上使用 Avg 和 Stdev 函数,然后在 C# 端使用 Stdev 值执行 Cpk 计算。虽然不是闪电般快速,但查询现在已降至每手 1 到 1.5 分钟左右。 如何只拉回需要的字段? @edo101 只需选择包含您需要的数据的列,而不是所有列。从表中选择 columnNameA、columnNameB。

以上是关于如何将大型 SQL Server 表拉入 C# 进行分析的主要内容,如果未能解决你的问题,请参考以下文章

Sybase sql 在任何地方同步数据库视图(从合并到远程)?

使用 C# 拉入 Access 数据库条目

如何从 C# 代码将 SQL Server CE 迁移到 SQL Server 2012 Express

C#需要存储一个解析后的SQL Server表,使用datatable或者list

使用 Powershell 将大型 CSV 批量导入 SQL Server

如何从 c# 将 SQL Server 数据库转换/导出到 MSAccess