【问题标题】:Generate Dynamic Column Value SQL生成动态列值 SQL
【发布时间】:2021-01-28 17:34:49
【问题描述】:

我有两张桌子。我需要根据配置表列形成一个比率。由于 100 只有 PROFIT 和 LOSS 列的比率将是 PROFIT:LOSS。这需要动态 SQL。

我试着写,但我不知道如何继续。你能建议一下吗?

配置表:

表格 - A100

输出:我需要根据表1和表2形成一个比例

我试过的查询:

    WITH col as (SELECT  COLUMN_NAME
             FROM   config
             WHERE TABLE_NAME='A100' 
            ) ,
            
 fetch_col as (    SELECT DISTINCT CUSTOMER,COLUMN_NAME,SALES,PROFIT,LOSS,DIVIDEND
                   FROM A100 CROSS JOIN dba_tab_columns 
                   WHERE COLUM_NAME IN (SELECT COLUMN_NAME FROM col)    
            )
   SELECT  CUSTOMER,PROFI||':'||LOSS
            CASE WHEN a.COLUMN_NAME=b.COLUMN_NAME THEN PROFIT||':'||LOSS ELSE '' END         
   FROM fetch_col a INNER JOIN col b
   ON a.COLUMN_NAME=b.COLUMN_NAME

【问题讨论】:

  • 我看不出表 1 与此有什么关系。
  • 我需要读取表 1 以检查要使用哪些列来形成比率,然后读取表 2 以形成基于它的比率
  • 所以您的配置表将始终有 2 个表值 - A100?由于口粮仅包含 2 个值。为什么你使用了 distinct?
  • 是 DISTINCT 不需要,现在更新。它只有两个值
  • 你怎么知道它应该是 'PROFIT:LOSS' 而不是 'LOSS:PROFIT' - 配置表是否有另一列指示订单?并且“由于 100 仅...”是否意味着某些表在配置表中的列将超过两列;如果是这样,您期望会发生什么?或者...您是否专门在配置表中寻找利润和损失,而忽略其他所有内容?目前尚不清楚您是否正在查看配置以获取列名;或只寻找固定名称。

标签: sql oracle oracle11g


【解决方案1】:

我已尝试解决您的问题。可能还不适合。

我已将“config”表扩展为“column_type”列,以识别哪个条目代表分子和哪个分母。

CREATE OR REPLACE PACKAGE pkg_demo
AS
   TYPE r_out IS RECORD
      ( table_name   VARCHAR2(30),
        customer     VARCHAR2(30),
        ratio        VARCHAR2(30)
      );
   TYPE t_out IS TABLE OF r_out;

   FUNCTION get_ratio ( i_table  IN VARCHAR2 )
      RETURN t_out PIPELINED;
   
END pkg_demo;
/

CREATE OR REPLACE PACKAGE BODY pkg_demo
AS

   FUNCTION get_ratio ( i_table  IN VARCHAR2 )
      RETURN t_out PIPELINED
   AS
      l_num config.column_name%TYPE;
      l_den config.column_name%TYPE;
      l_sql VARCHAR2(32767);
      TYPE cur_type IS REF CURSOR;
      l_cur cur_type;
      l_rec r_out;
   BEGIN
      SELECT config.column_name
        INTO l_num
        FROM config
       WHERE table_name = i_table
         AND column_type = 'N';
      
      SELECT config.column_name
        INTO l_den
        FROM config
       WHERE table_name = i_table
         AND column_type = 'D';

      l_sql := 'SELECT ''' || TRIM(LEADING 'A' FROM i_table) || ''', customer, TO_CHAR(' || l_num || ') ||'':''|| TO_CHAR('|| l_den || ', ''FM00'') FROM ' || i_table;
      
      OPEN l_cur FOR l_sql;
      LOOP
         FETCH l_cur INTO l_rec;
         EXIT WHEN l_cur%NOTFOUND;
         
         PIPE ROW(l_rec);
      END LOOP;
      
      CLOSE l_cur;
         
   END get_ratio;
   
END pkg_demo;
/

SELECT * FROM TABLE(pkg_demo.get_ratio('A100'));

结果:

TABLE_NAME  CUSTOMER  RATIO                         
----------- --------- ---------
100         Microsoft 10:01                         
100         Tesla     15:02     

【讨论】:

    【解决方案2】:

    您可以使用DBMS_XMLGEN.GETXML 在单个 SQL 语句中生成动态列:

    --Convert the XML to relational data.
    select table_name, customer, ratio
    from
    (
        --Convert the SQL statement into XML results.
        select XMLType(dbms_xmlgen.getXML(sql)) results
        from
        (
            --Generate a SQL statement with column names based on configuration.
            select table_name,
                replace(replace(replace(
                    q'[select '#TABLE_NAME#' table_name, customer, #COLUMN_1# || ':' || #COLUMN_2# ratio from #TABLE_NAME#]'
                , '#TABLE_NAME#', table_name)
                , '#COLUMN_1#', max(column_name) keep(dense_rank first order by column_order))
                , '#COLUMN_2#', max(column_name) keep(dense_rank first order by column_order desc)) sql
            from config
            group by table_name
        )
    )
    cross join
    xmltable
    (
        '/ROWSET/ROW'
        passing results
        columns
            table_name varchar2(128) path 'TABLE_NAME',
            customer   varchar2(128) path 'CUSTOMER',
            ratio      varchar2(128) path 'RATIO'
    );
    

    有关可运行示例,请参阅 this db<>fiddle

    关于此解决方案的几点说明:

    1. 此 XML 技巧仅在您拥有一定数量的动态列时才有效。
    2. 您希望尽可能避免这种动态解决方案。 SQL 中的动态 SQL 有时是不可避免的,但通常有一种方法可以改进您的数据模型以使 SQL 更简单。或者也许这种类型的动态代码最好由应用程序处理。
    3. 正如 Thomas Kirchhoff 指出的那样,该解决方案需要通过某种方式知道哪个列在前,因此我添加了一个 COLUMN_ORDER 值。
    4. 与 Thomas 的解决方案相比,我的解决方案要小一些,并且不需要创建额外的对象。另一方面,我的解决方案更神秘,运行速度会更慢。哪个答案最好可能取决于您使用这些查询的频率。如果这是一个罕见的查询,使用 DBMS_XMLGEN 并完成它会更简单。如果这是一个常见查询,则值得创建和维护一个包来简化查询。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-12-29
      • 1970-01-01
      • 2023-03-26
      • 1970-01-01
      • 1970-01-01
      • 2012-06-11
      • 1970-01-01
      • 2022-07-22
      相关资源
      最近更新 更多