【问题标题】:Performance of separate SQL commands versus a single stored procedure单独的 SQL 命令与单个存储过程的性能
【发布时间】:2018-03-16 23:46:08
【问题描述】:

我继承了一些与 GP 接口的代码。在调试问题之前查看代码时,我发现了一个撤消对一组表的更新的函数。

该代码包含许多 SQL 命令,我想知道与在每一行上运行所有这些命令相比,我是否会从具有输入参数表 ds 的单个存储过程中获得更好的性能单独的表。

    private void revertSave()
    {
        try
        {
            SqlConnection conn = GetConnectionToGP();
            if (conn != null)
            {
                using (SqlCommand cmd = new SqlCommand())
                {
                    //records from InventoryTransaction Master, Detail, and Serial tables when window is opened.
                    foreach (DataRow dr in _ds.Tables[0].Rows)
                    {
                        cmd.Connection = conn;
                        //TRANSACTION tables
                        cmd.CommandText = "UPDATE InventoryTransaction_Master " +
                            "SET Completed = 0 WHERE DocumentNumber = " + SqlString(dr["DocumentNumber"].ToString().Trim());
                        cmd.ExecuteNonQuery();

                        cmd.CommandText = "UPDATE InventoryTransaction_Serial " +
                            "SET BIN = '' " +
                            "WHERE DocumentNumber = " + SqlString(dr["DocumentNumber"].ToString().Trim()) +
                            " and DocumentType = 1 and ItemNumber = " + SqlString(dr["PrefixedItemNumber"].ToString().Trim()) +
                            " and LineSequenceNumber = " + SqlString(dr["LineSequenceNumber"].ToString().Trim()) +
                            " and SerialNumber = " + SqlString(dr["SerialNumber"].ToString().Trim());
                        cmd.ExecuteNonQuery();

                        //TRANSFER tables
                        cmd.CommandText = "SELECT DISTINCT detail.DocumentNumber " +
                            "FROM  InventoryTransfer_Detail detail " +
                            "INNER JOIN InventoryTransfer_Serial serial ON detail.DocumentNumber = serial.DocumentNumber " +
                            "and detail.ItemNumber = serial.ItemNumber " +
                            "and detail.LineSequenceNumber = serial.LineSequenceNumber " +
                            "WHERE SessionID = " + SqlString(dr["SessionID"].ToString().Trim()) +
                            " and SerialNumber = " + SqlString(dr["SerialNumber"].ToString().Trim());
                        object obj = cmd.ExecuteScalar();
                        if (obj != null)
                        {
                            cmd.CommandText = "UPDATE InventoryTransfer_Master " +
                                "SET Completed = 0 WHERE DocumentNumber = " + SqlString(obj.ToString());
                            cmd.ExecuteNonQuery();

                            cmd.CommandText = "UPDATE InventoryTransfer_Serial " +
                                "SET OriginalBin = '', NewBin = '' " +
                                "WHERE DocumentNumber = " + SqlString(obj.ToString()) + " and SerialNumber = " + SqlString(dr["SerialNumber"].ToString().Trim());
                            cmd.ExecuteNonQuery();
                        }
                    }
                }

                if (conn.State == ConnectionState.Open)
                    conn.Close();
            }

            //this.Close();
        }
        catch (Exception ex)
        {
            ELog el = new ELog(ex, CLASS_NAME + " revertSave");
        }
    }

【问题讨论】:

  • 对代码的最大担忧是 SQL 注入,但这并不意味着存储过程就是答案。参数是一个单独的主题。
  • “显示的代码”和“存储过程”之间存在错误的二分法;您可以使用正确参数化的命令获得存储过程的所有相同性能特征,注意单个CommandText 仍然可以执行多个TSQL 操作;关于该主题的一些冗长阅读:blog.marcgravell.com/2017/12/…
  • @MarcGravell 它是在防火墙后面的终端服务器中运行的应用程序,而不是 Web 应用程序。
  • 除了完全参数化的存储过程或批处理之外,您可能还希望将语句包含在事务中以确保全或无。由于更少的日志记录,这也将提高性能。
  • @pacmaninbw 这不会改变我说的一件事:) 如果您认为 SQL 注入不适用于内部系统,那么……您需要重新考虑这一点;除此之外,我所说的关于性能的一切都适用于无论你的 SQL 注入方法

标签: c# sql sql-server performance stored-procedures


【解决方案1】:

如果不实际尝试,很难预测复杂系统的性能,但存储过程很可能会提高执行速度,尤其是在您的数据集很大的情况下。

用你现在的方法

  • 每行四个 SQL 命令。因此,对于 1000 行,将有 4000 个命令。
  • 4,000 个网络跃点
  • 4,000 个单行锁实例
  • 4,000 个 SQL 编译事件

使用存储过程,理论上你可以拥有

  • 总共三个 SQL 命令,用于关联获取必要值所需的所有行。虽然这些语句非常复杂(有很多连接),但 3 远少于 4,000。
  • 一个网络跃点
  • 一个事务,包含所有必要的行、页或潜在的表锁。
  • 如果存储过程提前编译,则根本没有编译事件。

使用存储过程,一切看起来都好多了,除了可能会更大的锁定。根据您的整体解决方案,该锁定可能会破坏交易,因为它可能会阻止尝试同时执行相同操作的其他用户的其他进程。

还有一些可怕的细节——例如,如果表很大并且有很多行您没有更新,并且批量更新触发,一次更新一行可能比进行大规模更新更有效表扫描而不是索引查找。但是,优秀的 SQL 开发人员可以定制 SQL 查询和命令,以推动 SQL Server 朝着正确的方向发展。

TLDR:在大多数情况下,存储过程将为您提供更好的性能。

【讨论】:

    【解决方案2】:

    正如其他人所说,SQL 注入和事务是问题。此代码的其他潜在问题是可维护性(易于键入、易于查找和易于调试)。这些问题中的任何一个都可能比性能更重要。看起来它可能比基于集合的逻辑做更多的基于行的逻辑,这通常是一个性能问题。如果公司习惯于在代码中构建 SQL,并且它的运行时间不会对系统或运行它的人造成负担,那么它可能是它的最佳位置(抛开SQL 注入)。

    如果这会导致实际的性能问题,则应该对其进行优化,而优化的最佳方法是进行测试。这样运行5分钟吗?当您将其转换为存储过程时,需要 1 分钟吗?当你移除循环并使其完全基于集合的逻辑时怎么样?

    【讨论】:

    • 代码中习惯性地调用存储过程。生产中存在性能问题。你对性能测试的建议很好,如果老板允许我就去做。谢谢你的回答。
    猜你喜欢
    • 1970-01-01
    • 2022-01-20
    • 2014-12-30
    • 1970-01-01
    • 2012-07-06
    • 2015-03-19
    • 1970-01-01
    • 1970-01-01
    • 2018-10-03
    相关资源
    最近更新 更多