【问题标题】:List aggregate column values when column contains customer当列包含客户时列出聚合列值
【发布时间】:2022-11-18 01:22:07
【问题描述】:

我在 ALL_TAB_COLUMNS 上运行查询以选择 column_names 其中包含名称 customer,然后输入这些表和 LISTAGG 来自这些列的值。

但是,我收到一个我无法理解的特别长的错误:

PL/SQL: ORA-00942: table or view does not exist
ORA-06550: line 10, column 1:
PL/SQL: SQL Statement ignored
ORA-06550: line 15, column 22:
PLS-00364: loop index variable 'TB' use is invalid
ORA-06550: line 15, column 1:
PL/SQL: Statement ignored
06550. 00000 -  "line %s, column %s:\n%s"

这是我尝试过的:

DECLARE
vcol VARCHAR2(128);
vtable VARCHAR(128);
BEGIN
FOR VAL IN (SELECT COLUMN_NAME, TABLE_NAME FROM ALL_TAB_COLUMNS WHERE COLUMN_NAME LIKE '%Customer%' AND DATA_TYPE in ( 'CHAR' ,  'VARCHAR2' ))
LOOP
vcol := VAL.COLUMN_NAME;
vtable := VAL.TABLE_NAME;
FOR TB IN (
WITH A AS (
  SELECT DISTINCT vcol FROM vtable
  ) SELECT LISTAGG(vcol, ',') as cols FROM A
)
LOOP
dbms_output.put_line(TB.cols);
END LOOP;
END LOOP;
END;

【问题讨论】:

    标签: sql oracle-sqldeveloper


    【解决方案1】:

    除非使用动态 SQL,否则不能使用运行时变量引用表名和列名。您可以将每个语句构造为一个字符串,然后使用该查询字符串动态打开游标:

    DECLARE
      vstmt VARCHAR2(4000);
      vresult VARCHAR2(4000);
      vcursor SYS_REFCURSOR;
    BEGIN
      FOR VAL IN (SELECT COLUMN_NAME, TABLE_NAME FROM ALL_TAB_COLUMNS WHERE COLUMN_NAME LIKE '%Customer%')
      LOOP
        vstmt := 'WITH A AS (
      SELECT DISTINCT "' || VAL.COLUMN_NAME || '" FROM "' || VAL.TABLE_NAME || '"
      ) SELECT LISTAGG("' || VAL.COLUMN_NAME || '", '','') FROM A';
    
        OPEN vcursor FOR vstmt;
        LOOP
          FETCH vcursor INTO vresult;
          EXIT WHEN vcursor%NOTFOUND;
          dbms_output.put_line(vresult);
        END LOOP;
      END LOOP;
    END;
    /
    

    或者由于您的 LISTAGG 上缺少 WITHIN GROUP 子句意味着您使用的是 19c 或更高版本,您可以将动态语句简化为:

        vstmt := 'SELECT LISTAGG(DISTINCT "' || VAL.COLUMN_NAME || '", '','') FROM "' || VAL.TABLE_NAME || '"';
    

    你也可以在没有 PL/SQL 的情况下做同样的事情,使用 XML 技巧来处理动态语句:

    select table_name, column_name,
      xmlquery('/ROWSET/ROW/C/text()'
        passing xmltype(dbms_xmlgen.getxml(
          'select listagg(distinct "' || column_name || '", '','') as c '
          || 'from "' || table_name || '"'))
      returning content) as value_list
    from all_tab_columns
    where column_name like '%Customer%';
    

    您的过滤器 where column_name like '%Customer%' 表示您已引用标识符。如果不是这种情况,则列名默认为大写;但您可以使用where upper(column_name) like '%CUSTOMER%' 搜索任何案例。

    当您从 all_tables 而不是 user_tables 查找表和列时,您实际上也应该指定所有者,无论您坚持使用 PL/SQL 还是使用 XML 方法:

    select owner, table_name, column_name,
      xmlquery('/ROWSET/ROW/C/text()'
        passing xmltype(dbms_xmlgen.getxml(
          'select listagg(distinct "' || column_name || '", '','') as c '
          || 'from "' || owner || '"."' || table_name || '"'))
      returning content) as value_list
    from all_tab_columns
    where upper(column_name) like '%CUSTOMER%';
    

    fiddle 包括一些虚拟表。

    【讨论】:

    • 我将不得不花一些时间来解开你的答案,它非常详尽,通过它我学到了很多东西!快速提问——我已经实现了你的第一个代码答案,但是得到了table or view not found,但是,我可以通过在第一个 for 循环中运行 select 查询来访问所有表和列名。有任何想法吗?
    • 嗯,只有当你正在查看另一个用户的表时才可能;以所有者为前缀(就像在我的上一个 XML 版本中一样,但您也可以在 PL/SQL 版本中这样做 - 请参阅updated fiddle)如果是这样会有所帮助。但是完全运行 vstmt 中的代码应该是一样的。 (除非您实际上使用的是过程而不是匿名块。)
    猜你喜欢
    • 2013-06-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-03
    • 2021-07-28
    相关资源
    最近更新 更多