C#,Npgsql,NpgsqlCommandBuilder和传输的数据

时间:2015-04-10 12:14:17

标签: c# npgsql

我正在开发一个连接到远程Postgresql数据库的C#应用​​程序。 数据库大小约为50-60Mb(这大约是" data \ base"文件夹的大小和&#34返回的大小; select pg_database_size")但是如果我执行&# 34; SELECT * FROM"在所有表格上,通过局域网传输的数据大约为600Mb(十倍大!)。

我检查过大多数数据传输是由于CommandBuilder

NpgsqlCommandBuilder cBuilder = new NpgsqlCommandBuilder(_dAdapter);
_dAdapter.DeleteCommand = cBuilder.GetDeleteCommand();
_dAdapter.InsertCommand = cBuilder.GetInsertCommand();
_dAdapter.UpdateCommand = cBuilder.GetUpdateCommand();

问题出在哪里?有一种方法可以最大限度地减少执行" SELECT *"查询?

2 个答案:

答案 0 :(得分:0)

这与C#无关,与Npgsql几乎无关。

与它相关的不是与之相比。

一方面,您拥有数据库的内部存储空间。这是磁盘上的数据,其设计方式主要是快速查询和更新,以及尽可能占用少量磁盘空间的目标。特别是,使用TOAST存储的所有大值都在内部压缩。

有关详情,请参阅Database Physical Storage上的文档。

你说这个大约是50-60 Mb(虽然我猜你可能意味着MB)在你的情况下。

另一方面,您拥有数据库的前端/后端协议,这是postgresql服务器和客户端之间的通信方式。

虽然减少所涉及的字节数也是一个目标,但所涉及的应用程序的翻译也很容易。表示不同值之间的边界的问题是完全不同的,如果你要通过电线,还有SSL开销等等。

有关详情,请参阅Frontend/Backend Protocol上的文档。

因此,虽然存储在文件中的一些内容与此无关,但我们希望完整传输的大小要大得多。你说600Mb(再次,我猜你的意思是MB),所以匹配。

  

问题出在哪里?

没有问题。

  

有一种方法可以最大限度地减少执行" SELECT *"查询?

好吧,有人希望免费提供一点,因为Npgsql团队正在通过上次检查时更优化地使用协议来减少传输大小。 (几年前我为Npgsql做出了贡献,但是现在我偶尔会看看他们最近会做些什么。)

这种优化将与其他优化工作一起值得做,但它仍然不会产生很大的不同:即使你手动优化了所有前端/后端协议的使用(即使你这样做的优势在于,通过先前的实际数据知识,您可以在一种方法有时会导致较小的转移而其他时间更大的情况下做出完美的选择。这样做的尺寸仍然会大得多{ {1}}在所有表上,而不是存储中涉及的大小。*

除此之外,降低执行SELECT *成本的最佳方法是不这样做:

  1. 首先避免抓住整张桌子;工程师尽可能只为特定用途选择有意义的数据。

  2. 如果您确实需要抓住所有内容,请使用SELECT *。 Npgsql支持这种对SQL的postgresql扩展,并且针对批量传输而非查询进行了优化。

  3. 使用COPY可能会或可能不会有意义地变小,因此值得了解它如何与您的特定数据集合展开。

    *更大的数量取决于索引占用了多少空间,包含在TOAST中压缩的大值的数据库节省了多少,因此可能存在传输大小实际上更小的情况比存储大小,但这将是存储数据的副作用,而不是人们可能故意设计的。

答案 1 :(得分:-1)

我做了很多研究,并自己找到了解决方法。现在我将做很多测试来检查这是否是一个稳定的解决方案。

我的旧代码看起来像这样:

_dAdapter = new NpgsqlDataAdapter("SELECT * FROM myTable", this.DatabaseConnection);

NpgsqlCommandBuilder cBuilder = new NpgsqlCommandBuilder(_dAdapter);
_dAdapter.DeleteCommand = cBuilder.GetDeleteCommand();
_dAdapter.InsertCommand = cBuilder.GetInsertCommand();
_dAdapter.UpdateCommand = cBuilder.GetUpdateCommand();

_dAdapter.FillSchema(_dataTable, SchemaType.Mapped);
_dAdapter.Fill(_dataTable);

我已经确认此代码执行了三次我的" SELECT *"命令,所以表内容从服务器传输到客户端三次:一个是NpgsqlCommandBuilder的第一个GetXXXXCommand,一个是FillSchema命令,另一个是Fill命令(这是我真正需要的)。

我的解决方法是使用"虚拟"选择一个不可能的查询"其中"子句,不提取数据,只提取表元数据,并在Fill命令之前设置实际查询,所以我的新代码是:

string temp = query.ToLower();

// Building my "dummy" query
if ((temp.StartsWith("select *")) && (!temp.Contains("where")) && (!temp.Contains("order")))
    temp += " where false";

_dAdapter = new NpgsqlDataAdapter(temp, this.DatabaseConnection);

NpgsqlCommandBuilder cBuilder = new NpgsqlCommandBuilder(_dAdapter);
_dAdapter.DeleteCommand = cBuilder.GetDeleteCommand();
_dAdapter.InsertCommand = cBuilder.GetInsertCommand();
_dAdapter.UpdateCommand = cBuilder.GetUpdateCommand();

_dAdapter.FillSchema(_dataTable, SchemaType.Mapped);

_dAdapter.SelectCommand.CommandText = query; // NOTE THIS!

_dAdapter.Fill(_dataTable);

我会用我的测试结果评论这篇文章。