【问题标题】:Apply a function for all columns of a table对表的所有列应用函数
【发布时间】:2019-12-18 21:30:02
【问题描述】:

我正在尝试在 MySQL 中对表的所有列进行计算。

表格:bev

Jahr     GKZ     gesamt A   B   C    
2017    1111000 88.519  855 888 814
2017    1112000 247.943 2.414   2.379   2.262
2017    1113000 253.106 2.290   2.343   2.289
2017    1113004 43.392  408 416 403
2017    1113008 12.383  137 134 124
2017    1113012 27.106  252 252 249
2017    1113016 41.673  391 410 398
2017    1113020 39.585  364 391 373
2017    1113024 10.075  63  73  74
2017    1113028 24.083  199 205 209
2017    1113032 8.745   63  77  65
2017    1113036 18.143  170 170 143
2017    1113040 27.921  243 215 251

表:ja

GKZ  Jahr    ja_name
1001000 2017     K X
1002000 2017     K Y
5370000 2017     L Z
5370004 2017    Z1
5370012 2017    Z2
5370016 2017    Z3
5370020 2017    Z4

我已经在函数中计算了一列(第一列:gesamt):

CREATE DEFINER=`DB`@`%` FUNCTION `Total_Amount_Funct`(
bev_ID int(11), 
bev_Total int(11), 
ja_name VARCHAR(255), 
ja_jahr int(11)) RETURNS int(11)
    DETERMINISTIC
BEGIN
    DECLARE Total_Amount int(11);
    DECLARE kreis int(11);
    DECLARE Total_Sum int(11); 

    SET kreis = (bev_ID / 1000) ;
    SET Total_Sum = (SELECT  SUM(b.gesamt)
        FROM bev as b, ja as j
        WHERE b.GKZ = j.GKZ 
        AND b.Jahr = j.Jahr    
        AND j.Jahr = ja_jahr
        AND (MOD(b.GKZ, 1000) <> 0)
        AND (MOD(b.GKZ, 1000) != 0)
        AND NOT (MOD(b.GKZ, 1000) = 0)
        AND (b.GKZ BETWEEN (kreis*1000 + 1) AND (((kreis+1)*1000)-1))
        AND j.ja_name IS NOT NULL);

    SET Total_Amount = bev_Total-Total_Sum;       

    RETURN (Total_Amount);
END 

这个函数可以用下面的select调用:

SELECT DISTINCT
    bev.GKZ,
    bev.Jahr,
    bev.gesamt,
CASE WHEN (bev.GKZ % 1000 = 0) THEN
    coalesce(Total_Amount_Funct(bev.GKZ, bev.gesamt, ja.ja_name, bev.Jahr), bev.gesamt)
    ELSE bev.gesamt
END AS bev,
    ja.ja_name
FROM
    ja, bev
WHERE
    bev.GKZ = ja.GKZ
AND bev.Jahr = ja.Jahr; 

我真的很想将该函数应用于表格的所有列。也许作为存储过程?也许作为动态列。我不知道。我已经用动态列在 MS SQL 中解决了这个问题,但我觉得翻译它比尝试将函数作为存储过程完成需要更多时间。

列名可以通过以下方式获取:

SELECT column_name 
FROM information_schema.columns 
WHERE table_name='bev'
and column_name  not in ('Jahr','GKZ'); 

结果应该是:

GKZ  Jahr    gesamt  bev     ja_name    
1111000 2017    88.519  88.519   K X    
1112000 2017    247.943 247.943  K Y    
1113000 2017    253.106 101.350  L Z    
1113004 2017    43.392  43.392  Z1    
1113012 2017    27.106  27.106  Z2    
1113016 2017    41.673  41.673  Z3    
1113020 2017    39.585  39.585  Z4

【问题讨论】:

    标签: mysql stored-procedures cursor stored-functions dynamic-variables


    【解决方案1】:

    由于您仅在 SUM 中使用该列,因此您可以将列名作为参数传递并使用 CASE 语句相应地选择该列。比如:

    CREATE FUNCTION `Total_Amount_Funct`(
    bev_ID decimal(8,3), 
    bev_Total int, 
    ja_name VARCHAR(255), 
    ja_jahr int,
    in_col varchar(10)
    ) 
    RETURNS int
    DETERMINISTIC
    BEGIN
        DECLARE Total_Amount int(11);
        DECLARE Total_Sum int(11); 
    
        SELECT  
          SUM(
           case 
            when in_col='gesamt' then b.gesamt 
            when in_col='A' then b.A 
            when in_col='B' then b.B 
            when in_col='C' then b.C
           end
          ) into Total_Sum
          FROM bev as b
            join ja as j on b.GKZ = j.GKZ AND b.Jahr = j.Jahr    
          WHERE 
            MOD(b.GKZ, 1000) != 0
            AND b.GKZ BETWEEN bev_ID+1 AND bev_ID+999
            AND j.ja_name IS NOT NULL
    
        SET Total_Amount = bev_Total-Total_Sum;       
    
        RETURN (Total_Amount);
    END 
    

    然后用列名和正确的值调用函数:

    Total_Amount_Funct(bev.GKZ, bev.gesamt, ja.ja_name, bev.Jahr, 'gesamt'),
    Total_Amount_Funct(bev.GKZ, bev.A, ja.ja_name, bev.Jahr, 'A')
    ...
    

    请注意,调用进行查询的函数将使您的 SQL 序列化(在每一行上调用函数会导致在每一行上执行函数查询)。这会损害查询性能。

    【讨论】:

      【解决方案2】:

      slaakso,

      非常感谢您的回答。从今天起你就是我的偶像:-)。

      谢谢谢谢。

      我可能有一个性能问题。

      可以为表 bev 的所有列编写函数。我们可以将列名复制到一个临时表中:

        CREATE  TEMPORARY TABLE listColumns(
              Columns_ID MEDIUMINT NOT NULL AUTO_INCREMENT ,
              Columnsnamen varchar(256) ,
               PRIMARY KEY (Columns_ID)
          );
      

      从系统信息中读取:

          insert into listColumns (Columnsnamen)
          SELECT column_name 
          FROM information_schema.columns 
          WHERE table_name='bev'
          and column_name  not in ('Jahr','GKZ');
      

      这个表看起来像:

      Columns_ID  Columnsnamen
      1           gesamt
      2           A
      3           B
      4           C
      5   
      6   
      

      这样,就不必提及每个列名(该表包含大约 100 列)。也许将光标悬停在 Columns_ID 上?

      如果您对我有其他建议,那就太好了。

      谢谢你和亲切的问候

      安娜

      【讨论】:

      • 如果你有一个有 100 列的表,你可能应该看看你的表结构。如果您的表包含 value1、value2、... valueN 等列,则应将这些值放在单独的表中。如果您尝试使用来自 information_schema 的动态信息创建临时表,那么您很有可能为自己挖一个更深的洞。您可以显示您的表格结构并将问题作为新问题放入 Stack Overflow。
      • 嗨,我想我的句子表达得不好。我使用临时表只是为了收集列名以使其更易于访问。您对上述问题的建议是使用 case SELECT SUM( case when in_col='gesamt' then b.gesamt when in_col='A' then b.A when in_col='B' then b.B when in_col='C' then b.C end ) 到 Total_Sum 我的想法是用 @ 或 for 对所有列进行动态选择来替换它。你明白我的问题吗? BR
      • 不清楚您所说的 100 列是什么意思,也不清楚您要对所有这些列做什么。您应该显示您的真实表结构。您问题中的表格有 6 列。
      • 我正在尝试将上述函数应用于表格的每一列。您的建议是将 SELECT SUM(case when in_col='gesamt' then b.gesamt when in_col='A' then b.A when in_col='B' then b.B when in_col='C' then b.C end ) 写入 Total_Sum FROM bev 为b ..... SET Total_Amount = bev_Total-Total_Sum;返回(总金额);在示例中,我只使用了 4 列。 gesamt、A、B 和 C。但还有更多。理想会是这样的
      • for i in column SUM(Column_i) into Total_Sum_i FROM bev as b ..... SET Total_Amount_i= bev_Total_i - Total_Sum_i;返回(Total_Amount_i);我表示该函数正在表 bev 的所有 100 列中动态使用。这就是我将所有这些列名放在临时表中的原因。你明白吗? BR 安娜
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-12
      • 1970-01-01
      • 1970-01-01
      • 2016-05-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多