【问题标题】:Oracle: Trying to loop thru insert statement using dynamic list of table namesOracle:尝试使用动态表名列表循环插入语句
【发布时间】:2014-11-17 06:29:04
【问题描述】:

我不太了解此处找到的解决方案: Selecting Values from Oracle Table Variable / Array?

我有一个表名列表。我想将它们作为一个数组循环,使用它们的值作为表进行搜索。

TMP_DORMANT_FILTERS 物理表的表名。下面的数组是同一个列表。 LM_DORMANT_EMAIL 是电子邮件地址列表。

我想检查表格列表中是否存在休眠电子邮件地址。我意识到我可以编写 12 次相同的查询来搜索每个表。但这不会提高我的 SQL 技能。

这是我的数组尝试。在这次尝试中,Oracle 不喜欢我在 where not exists sql 中调用数组值的方式:

DECLARE 
TYPE array_t IS VARRAY(12) OF VARCHAR2(25);

ARRAY array_t := array_t('BT_ABANDONED_HIST', 'BT_BROWSED_HIST', 'BT_PURCHASED_HIST',       'CM_ABANDONED_HIST', 'CM_BROWSED_HIST', 'CM_PURCHASED_HIST', 'CM_PAGE_VIEWS_HIST', 'MB_ABANDONED_HIST', 'MB_BROWSED_HIST', 'MB_CARTED_HIST', 'MB_PAGE_VIEWS_HIST', 'MB_PURCHASED_HIST');

BEGIN
FOR i IN 1..array.count LOOP
INSERT INTO TMP1_DORMANT_EMAIL
SELECT feed.EMAIL_ADDRESS
FROM LM_DORMANT_EMAIL feed
WHERE NOT EXISTS (
        SELECT 1 FROM array(i) hist
            WHERE ACTIVITY_DATE >= TRUNC(SYSDATE - 90)
            AND hist.EMAIL = feed.EMAIL_ADDRESS
        );

COMMIT;

END LOOP;

END;
/

或者使用上面链接中的解决方案,我试过了。 Oracle 无法识别我在开始部分下插入 dormant_filters 的内容。它告诉我我的物理表 TMP_DORMANT_FILTERS 不存在:

CREATE GLOBAL TEMPORARY TABLE dormant_filters
( filters varchar2(100)
)
ON COMMIT DELETE ROWS;

BEGIN 
INSERT INTO dormant_filters
  ( filters )
  ( SELECT TABLE_NAMES 
      FROM TMP_DORMANT_FILTERS 
  ); 

 FOR j IN ( SELECT filters FROM dormant_filters ) LOOP 
    INSERT INTO TMP1_DORMANT_EMAIL
        SELECT feed.EMAIL_ADDRESS, j as DORMANT_SOURCE
        FROM LM_DORMANT_EMAIL feed
        WHERE NOT EXISTS (
            SELECT 1 FROM j hist
            WHERE feed.ACTIVITY_DATE >= TRUNC(SYSDATE - 90)
            AND hist.EMAIL = feed.EMAIL_ADDRESS
            );
        NULL;
END LOOP; 
COMMIT;
END; 
/

【问题讨论】:

    标签: oracle


    【解决方案1】:

    这个问题需要动态 SQL。绑定变量可以用于值,但不能用于对象。

    declare 
        type array_t is varray(12) of varchar2(25);
        array array_t := array_t('BT_ABANDONED_HIST', 'BT_BROWSED_HIST', 'BT_PURCHASED_HIST', 'CM_ABANDONED_HIST', 'CM_BROWSED_HIST', 'CM_PURCHASED_HIST', 'CM_PAGE_VIEWS_HIST', 'MB_ABANDONED_HIST', 'MB_BROWSED_HIST', 'MB_CARTED_HIST', 'MB_PAGE_VIEWS_HIST', 'MB_PURCHASED_HIST');
    begin
        for i in 1 .. array.count loop
            execute immediate '
                INSERT INTO TMP1_DORMANT_EMAIL
                SELECT feed.EMAIL_ADDRESS
                FROM LM_DORMANT_EMAIL feed
                WHERE NOT EXISTS (
                        SELECT 1 FROM '||array(i)||' hist
                            WHERE ACTIVITY_DATE >= TRUNC(SYSDATE - 90)
                            AND hist.EMAIL = feed.EMAIL_ADDRESS
                        )
            ';
            commit;
        end loop;
    end;
    /
    

    更新

    如果每个表的列名不同,您可以使用数据字典来选择正确的列名。

    declare 
        type array_t is varray(12) of varchar2(25);
        array array_t := array_t('BT_ABANDONED_HIST', 'BT_BROWSED_HIST', 'BT_PURCHASED_HIST', 'CM_ABANDONED_HIST', 'CM_BROWSED_HIST', 'CM_PURCHASED_HIST', 'CM_PAGE_VIEWS_HIST', 'MB_ABANDONED_HIST', 'MB_BROWSED_HIST', 'MB_CARTED_HIST', 'MB_PAGE_VIEWS_HIST', 'MB_PURCHASED_HIST');
        v_column_name varchar2(30);
    begin
        for i in 1 .. array.count loop
            select column_name
            into v_column_name
            from all_tab_columns
            where owner = 'SCHEMA NAME'
                and table_name = array(i)
                and column_name in ('ACTIVITY_TIME','DATE_ABANDONED');
    
            execute immediate '
                INSERT INTO TMP1_DORMANT_EMAIL
                SELECT feed.EMAIL_ADDRESS
                FROM LM_DORMANT_EMAIL feed
                WHERE NOT EXISTS (
                        SELECT 1 FROM '||array(i)||' hist
                            WHERE '||v_column_name||' >= TRUNC(SYSDATE - 90)
                            AND hist.EMAIL = feed.EMAIL_ADDRESS
                        )
            ';
            commit;
        end loop;
    end;
    /
    

    【讨论】:

    • 感谢 Jon 提供的解决方案。这是工作。还有一件小事。几个表中的日期列的名称与其他表不同。为了避免写两个 sql 块,我尝试了以下方法,但它不起作用... WHERE DECODE('||k||',1,ACTIVITY_TIME,2,ACTIVITY_TIME,DATE_ABANDONED) >= TRUNC(SYSDATE - 90) decode 语句似乎不起作用。 Oracle 告诉我 DATE_ABANDONED 是无效标识符。但我数组中的前两个表使用 ACTIVITY_DATE,其余的使用 DATE_ABANDONED。
    • 我的sql是正确的,列名是ACTIVITY_TIME,而不是我之前发布的ACTIVITY_DATE。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-31
    相关资源
    最近更新 更多