【问题标题】:Is it better to use parameter or column value when copying data from one table to another?将数据从一个表复制到另一个表时使用参数或列值更好吗?
【发布时间】:2020-05-23 06:42:26
【问题描述】:

我有一条 SQL 语句将记录从一个表复制到另一个:

INSERT INTO [deletedItems] (
    [id],
    [shopId])
SELECT 
    [id],
    [shopId]
FROM [items]
WHERE shopId = @ShopId

@ShopId 是从我的应用程序代码调用数据库时提供给 sql 命令的参数。

如果我将其更改为直接使用提供的参数,是否会使语句执行得更好,因此 SQL 服务器不必在投影中包含 products 表中的 shopId 列?

INSERT INTO [deletedItems](
    [id],
    [shopId])
SELECT 
    [id],
    @ShopId
FROM [items]
WHERE shopId = @ShopId

直觉告诉我是的,但同时,我希望 sql server 优化第一个查询的执行计划并忽略shopId 列的投影(因为对于所有记录)并改用一个常量值。

【问题讨论】:

  • 我们不使用直觉来知道哪个“更好”。我们需要定义“更好”的含义以及您打算如何衡量它。对于性能问题,答案总是一样的。两种方式都做,然后比较执行计划。

标签: sql sql-server tsql join insert


【解决方案1】:

如果我将它更改为使用 直接提供参数

都是一样的。因为您的结果只有一个独特的ShopId 作为Where 子句。

INSERT INTO [deletedItems] (
    [id],
    [shopId])
SELECT 
    [id],
    [shopId]
FROM [items]
WHERE shopId = @ShopId -- this condition makes the `shopId` value is become unique

【讨论】:

    【解决方案2】:

    除了任何性能差异之外,我没有。缓慢的部分将通过@ShopID 或 IO 操作找到正确的项目。

    可以提高查询性能的方法是在[ShopID] 列上建立索引,其中ID 是主键或包含列。

    【讨论】:

      【解决方案3】:

      两个重点。

      SELECT(一般)中标量表达式的计算对查询性能影响不大。性能由数据移动决定。

      因此,选择“常量”与从表中选择列无关紧要。

      其次,如果您关心性能,则需要非常小心查询计划。要么强制使用索引,要么确保随着表中数据的变化定期重新编译查询。

      特别是,如果表跨越多个数据页,您希望确保查询使用items(shopId) 上的索引。

      【讨论】:

        【解决方案4】:

        我希望 sql server 能够优化 首先查询并忽略 shopId 列的投影 (因为所有记录的值都相同)并使用 而是常量值。

        不,SQL Server 不这样做。您可以通过查看执行计划和访问items 的操作员的“输出列”来验证这一点。

        在一般情况下,这不是安全的转换,可能会导致信息丢失。例如,如果源匹配行

        +--------+
        | ShopId |
        +--------+
        | A123   |
        | a123   |
        +--------+
        

        然后在不区分大小写的排序规则中,两者都将匹配相同的谓词并应插入但不同。

        如果以下情况之一适用

        • 您使用的数据类型无法做到这一点
        • 您知道这不是您的数据中的问题 - 例如。作为检查约束,确保所有数据都以修剪和大写形式存储。
        • 如果有问题,我们很乐意为所有行使用规范表示。

        然后可能会想出令人费解的场景,您的手动优化如下所示

        CREATE TABLE #T(X INT IDENTITY, Y CHAR(4000));
        
        INSERT INTO #T
        SELECT TOP 1000000 REPLICATE('A',4000)
        FROM sys.all_objects o1, sys.all_objects o2
        
        SELECT X, Y
        FROM #T
        WHERE Y = REPLICATE('A',4000)
        ORDER BY X
        
        
        SELECT X, REPLICATE('A',4000) AS Y
        FROM #T
        WHERE Y = REPLICATE('A',4000)
        ORDER BY X
        

        在第一种情况下,进入排序运算符的行的大小要大得多,因为它包括大字符串列并且排序溢出到tempdb。结果,查询执行需要更长的时间。第二个查询的内存授予请求与第一个查询相同,因为它没有考虑到该列是在排序之后计算的,但是要排序的数据较少并且不会溢出。在提供自适应内存授予反馈的 SQL Server 版本上,如果重复执行查询,则会更正过度授予。

        在大多数现实世界的场景中,我怀疑手动优化是否会产生任何实际差异,因此您应该选择能满足您需要并且感觉更清晰的任何一个,并将优化工作集中在更有希望的领域(对我来说,第二个更清晰将在所有行中插入相同的值)。

        【讨论】:

          猜你喜欢
          • 2014-10-05
          • 2020-11-03
          • 2017-06-24
          • 1970-01-01
          • 1970-01-01
          • 2017-03-28
          • 1970-01-01
          • 2023-01-12
          • 1970-01-01
          相关资源
          最近更新 更多