Oracle PLSQL 转义单引号
Oracle PLSQL escaping a single quote
我很抱歉冗长 post 但设置是显示我的问题和提出问题所必需的。
在下面的匿名块中,我试图构造一个字符串,它将 table 封装在单引号中,即 'T1' 但过去一个小时我一直在努力并且可以使用一些帮助。
其次,我故意在 table partition_rention 中为 table 名称 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
没有动态的理由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;
注意,我想下一步可能是删除过时的分区。为此,请查看
如果 LOOP 语句位于 BEGIN 语句及其匹配的 EXCEPTION 或 END 之间;那么 END LOOP 语句也必须位于它们之间。如果我想要一个异常处理程序来捕获循环中可能发生的错误,然后继续循环,那么异常处理程序似乎不起作用。
代码已重组以删除期望处理程序。
我已经有一个查询可以找到我感兴趣的 table 和列。现在,对于该结果集中的每个 table,我想从中获取匹配的天数值partition_retention table 如果有,并且如果 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
我很抱歉冗长 post 但设置是显示我的问题和提出问题所必需的。
在下面的匿名块中,我试图构造一个字符串,它将 table 封装在单引号中,即 'T1' 但过去一个小时我一直在努力并且可以使用一些帮助。
其次,我故意在 table partition_rention 中为 table 名称 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
没有动态的理由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;
注意,我想下一步可能是删除过时的分区。为此,请查看
如果 LOOP 语句位于 BEGIN 语句及其匹配的 EXCEPTION 或 END 之间;那么 END LOOP 语句也必须位于它们之间。如果我想要一个异常处理程序来捕获循环中可能发生的错误,然后继续循环,那么异常处理程序似乎不起作用。
代码已重组以删除期望处理程序。
我已经有一个查询可以找到我感兴趣的 table 和列。现在,对于该结果集中的每个 table,我想从中获取匹配的天数值partition_retention table 如果有,并且如果 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