oracle 中的动态列使用 sql

dynamic columns in oracle using sql

我有以下 table 的例子。 Thera可以无限分行和客户。我需要将这些分支机构分组并计算他们的客户数量,然后用不同的列显示它。

BRANCHNAME  CUSTOMERNO
100         1001010
100         1001011
103         1001012
104         1001013
104         1001014
104         1001015
105         1001016
105         1001017
106         1001018

注意可以有无限的分支和客户,查询必须不仅在这种情况下有效。

在这种情况下,接受的结果是:

100 103 104 105 106
 2   1   3   2   1

示例SQL数据

    select '100' BranchName,'1001010' CustomerNo from dual   UNION ALL 
    select '100' BranchName,'1001011' CustomerNo from dual   UNION ALL 
    select '103' BranchName,'1001012' CustomerNo from dual   UNION ALL 
    select '104' BranchName,'1001013' CustomerNo from dual   UNION ALL 
    select '104' BranchName,'1001014' CustomerNo from dual   UNION ALL 
    select '104' BranchName,'1001015' CustomerNo from dual   UNION ALL 
    select '105' BranchName,'1001016' CustomerNo from dual   UNION ALL 
    select '105' BranchName,'1001017' CustomerNo from dual   UNION ALL 
    select '106' BranchName,'1001018' CustomerNo from dual   

这将在行(而不是列)中获取它:

SELECT branchname,
       COUNT( DISTINCT customerno ) AS customers
FROM   your_table
GROUP BY branchname;

(注意:如果 branchnamecustomerno 对永远不会重复,则可以省略 DISTINCT 关键字。)

在不知道分支名称是什么的情况下,你只能做 dynamic pivot

获取上述查询的输出(以行格式)并将其转置为您用来访问数据库的任何 front-end。

来自评论:

I need a report in this format, and don't want write some application , wants to do with sql for easily export to excell in such format

不,您不需要 SQL 中的列格式。您可以将其以行格式放入 excel,然后使用 excel 的 TRANSPOSE 函数将其(非常简单)转换为列,而无需实现复杂的动态 SQL解决方案。

我认为写一个pipelined table function that returns a variable structure是可能的,虽然很复杂。您的管道 table 函数将在运行时使用 Oracle Data Cartridge 接口和 AnyDataSet 类型的魔力 return 动态结构。然后,您可以在后续的 SQL 语句中使用它,就好像它是 table,即

SELECT *
  FROM TABLE( your_pipelined_function( p_1, p_2 ));

讨论相同示例实现的更多参考资料

  • Dynamic SQL Pivoting
  • Oracle Data Cartridge 开发人员指南的 Implementing the Interface Approach 部分
  • Method4. 下载并安装开源PL/SQL代码后,这里是一个完整的实现:

    --Create sample table.
    create table branch_data as
    select '100' BranchName,'1001010' CustomerNo from dual   UNION ALL 
    select '100' BranchName,'1001011' CustomerNo from dual   UNION ALL 
    select '103' BranchName,'1001012' CustomerNo from dual   UNION ALL 
    select '104' BranchName,'1001013' CustomerNo from dual   UNION ALL 
    select '104' BranchName,'1001014' CustomerNo from dual   UNION ALL 
    select '104' BranchName,'1001015' CustomerNo from dual   UNION ALL 
    select '105' BranchName,'1001016' CustomerNo from dual   UNION ALL 
    select '105' BranchName,'1001017' CustomerNo from dual   UNION ALL 
    select '106' BranchName,'1001018' CustomerNo from dual;
    
    --Create a dynamic pivot in SQL.
    select *
    from table(method4.dynamic_query(
        q'[
            --Create a select statement
            select
                --The SELECT:
                'select'||chr(10)||
                --The column list:
                listagg(
                    replace(q'!sum(case when BranchName = '#BRANCH_NAME#' then 1 else 0 end) "#BRANCH_NAME#"!', '#BRANCH_NAME#', BranchName)
                    , ','||chr(10)) within group (order by BranchName)||chr(10)||
                --The FROM:
                'from branch_data' v_sql
            from
            (
                --Distinct BranchNames.
                select distinct BranchName
                from branch_data
            )
        ]'
    ));
    

如果你只是想在某处报告结果,你可以对 select 语句使用游标:

select branchname, count(*) from test group by branchname order by branchname asc;

通过光标循环你可能会得到你的值。

这是我的样本:

declare
  v_b varchar2(1000);
  v_t varchar2(1000);
begin
  for i in (select branchname, count(*) total from test group by branchname order by branchname asc)
  loop
      v_b := v_b || i.branchname || ' ';
      v_t := v_t || i.total || '   ';     
  end loop;

  dbms_output.put_line(v_b);
  dbms_output.put_line(v_t);
end;

您可以使用此选择:

SELECT branchname, count(*) 
FROM test 
GROUP BY branchname

总的来说branchname中的每一个数字都使用选择是不专业的。

这个解决方案怎么样。没有table创建,直接设置v_sql参数即可。

SET SERVEROUTPUT ON SIZE 100000

DECLARE
   v_cursor    sys_refcursor;

   CURSOR get_columns
   IS
      SELECT EXTRACTVALUE (t2.COLUMN_VALUE, 'node()') VALUE
        FROM (SELECT *
                FROM TABLE (XMLSEQUENCE (v_cursor))) t1,
             TABLE (XMLSEQUENCE (EXTRACT (t1.COLUMN_VALUE, '/ROW/node()'))) t2;

   v_column    VARCHAR2 (1000);
   v_value     VARCHAR2 (1000);
   v_counter   NUMBER (3)      := 0;
   v_sql       VARCHAR2 (4000);
BEGIN
   v_sql :=
         'SELECT   branchname, COUNT (DISTINCT customerno) AS customers'
      || ' FROM (SELECT 100 branchname, 1001010 customerno'
      || ' FROM DUAL'
      || ' UNION ALL'
      || ' SELECT 100 branchname, 1001011 customerno'
      || ' FROM DUAL'
      || ' UNION ALL'
      || ' SELECT 103 branchname, 1001012 customerno'
      || ' FROM DUAL'
      || ' UNION ALL'
      || ' SELECT 104 branchname, 1001013 customerno'
      || ' FROM DUAL'
      || ' UNION ALL'
      || ' SELECT 104 branchname, 1001014 customerno'
      || '   FROM DUAL'
      || ' UNION ALL'
      || ' SELECT 104 branchname, 1001015 customerno'
      || '  FROM DUAL'
      || ' UNION ALL'
      || ' SELECT 105 branchname, 1001016 customerno'
      || '   FROM DUAL'
      || ' UNION ALL'
      || ' SELECT 105 branchname, 1001017 customerno'
      || '   FROM DUAL'
      || ' UNION ALL'
      || ' SELECT 106 branchname, 1001018 customerno'
      || '   FROM DUAL)'
      || ' GROUP BY branchname';

   OPEN v_cursor FOR v_sql;

   FOR v_record IN get_columns
   LOOP
      IF v_counter = 0
      THEN
         v_column := v_column || v_record.VALUE || ' ';
         v_counter := 1;
      ELSIF v_counter = 1
      THEN
         v_value := v_value || v_record.VALUE || '   ';
         v_counter := 0;
      END IF;
   END LOOP;

   DBMS_OUTPUT.put_line (v_column);
   DBMS_OUTPUT.put_line (v_value);
END;
/

输出为

100 105 104 103 106 
2   2   3   1   1  
with src as
(select '100' BranchName,'1001010' CustomerNo from dual   UNION ALL 
select '100' BranchName,'1001011' CustomerNo from dual   UNION ALL 
select '103' BranchName,'1001012' CustomerNo from dual   UNION ALL 
select '104' BranchName,'1001013' CustomerNo from dual   UNION ALL 
select '104' BranchName,'1001014' CustomerNo from dual   UNION ALL 
select '104' BranchName,'1001015' CustomerNo from dual   UNION ALL 
select '105' BranchName,'1001016' CustomerNo from dual   UNION ALL 
select '105' BranchName,'1001017' CustomerNo from dual   UNION ALL 
select '106' BranchName,'1001018' CustomerNo from dual )
SELECT * FROM
(select BranchName from src)
PIVOT XML 
(COUNT(*) FOR (BranchName) 
IN 
(SELECT DISTINCT BranchName FROM SRC))

此查询以 xml 格式提供输出。整个 xml 数据将包含在查询结果的字段中(查询只有单行单列输出)。下一步是解析 xml 数据并以表格形式显示。