如何将大型 SQL 服务器表拉入 C# 进行分析
How to pull large SQL server tables into C# for analysis
我需要一些关于如何最好地解决这个问题的建议。我继承了一个从现有 SQL 服务器数据库构建报告实用程序的项目。该数据库包含一个 "Raw Data" table,其中转储了每个生产数据点。报告需要提供几个数据列的平均值和Cpk。
"Raw Data"table包含25列,包括ID
列、LotID
列和TimeStamp
列,以及5包含测量数据的列。总的来说,table 似乎有 20 多万条记录,本质上是一个巨大的平面文件。
我需要做的是提供两种搜索方法;按日期范围和批号。一个lot ID最多可以容纳2M条记录。
我开始开发一个使用简单 SELECT 查询的 C# 应用程序。
SELECT *
FROM tblRawData
WHERE [LotID] = "XXXX"
然后使用SqlDataAdapter.Fill
函数填充一个DataTable
。我还尝试了 SqlDataReader
并循环将结果填充到 DataTable
中。
我看到的最大问题是 90% 以上的内存使用率(其中大部分在 SQL 服务器进程上),偶尔会出现内存不足警告,每个查询需要几分钟才能 运行.
不是 SQL 服务器大师,我正在寻求有关此方法是否合理的建议,或者我是否应该寻求不同的做法?
2000 万条记录通常不被视为 "a lot of data",除非您的服务器速度很慢或者您的数据集中有 text/blob/(n)varchar(max) 数据类型——您应该避免这种情况,如果可能的话。为了澄清 varchar(8000)/nvarchar(4000) 或更少,数据类型是可以的,因为它们不会被视为 blob 式存储(性能存储要慢得多)。
有几种方法可以优化您的方法:
- 不要"SELECT *"。只拉回您需要的字段,这将减少时间 "over the wire",您的数据从 SQL 服务器拉取并移入您的 C# 应用程序。
- 在SQL服务器上进行处理。 SQL 服务器的性能往往很高,尽管并不总是像 C# 那样高。如果您的应用程序只需要答案,请考虑使用内置的 AVG() 函数进行平均。虽然我从未做过 Cpk,但也可能有一种方法可以达到 do that in SQL。此外,您可以使用 BETWEEN 关键字来设置日期范围。
- 合理使用INDEXing。不幸的是,正确的索引几乎是一门艺术。本质上,使用尽可能少的索引。总是有一个主聚集索引,然后针对重要数据聚集的非聚集索引。索引会降低 INSERT、UPDATE 和 DELETE 操作的速度,同时提高 SELECT 上的性能(有时)。在您的情况下,您可能需要 LotID 上的索引,或 LotID 和 timestamps/date 字段的组合。
- 分块你的数据。如果可行,一次只拉出合理数量的行。在许多情况下,这是不可行的,但作为一种选择保持开放。您可以在循环中分块数据,或将数据拉入单独的结构,例如内存中的临时文件 table(表示为 @tableName)或服务器上的临时文件 table (表示为#table名称)。每个都有优点和缺点。服务器上的临时 tables 可能更适合您的问题,因为它们不会占用太多内存。
- 如果您使用的是较新版本的 SQL Server Management Studio,则有一个内置的查询 Analyzer/Optimizer。其他主要工具通常也具有此功能。它可以告诉你所有的时间都被发送到哪里,并且经常建议使用一个 INDEX。
因此,如果您必须将大量数据提取到 C# 中,您希望 SELECT 仅在索引字段上,并且只提取尽可能小的数据集。
根据我的经验,将数据拉入 C# 的所有形式都很快。这包括 SqlDataAdapter、SQLDataReader,甚至 Entity Framework 的 ORM。但是,如果您要拉回的数据集很大,那么您肯定会 运行 在较小的盒子上内存不足,并且您将不得不等待将所有这些数据从磁盘中移出——磁盘速度除了任何网络延迟之外,它还成为性能的一个重要瓶颈。如果您有权访问 SQL 服务器框的资源管理器,您可以实时查看。
我需要一些关于如何最好地解决这个问题的建议。我继承了一个从现有 SQL 服务器数据库构建报告实用程序的项目。该数据库包含一个 "Raw Data" table,其中转储了每个生产数据点。报告需要提供几个数据列的平均值和Cpk。
"Raw Data"table包含25列,包括ID
列、LotID
列和TimeStamp
列,以及5包含测量数据的列。总的来说,table 似乎有 20 多万条记录,本质上是一个巨大的平面文件。
我需要做的是提供两种搜索方法;按日期范围和批号。一个lot ID最多可以容纳2M条记录。
我开始开发一个使用简单 SELECT 查询的 C# 应用程序。
SELECT *
FROM tblRawData
WHERE [LotID] = "XXXX"
然后使用SqlDataAdapter.Fill
函数填充一个DataTable
。我还尝试了 SqlDataReader
并循环将结果填充到 DataTable
中。
我看到的最大问题是 90% 以上的内存使用率(其中大部分在 SQL 服务器进程上),偶尔会出现内存不足警告,每个查询需要几分钟才能 运行.
不是 SQL 服务器大师,我正在寻求有关此方法是否合理的建议,或者我是否应该寻求不同的做法?
2000 万条记录通常不被视为 "a lot of data",除非您的服务器速度很慢或者您的数据集中有 text/blob/(n)varchar(max) 数据类型——您应该避免这种情况,如果可能的话。为了澄清 varchar(8000)/nvarchar(4000) 或更少,数据类型是可以的,因为它们不会被视为 blob 式存储(性能存储要慢得多)。
有几种方法可以优化您的方法:
- 不要"SELECT *"。只拉回您需要的字段,这将减少时间 "over the wire",您的数据从 SQL 服务器拉取并移入您的 C# 应用程序。
- 在SQL服务器上进行处理。 SQL 服务器的性能往往很高,尽管并不总是像 C# 那样高。如果您的应用程序只需要答案,请考虑使用内置的 AVG() 函数进行平均。虽然我从未做过 Cpk,但也可能有一种方法可以达到 do that in SQL。此外,您可以使用 BETWEEN 关键字来设置日期范围。
- 合理使用INDEXing。不幸的是,正确的索引几乎是一门艺术。本质上,使用尽可能少的索引。总是有一个主聚集索引,然后针对重要数据聚集的非聚集索引。索引会降低 INSERT、UPDATE 和 DELETE 操作的速度,同时提高 SELECT 上的性能(有时)。在您的情况下,您可能需要 LotID 上的索引,或 LotID 和 timestamps/date 字段的组合。
- 分块你的数据。如果可行,一次只拉出合理数量的行。在许多情况下,这是不可行的,但作为一种选择保持开放。您可以在循环中分块数据,或将数据拉入单独的结构,例如内存中的临时文件 table(表示为 @tableName)或服务器上的临时文件 table (表示为#table名称)。每个都有优点和缺点。服务器上的临时 tables 可能更适合您的问题,因为它们不会占用太多内存。
- 如果您使用的是较新版本的 SQL Server Management Studio,则有一个内置的查询 Analyzer/Optimizer。其他主要工具通常也具有此功能。它可以告诉你所有的时间都被发送到哪里,并且经常建议使用一个 INDEX。
因此,如果您必须将大量数据提取到 C# 中,您希望 SELECT 仅在索引字段上,并且只提取尽可能小的数据集。
根据我的经验,将数据拉入 C# 的所有形式都很快。这包括 SqlDataAdapter、SQLDataReader,甚至 Entity Framework 的 ORM。但是,如果您要拉回的数据集很大,那么您肯定会 运行 在较小的盒子上内存不足,并且您将不得不等待将所有这些数据从磁盘中移出——磁盘速度除了任何网络延迟之外,它还成为性能的一个重要瓶颈。如果您有权访问 SQL 服务器框的资源管理器,您可以实时查看。