【问题标题】:Oracle PLSQL escaping a single quoteOracle PLSQL 转义单引号
【发布时间】:2022-01-20 14:59:14
【问题描述】:

对于冗长的帖子,我深表歉意,但设置对于显示我的问题和提出问题是必要的。

在下面的匿名块中,我正在尝试构造一个字符串,它将表格封装在单引号中,即“T1”,但过去一个小时我一直在苦苦挣扎,可以使用一些帮助。

其次,我故意在表 partition_rention 中为表名 T2 省略了一行。我怀疑在执行语句时会将 NULL 返回到变量中。这行得通吗?

if v_days is NULL 
then  
  v_days  := 30
 END IF;

提前感谢所有回答和您的专业知识


create table partition_rention
(
   TABLE_NAME VARCHAR2(30) NOT NULL,
DAYS NUMBER(6),
CONSTRAINT Check_gt0
    CHECK (DAYS> 0)
   ); 
/

INSERT into partition_rention (TABLE_NAME, DAYS) 
 VALUES
 ('T1', 15);
/
INSERT into partition_rention (TABLE_NAME, DAYS) 
 VALUES
 ('T3', 15);
/

CREATE TABLE t1 (     
 seq_num NUMBER GENERATED BY DEFAULT AS IDENTITY (START WITH 1) NOT NULL,
   dt   DATE
)
PARTITION BY RANGE (dt)
INTERVAL (NUMTODSINTERVAL(7,'DAY'))
(
   PARTITION OLD_DATA values LESS THAN (TO_DATE('2022-01-01','YYYY-MM-DD'))
);
/

INSERT /*+ APPEND */ into t1 (dt)
with dt (dt, interv) as (
select date '2022-01-01', numtodsinterval(30,'MINUTE') from dual
union all
select dt.dt + interv, interv from dt
where dt.dt + interv < date '2022-01-15')
select dt from dt;
/

create index ix_local on t1 (dt) local;
/

CREATE TABLE t2
 (     
     seq_num NUMBER  GENERATED BY DEFAULT AS IDENTITY (START WITH 1) NOT NULL,
   dt   DATE
)
PARTITION BY RANGE (dt)
INTERVAL (NUMTODSINTERVAL(1,'DAY'))
(
   PARTITION OLD_DATA values LESS THAN (TO_DATE('2022-01-01','YYYY-MM-DD'))
);
/

INSERT /*+ APPEND */ into t2 (dt)
with dt (dt, interv) as (
select date '2022-01-01', numtodsinterval(30,'MINUTE') from dual
union all
select dt.dt + interv, interv from dt
where dt.dt + interv < date '2022-01-15')
select dt from dt;
/

create index ix_global on t2 (dt);
/

CREATE TABLE t3 (
seq_num NUMBER  GENERATED BY DEFAULT AS IDENTITY (START WITH 1) NOT NULL,
dt TIMESTAMP)
  PARTITION BY RANGE (dt) 
  INTERVAL ( NUMTODSINTERVAL (1, 'DAY') ) ( 
    PARTITION OLD_DATA VALUES LESS THAN (TIMESTAMP '2022-01-01 00:00:00.000000')
  );
/

INSERT /*+ APPEND */ into t3 (dt)
SELECT TIMESTAMP '2022-01-01 00:00:00'
         + (LEVEL - 1) * INTERVAL '5' MINUTE
         + MOD(LEVEL - 1, 10) * INTERVAL '0.1' SECOND
FROM   DUAL
CONNECT BY
       TIMESTAMP '2022-01-01 00:00:00'
         + (LEVEL - 1) * INTERVAL '5' MINUTE
         + MOD(LEVEL - 1, 10) * INTERVAL '0.1' SECOND < DATE '2022-01-15';
/

DECLARE
      v_str  VARCHAR2 (500);
       v_days  NUMBER := 0;
BEGIN 
    FOR cur_r IN(
      SELECT TABLE_NAME, PARTITIONING_TYPE, COLUMN_NAME, DATA_TYPE
FROM USER_PART_TABLES 
    JOIN USER_PART_KEY_COLUMNS ON NAME = TABLE_NAME
    JOIN USER_TAB_COLS USING (TABLE_NAME, COLUMN_NAME)
where OBJECT_TYPE = 'TABLE' AND 
PARTITIONING_TYPE='RANGE' AND
regexp_like(DATA_TYPE,'^DATE$|^TIMESTAMP*')
)
   LOOP
  --DBMS_OUTPUT.put_line('Table '|| cur_r.table_name);
 
  v_str := 'select DAYS FROM partition_rention into  v_days where TABLE_NAME = '||cur_r.table_name||'';
 

DBMS_OUTPUT.put_line(v_str);

-- execute immediate v_str;

   END LOOP;
END;

Statement processed.
select DAYS FROM partition_rention into  v_days where TABLE_NAME = T1
select DAYS FROM partition_rention into  v_days where TABLE_NAME = T2
select DAYS FROM partition_rention into  v_days where TABLE_NAME = T3

【问题讨论】:

    标签: oracle plsql escaping


    【解决方案1】:

    没有理由使用动态 SQL。应该是这样的:

    begin
        select DAYS 
        into v_days
        FROM partition_rention  
        where TABLE_NAME = cur_r.table_name;
    exception
       when NO_DATA_FOUND THEN
          v_days := 30;
    end;
    

    如果你真的坚持使用动态 SQL,那就是这个

    begin
       v_str := 'select DAYS FROM partition_rention where TABLE_NAME = :t';
       execute immediate v_str into v_days using cur_r.table_name;
    exception
       when NO_DATA_FOUND THEN
          v_days := 30;
    end;
          
    

    注意,我想下一步可​​能是删除过时的分区。为此,请查看How to drop multiple interval partitions based on date?

    【讨论】:

    • Wernfried Domscheit 我在运行您的解决方案时遇到语法错误。我在一个循环中添加了您的代码。这可能是问题吗?任何建议将不胜感激。感谢您的帮助和专业知识
    • “出现语法错误”是相当没用的!哪个错误?在哪一行?你的代码是什么?
    • Wernfried Domscheit 问题是在循环中调用异常处理程序。我在下面的答案中重组了您的解决方案。感谢您的帮助和专业知识
    【解决方案2】:

    如果 LOOP 语句位于 BEGIN 语句与其匹配的 EXCEPTION 或 END 之间;那么 END LOOP 语句也必须出现在它们之间。如果我想要一个捕获循环中可能发生的错误的异常处理程序,然后继续循环,那么异常处理程序似乎不起作用。

    对代码进行了重组以删除期望处理程序。

    我已经有一个查询可以找到我感兴趣的表和列。现在,对于该结果集中的每个表,我想从 partition_retention 表中获取匹配的天数(如果有的话),如果有的话在 partition_retention 中没有匹配的行,无论如何我都希望 table_name 具有默认值 (30) 天。为此,我实现了这样的外部 JOIN:

    
    BEGIN
        FOR td IN
        (
            SELECT      table_name
            ,      NVL (pr.days, 30) AS days
            FROM       user_part_tables      pt
              JOIN       user_part_key_columns pkc ON pkc.name = pt.table_name
              JOIN       user_tab_cols            tc  USING (table_name, column_name)
            LEFT JOIN partition_retention   pr  USING (table_name)
            WHERE       pkc.object_type      = 'TABLE'
            AND       pt.partitioning_type = 'RANGE'
            AND      REGEXP_LIKE (tc.data_type, '^DATE$|^TIMESTAMP*')
            ORDER BY  table_name -- not needed, but could be handy in debugging
        )
        LOOP
                -- For debugging:
              dbms_output.put_line ( td.table_name
                         || ' = table_name, '
                         || TO_CHAR (td.days)
                         || ' = days'
                         );
              -- call procedure to remove old PARTITIONs here.
        END LOOP;
    END;
    /
    
    Output from my sample data:
    
    T1 = table_name, 15 = days
    T2 = table_name, 30 = days
    T3 = table_name, 5 = days
    
    

    【讨论】:

      猜你喜欢
      • 2011-10-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-23
      • 2014-01-07
      相关资源
      最近更新 更多