【问题标题】:ORACLE - Count specific value from whole table (all columns)ORACLE - 从整个表(所有列)中计算特定值
【发布时间】:2020-03-12 20:56:50
【问题描述】:

我有一个名为“F_ParqueInfra”的表,我想计算其中值等于 -1 的所有值。

所以,这张表有 11 列和 833 行 = 9.163 条数据。

我想知道,以最简单的方式,整个表(所有列)中有多少个“-1”值。 此外,我将使用我的数据仓库中的大量表来执行此操作。

非常感谢!

【问题讨论】:

    标签: sql oracle group-by count


    【解决方案1】:

    一种选择是使用动态 SQL。例如:

    SQL> select * from f_parqueinfra;
    
    ID_USUARIO ID_EMPRESA ID_DEPARTAMENTO
    ---------- ---------- ---------------
           250         32              12
            -1         -1              -1
             0         -1               1
             5          2              -1
    
    SQL> set serveroutput on;
    SQL> declare
      2    l_table_name varchar2(30) := 'F_PARQUEINFRA';
      3    l_value number := -1;  -- search value
      4    l_str varchar2(200);   -- to compose SELECT statement
      5    l_cnt number := 0;     -- number of values in one column
      6    l_sum number := 0;     -- total sum of values
      7  begin
      8    for cur_r in (select table_name, column_name
      9                  from user_tab_columns
     10                  where table_name = l_table_name
     11                    and data_type = 'NUMBER'
     12                 )
     13    loop
     14      l_str := 'select count(*) from ' ||cur_r.table_name ||
     15               ' where ' || cur_r.column_name || ' = ' || l_value;
     16      execute immediate l_str into l_cnt;
     17      l_sum := l_sum + l_cnt;
     18    end loop;
     19    dbms_output.put_line('Number of ' || l_value ||' values = ' || l_sum);
     20  end;
     21  /
    Number of -1 values = 5
    
    PL/SQL procedure successfully completed.
    
    SQL>
    

    如果您更改l_value(第 3 行),您可以搜索其他值,例如

    SQL> l3
      3*   l_value number := -1;  -- search value
    SQL> c/-1/250
      3*   l_value number := 250;  -- search value
    SQL> /
    Number of 250 values = 1
    
    PL/SQL procedure successfully completed.
    
    SQL>
    

    或者,您可以更改表名(第 2 行)并搜索其他表。

    基本上,您可能希望将匿名 PL/SQL 代码转换为一个函数,该函数将接受表名和搜索值并返回出现次数。这应该不会太难,所以我会留给你练习。


    [编辑:将其转换为函数]

    虽然远非完美,但类似这样的方法可以让您在当前模式的表中搜索一些数字和字符串值。日期更复杂,具体取决于格式等。但是 - 对于简单的情况 - 此代码可能适合您。看看:

    SQL> create or replace function f_cnt (par_table_name in varchar2,
      2                                    par_data_type  in varchar2,
      3                                    par_value      in varchar2)
      4    return sys.odcivarchar2list
      5  is
      6    l_data_type varchar2(20) := upper(par_data_type);
      7    l_retval    sys.odcivarchar2list := sys.odcivarchar2list();
      8    l_str varchar2(200);   -- to compose SELECT statement
      9    l_out varchar2(200);   -- return value
     10    l_cnt number := 0;     -- number of values in one column
     11    l_sum number := 0;     -- total sum of values
     12  begin
     13    -- loop through all tables in current schema
     14    for cur_t in (select table_name
     15                  from user_tables
     16                  where table_name = upper(par_table_name)
     17                     or par_table_name is null
     18                 )
     19    loop
     20      -- reset the counter
     21      l_sum := 0;
     22      -- loop through all columns in that table
     23      for cur_c in (select column_name
     24                    from user_tab_columns
     25                    where table_name = cur_t.table_name
     26                      and data_type = l_data_type
     27                   )
     28      loop
     29        -- pay attention to search value's data type
     30        if l_data_type = 'VARCHAR2' then
     31           l_str := 'select count(*) from ' ||cur_t.table_name ||
     32                    ' where ' || cur_c.column_name || ' = ' ||
     33                    chr(39) || par_value ||chr(39);
     34        elsif l_data_type = 'NUMBER' then
     35           l_str := 'select count(*) from ' ||cur_t.table_name ||
     36                    ' where ' || cur_c.column_name || ' = ' || par_value;
     37        end if;
     38
     39        execute immediate l_str into l_cnt;
     40        l_sum := l_sum + l_cnt;
     41      end loop;
     42
     43      if l_sum > 0 then
     44         l_out := cur_t.table_name ||' has ' || l_sum ||' search values';
     45         l_retval.extend;
     46         l_retval(l_retval.count) := l_out;
     47      end if;
     48    end loop;
     49    return l_retval;
     50  end;
     51  /
    
    Function created.
    

    测试:

    SQL> select * From table(f_cnt(null, 'number', -1));
    
    COLUMN_VALUE
    -----------------------------------------------------------------
    F_PARQUEINFRA has 5 search values
    
    SQL> select * From table(f_cnt(null, 'varchar2', 'KING'));
    
    COLUMN_VALUE
    -----------------------------------------------------------------
    EMP has 1 search values
    
    SQL>
    

    【讨论】:

    • 很好的解决方案!如何在特定所有者上执行此操作? @GMB 的解决方案也解决了。
    • 一种选择是以“特定所有者”身份连接并在其架构中运行代码。另一种是作为特权用户(如 SYS)连接,从 DBA_TAB_COLUMNS 中选择并包含 OWNER 列。
    • 好吧,我仍然在转换为函数时遇到了一些困难。我将如何使用函数从所有表中查找所有这些计数?其中“l_table_name”等于我所有来自所有者的表(已经有这个选择语句,但我可以解决)。
    • 我编辑了我的答案并添加了更多信息和代码。看看吧。
    • 非常感谢您的努力使其发挥作用。我以前从未像这样使用过这个函数,例如,我总是在“SELECT F_CNT(*)”中使用我的函数。我也想改进这个功能,我自己尝试过,但对我的技能来说太难了。我将此功能保存在 Data Stage 数据库中,但我想在我的数据仓库上执行。我将选择语句“user_tables”和“user_tab_columns”更改为“all”,但它不起作用。我们怎么能做到这一点?此外,搜索表中字段“LINORIGEM”与“CARGA MANUAL”不同的所有行。
    【解决方案2】:

    这可能是使用unpivot 语法的好地方。这仍然需要您输入所有列名一次 - 但不会更多。

    以下是 4 列的示例:

    select count(*) cnt
    from mytable
    unpivot(myval for mycol in (col1, col2, col3, col4))
    where myval = -1
    

    作为奖励,您可以轻松修改查询以获取每列 -1 的数量:

    select mycol, count(*) cnt
    from mytable
    unpivot(myval for mycol in (col1, col2, col3, col4))
    where myval = -1
    group by mycol
    

    【讨论】:

    • 非常感谢,您的解决方案对我帮助很大!我还在测试@Littlefoot 解决方案。
    【解决方案3】:

    这应该可以满足您的需求。

    注意事项

    1. 使用内联函数执行真正的数字比较(例如也匹配 -1.00),以防止比较单元格中的值是非数字时出错。 (如果您的所有比较值都保证为数字,则可以显着简化内联函数)
    2. 仅搜索 varchar2 和 number 列类型(如果需要,可以更改)。

    代码如下:

    set serveroutput on size 10000
    declare
      vMyTableName   varchar2(200)   := 'F_ParqueInfra';
      vMyValue       number          := -1;
      vSQL           varchar2(4000);
      vTotal         pls_integer;
      vGrandTotal    number(18)    := 0;
      cursor c1 is
        select * 
        from user_tab_columns
        where table_name = vMyTableName;
      vInlineFn varchar2(4000) := 'with
      function matchesMyValue(value varchar2) return pls_integer
               is
      begin
      return case
               when to_number(value) = '||vMyValue||' then
                 1
               else 
                 0
             end;
    exception
      when value_error then
        return 0;
    end;
    ';
    begin
      for x in c1 loop
        if x.data_type in ('VARCHAR2','NUMBER') then  -- only looking in these column data types for -1 but you can flex this to suit your column type definitions
          vSQL := 'select sum(matchesMyValue('||x.column_name||')) from '||vMyTableName;
          execute immediate vInlineFn||vSQL into vTotal;
          vGrandTotal := vGrandTotal + vTotal;
        end if;
      end loop;
      dbms_output.put_line('Total cells containing -1 = '||vGrandTotal);
    end;
    /
    

    【讨论】:

      猜你喜欢
      • 2021-10-27
      • 2021-07-27
      • 1970-01-01
      • 2020-03-17
      • 1970-01-01
      • 2014-04-15
      • 1970-01-01
      • 2013-04-07
      • 1970-01-01
      相关资源
      最近更新 更多