【问题标题】:Use for loop to select different columns by names in PL/SQL在 PL/SQL 中使用 for 循环按名称选择不同的列
【发布时间】:2016-03-05 23:26:20
【问题描述】:

现在我有一张这样的桌子。

表名:itemlist 项目 年份 value1 value2 value3 value4 value5 ... value99 -------------------------------------- 富1 12 32 48 富2 13 32 50 富3 14 32 50 富4 15 33 48 富5 16 33 48 富6 17 33 48 富7 13 38 42 富8 14 34 44 富9 15 36 46 富10 16 37 48

这里是任务,我要找出对于每个值(如value1、value2等),有多少项在35以上,多少项在35以下。

有没有办法循环这个过程,所以我不必从每个值列中选择所有内容。

这是选择一列的示例。

SELECT count(value1) as v1_number
FROM itemlist
WHERE v1_number > 35
UNION
SELECT v1_number
FROM itemlist
WHERE v1_number <35;

这可行,但我必须输入 99 次。

所以我的解决方案是 PL/SQL 中的 for 循环。它看起来像这样:

BEGIN
FOR i in 1..99 LOOP
SELECT count(value || i) as v_number || i
FROM itemlist
WHERE v_number || i > 35
END LOOP

显然它不起作用,问题是我不知道如何将值与索引 1-99 连接。

【问题讨论】:

  • 这是非规范化数据时遇到的问题。如果每个项目、年份和值都有一行,那么查询会很简单。
  • @eli,任何答案对您有帮助吗?你能评论或接受吗?

标签: oracle plsql oracle11g


【解决方案1】:

使用UNPIVOT syntax 创建视图。这是一次性的努力。然后在该视图上执行查询会更容易。

所以首先创建视图(扩展所有值列):

CREATE VIEW v_itemlist AS
    SELECT  *
    FROM    unpivot_test
    UNPIVOT (value FOR value_id IN (
                value1 AS 1,
                value2 AS 2,
                value3 AS 3,
                ...
                value98 AS 98,
                value99 AS 99
            ));

然后你可以做如下查询:

SELECT   item, year,
         SUM(CASE value > 35 THEN 1 END) AS above_35,
         SUM(CASE value < 35 THEN 1 END) AS below_35,
FROM     v_itemlist
GROUP BY item, year;

一旦习惯了使用视图,您可以考虑以normalised 方式创建基表,这样您就可以从索引中受益,并且可以轻松地分别插入和删除值、对它们进行排序、检测重复项等。这就是关系数据库的建模方式并提供最佳价值。

【讨论】:

    【解决方案2】:
    SELECT COUNT( CASE WHEN Value1 > 35 THEN 1 END ) AS v1_number,
           COUNT( CASE WHEN Value2 > 35 THEN 1 END ) AS v2_number,
           COUNT( CASE WHEN Value3 > 35 THEN 1 END ) AS v3_number,
           -- ...
           COUNT( CASE WHEN Value99 > 35 THEN 1 END ) AS v99_number
    FROM   itemlist;
    

    然后您可以复制/粘贴并查找/替换将&gt; 更改为&lt; 以获得以下值。

    或者您可以使用 PL/SQL 动态生成查询:

    SET SERVEROUTPUT ON;
    VARIABLE cur REFCURSOR;
    
    DECLARE
      qry  CLOB := EMPTY_CLOB();
      line_above VARCHAR2(4000);
      line_below VARCHAR2(4000);
    BEGIN
      FOR i IN 1 .. 99 LOOP
        line_above := ', COUNT( CASE WHEN Value' || i || ' > 35 THEN 1 END ) AS v' || i || '_above_number';
        line_below := ', COUNT( CASE WHEN Value' || i || ' < 35 THEN 1 END ) AS v' || i || '_below_number';
        qry := qry || line_above || line_below;
      END LOOP;
      qry := 'SELECT' || SUBSTR( qry, 2 ) || ' FROM itemlist';
      DBMS_OUTPUT.PUT_LINE( qry );
      OPEN :cur FOR qry;
    END;
    /
    
    PRINT :cur;
    

    【讨论】:

      【解决方案3】:

      我赞成 trincot 的回答,但也许你正在寻找更像这样的东西

      create or replace function eval(tab in varchar2, col in varchar2, cond in varchar2) return number as
          ret number; 
      begin
          execute immediate 'select count(*) from '||tab||' where '||col||cond into ret;
          return ret;
      end;
      

      select table_name, column_name, 
      eval(table_name, column_name, '>35'), eval(table_name, column_name, '<35') 
      from all_tab_columns 
      where table_name = upper('itemlist') and column_name like 'value%' /*and data_type = 'NUMBER'*/
      

      【讨论】:

        猜你喜欢
        • 2019-04-06
        • 2013-07-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-07-30
        • 1970-01-01
        • 1970-01-01
        • 2017-05-13
        相关资源
        最近更新 更多