【问题标题】:Update a column in table based on formula given in another column Sql Server根据另一列Sql Server中给出的公式更新表中的列
【发布时间】:2020-07-08 11:16:27
【问题描述】:

我有一个有 4 列的表格:

Col1  Col2      Col3       Col4
1      2      Col1+Col2     3
3      4      Col1*Col2     12
6      3      Col1/Col2     2

Col1, Col2 会有一些值,col3 会有一个公式,基于该公式我需要在 col4 中填充值。我已经尝试使用动态 SQL 逐行运行,但我的表中有数百万条记录,并且执行脚本需要很长时间。请提出处理这种情况的最简单和最快的方法。

【问题讨论】:

  • 公式有多复杂? SQL 并不真正适合这种类型的处理。
  • 仅限普通算术运算。可能有多个列,但像 col1 +col2 /Col3 * Col4。
  • 这里没有“简单”或“快速”。您实际上是在 [Col3] 中定义一种语言,并且您必须创建一个解析器。这不是一件容易的事。
  • 你的表有主键吗?批量迭代在这里可能会有所帮助。
  • @PiyushBhardwaj this 也可能有用。

标签: sql sql-server


【解决方案1】:

如果 +,-,/,* 列中只有一个运算符,则可以为每个运算符编写更新查询

根据数据的分布情况,先批量解决简单操作,会加快处理速度


-- Bulk operation for Col1 * Col2
UPDATE Table
    Set Col4 = Col1 * Col2
WHERE LEN(Col4) - LEN(REPLACE(Col4, '*', '')) = 1 -- Only has one *
AND Like 'Col1%'
AND Like '%Col2'
-- Doesn't have other operator
AND NOT LIKE '%+%'
AND NOT LIKE '%*%'
AND NOT LIKE '%/%'

如果您有多个列/组合,您可以使用 T4 生成这些查询。

对于多个运算符,您将需要一个解析器,因为您需要注意运算符的顺序和其他规则。

【讨论】:

    【解决方案2】:

    这是适合使用游标的(非常)罕见的情况之一。事实上,这相当复杂——一个可更新的游标以及动态 SQL。

    这是一个想法:

    declare @col1 int;
    declare @col2 int;
    declare @col3 nvarchar(max);
    declare @sql nvarchar(max);
    declare @col4 int;
    
    
    DECLARE c CURSOR
    FOR SELECT col1, col2, col3 FROM t FOR UPDATE;
    
    OPEN c;
    
    FETCH NEXT FROM c INTO @col1, @col2, @col3;
    
    WHILE @@fetch_status = 0
    BEGIN
        set @sql = 'select @col4=[col3] from (select @col1 as col1, @col2 as col2) t';
        
        set @sql = replace(@sql, '[col3]', @col3);
        
        exec sp_executesql @sql, N'@col1 int, @col2 int, @col4 int output',
                           @col1=@col1, @col2=@col2, @col4=@col4 output;
    
        SELECT @col1, @col2, @col3, @col4;
        
        UPDATE t
            set col4 = @col4
            WHERE current of c;
        
        FETCH NEXT FROM c INTO @col1, @col2, @col3;
    END;
    
    CLOSE c;
    DEALLOCATE c;
    

    here 是一个数据库小提琴。

    请注意,这不会很快。获得此类查询的性能非常困难,但您可以将表分成块并使用单独的线程更新每个块。

    【讨论】:

      【解决方案3】:

      您可以更改方法并在基于集合的模式下工作,也可以使用 C# 等编程语言

      通过运行总查询,您可以计算数据的分布并使用 x 记录块以通过 Col3 更新。为了不进行锁升级,我建议一次使用 1000 行。 运行总查询示例:

      https://codingsight.com/calculating-running-total-with-over-clause-and-partition-by-clause-in-sql-server/

      现在,您可以在 C# 循环或其他编程语言中使用 Col3 作为条件以相等的块(之前计算)更新 Col4(您也可以在 SQL 中使用 while 循环,但不推荐)

      附:在运行程序之前检查您的索引

      【讨论】:

        【解决方案4】:

        如果col3 中的表达式在 SQL Server 可以理解和执行的方式上是正确的,则可以使用动态 SQL。使用 CASE 表达式创建查询。在 case 表达式中,每个分支都会检查 col3 的字符串值并返回相应的表达式。您只需要与不同的表达式一样多的分支。

        DECLARE @query nvarchar(MAX);
        
        SELECT @query = concat('UPDATE elbat', nchar(13), nchar(10),
                               '       SET col4 = CASE', nchar(13), nchar(10),
                               (SELECT DISTINCT
                                       concat('                    WHEN col3 = ''', col3, '''', nchar(13), nchar(10),
                                              '                      THEN ', col3, nchar(13), nchar(10)) [text()]
                                       FROM elbat
                                       FOR XML PATH(''),
                                               TYPE).value('.', 'nvarchar(max)'),
                               '                  END;')
               WHERE EXISTS (SELECT *
                                    FROM elbat);
        
        PRINT @query;
        
        EXEC(@query);
        

        db<>fiddle

        【讨论】:

          猜你喜欢
          • 2015-06-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-02-22
          • 2021-11-21
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多