【问题标题】:Npgsql: nested sql commands on one connectionNpgsql:一个连接上的嵌套 sql 命令
【发布时间】:2019-05-29 09:21:15
【问题描述】:

我正在将应用程序从 firebird 移植到 postgres,并且有一堆嵌套的 sql 命令。第一个命令是运行第二个命令所必需的,有些函数有 5 或 6 个嵌套命令,它们都使用相同的连接。我只是想知道是否有一种方法可以在一个连接上完成所有这些操作,而无需重写整个事情。

    static void NestedCommandsOnOneConnection()
    {
        using (NpgsqlConnection connection = new NpgsqlConnection(ConnectionString))
        {
            using (NpgsqlCommand command = new NpgsqlCommand("SELECT * FROM tableA", connection))
            {
                using (NpgsqlDataReader reader = command.ExecuteReader())
                {
                    using (NpgsqlCommand command2 = new NpgsqlCommand("SELECT * FROM tableB where column1 = @column1", connection))
                    {
                        command2.Parameters.AddWithValue("@column1", reader["column1"]);

                        using (NpgsqlDataReader reader2 = command2.ExecuteReader())
                        {
                            while (reader2.Read())
                            {
                                //Do things
                            }
                        }
                    }
                }
            }
        }
    }

//编辑:如果我这样做会更好吗?

    static void NestedCommandsOnOneConnection()
    {
        using (NpgsqlConnection connection = new NpgsqlConnection(ConnectionString))
        {
            var column1 = "";
            using (NpgsqlCommand command = new NpgsqlCommand("SELECT * FROM tableA LIMIT 1", connection))
            {
                using (NpgsqlDataReader reader = command.ExecuteReader())
                {
                    while(reader.Read())
                    {
                       column1 = reader["column1"].ToString();
                    }

                }
            }
            using (NpgsqlCommand command2 = new NpgsqlCommand("SELECT * FROM tableB where column1 = @column1", connection))
            {
                command2.Parameters.AddWithValue("@column1", column1);

                using (NpgsqlDataReader reader2 = command2.ExecuteReader())
                {
                    while (reader2.Read())
                    {
                        //Do things
                    }
                }
            }
        }
    }

当第二个命令被执行时,我收到错误消息“命令已经在进行中:SELECT * FROM tableA”那么有什么方法可以做到这一点而不必为每个命令建立连接?

【问题讨论】:

  • 那些不是嵌套命令。 ADO.NET 没有这样的东西。没有什么能阻止您使用同一连接执行多个命令。您的代码正在尝试从多个开放阅读器中读取。即使在支持这个的数据库中(带有 MARS 的 SQL Server),这也需要特殊处理
  • 你想做什么?编写一个 single 查询将多个表连接在一起并仅返回您需要的结果会更快 很多
  • 此代码可以替换为执行SELECT * from TableB where column1 in (SELECT column1 from TableA)单个 命令。性能可能会比缓慢的客户端循环好几倍
  • sn-p 代码只是一个例子,不要只发布我正在使用的代码,因为这会让你们感到困惑。我只是想知道是否可以使用一个连接来做类似剪辑的事情
  • 此代码显示了错误的数据访问模式。 为什么你想让两个阅读器同时打开?那些应该是只向前的firehose(即快速)阅读器,而不是循环。不管是什么原因,代码都应该修改,这样就不需要了

标签: c# postgresql npgsql


【解决方案1】:

问题在于,当您在第一个命令/阅读器的上下文中时,会为此建立连接。在您从第一个命令/阅读器释放连接之前,您无法运行第二个命令,依此类推。

解决方案是将要获取的数据加载到列表中,并根据该列表进行迭代并运行第二个命令/读取器。在第二个命令/阅读器上,您还将结果加载到另一个列表中,并对其进行迭代。

最后,我的建议是让您评估您想要从数据库中获取的数据以及您正在执行的查询。您的想法是正确的,但它会增加数据库的巨大过载,这会在其他并发用户尝试从同一个表中获取数据时产生问题。

【讨论】:

  • 导致问题的是阅读器,而不是命令。 OP 的代码试图同时创建和使用多个阅读器。多个ExecuteNonQueryExecuteScalar 调用不会导致任何问题
  • 事实上,代码看起来像是在尝试模拟tableAtableB 之间的连接。所有这些都可以替换为 JOIN 或 SELECT * from TableB where column1 in (SELECT column1 from TableA)
【解决方案2】:

要添加到上述@paulo-correia 的回复,打开第二个连接可能没问题,具体取决于您的用例。假设您正在使用池并且您并不缺少连接(即大规模并发场景),这样做没有任何问题,并且可以节省缓冲第一个查询结果所需的客户端内存。但是请注意,如果两个查询需要在一个事务中,这是不可能的。

【讨论】:

  • 问题是由阅读器引起的,而不是命令引起的。没有理由打开多个连接,除非 OP 想要同时读取不同的结果。但这不会提高性能,因为代码必须从每个读取器一次读取一行。
猜你喜欢
  • 2016-01-05
  • 1970-01-01
  • 2019-07-01
  • 2022-08-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-29
  • 1970-01-01
相关资源
最近更新 更多