SET SERVEROUTPUT ON SIZE 1000000
DECLARE
v_col_exists NUMBER;
BEGIN
SELECT COUNT (*)
INTO v_col_exists
FROM user_tab_cols
WHERE column_name = 'EFFECTIVE_DATE' AND table_name = 'MEMBERS';
IF (v_col_exists = 0)
THEN
EXECUTE IMMEDIATE 'ALTER TABLE members ADD effective_date DATE';
ELSE
DBMS_OUTPUT.PUT_LINE ('The column effective_date already exists');
END IF;
END;
Related
I have the following query, which returns the number of rows per table in a schema.
Can this also be modified to RETURN the number of columns per table too?
CREATE table stats (
table_name VARCHAR2(128),
num_rows NUMBER,
num_cols NUMBER
);
/
DECLARE
val integer;
BEGIN
for i in (SELECT table_name FROM all_tables WHERE owner = 'Schema')
LOOP
EXECUTE IMMEDIATE 'SELECT count(*) from ' || i.table_name INTO val;
INSERT INTO stats VALUES (i.table_name,val);
END LOOP;
END;
/
You can use the ALL_TAB_COLS dictionary table:
DECLARE
val integer;
BEGIN
FOR i IN (
SELECT table_name,
COUNT(*) AS num_cols
FROM all_tab_cols
WHERE owner = 'Schema'
GROUP BY table_name
)
LOOP
EXECUTE IMMEDIATE 'SELECT count(*) from ' || i.table_name INTO val;
INSERT INTO stats VALUES (i.table_name,val, i.num_cols);
END LOOP;
END;
/
db<>fiddle here
I tried to create table from all_tab_columns but it throws me an error.
like error ORA-00998: must name this expression with a column alias.
i tried to figure it out but not working.
Declare
CURSOR c1 IS
SELECT COLUMN_NAME FROM (select 'standard_hash(MY_NAME) AS MY_NAME' COLUMN_NAME from DUAL
UNION
SELECT COLUMN_NAME FROM ALL_TAB_COLUMNS WHERE TABLE_NAME='TABLE1' AND COLUMN_NAME<>'MY_NAME');
cols c1%ROWTYPE;
sqlstmt VARCHAR2(4000);
BEGIN
OPEN c1;
LOOP
FETCH c1 into cols;
EXIT WHEN c1%NOTFOUND;
sqlstmt := sqlstmt ||cols.column_name||',';
END LOOP;
CLOSE c1;
sqlstmt := 'CREATE TABLE TABLE2 AS SELECT '||substr(sqlstmt, 1, length(sqlstmt)-1)||' FROM TABLE1';
DBMS_OUTPUT.PUT_LINE(sqlstmt);
EXECUTE IMMEDIATE sqlstmt;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('error '||sqlerrm);
END;
/
You just have to give the name to the expression as follows:
sqlstmt := 'CREATE TABLE TABLE2 AS SELECT '
||substr(sqlstmt, 1, length(sqlstmt)-1)
||' as column_name FROM TABLE1'; -- column_name will bre name of the column of new table
As it will become the name of the column of newly created table.
I think this can be simplified somewhat.
You don't need to declare an explicit cursor and a record type and fetch each row into it, as this is already built into the language as the Cursor FOR loop construction:
declare
sqlStmt long := 'create table table2 as select ';
begin
for r in (
select 'standard_hash(my_name) as my_name' column_name
from dual
union
select column_name
from all_tab_columns
where table_name = 'TABLE1'
and column_name <> 'MY_NAME'
)
loop
sqlStmt := sqlStmt || r.column_name || ', ';
end loop;
sqlStmt := sqlStmt || rtrim(sqlStmt,', ') || ' from table1';
dbms_output.put_line(sqlStmt);
execute immediate sqlStmt;
end;
/
But you can do this without any loop, as the listagg function can already build a comma-separated list for you. It also makes it easier to retain the order of columns in the original table.
declare
selectList long;
sqlStmt long;
begin
select listagg(
case column_name
when 'MY_NAME' then 'standard_hash(my_name) as my_name'
else lower(column_name)
end, ', '
) within group (order by column_id) as select_list
into selectList
from user_tab_columns c
where c.table_name = 'TABLE1';
sqlStmt :=
'create table table2 pctfree 0 nologging parallel as '||chr(10)||
'select '||selectList||chr(10)||
'from table1';
dbms_output.put_line(sqlStmt||';');
execute immediate sqlstmt;
end;
I have a table which has Partitions and subpartitions. I have to first filter the results of Partition's High_Value (TO_DATE(' 2020-03-29 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GREGORIAN')) with less than some date value and then in the result set filter with Subpartion High value with some text value and if the condition satisfy's then drop that sub partition. I have written the code until given below, but not sure how to proceed further. Can someone please help on how to declare d_tmp and then loop through each subpartion and check
DECLARE
CURSOR get_parts IS
select partition_name, high_value
from ALL_TAB_PARTITIONS
where table_name = 'TempTable';
l_tmp LONG;
d_tmp DATE;
BEGIN
FOR part_rec IN get_parts
LOOP
l_tmp := part_rec.high_value;
EXECUTE IMMEDIATE 'SELECT ' || SUBSTR(l_tmp, 1, 90) || ' FROM DUAL' INTO d_tmp;
DBMS_OUTPUT.PUT_LINE( to_char(d_tmp, 'DD-MM-YYYY'));
END LOOP;
END;
You are most of the way there. You just loop around the sub partitions for that partition
DECLARE
CURSOR get_parts IS
select partition_name, high_value
from ALL_TAB_PARTITIONS
where table_name = 'TEMPTABLE';
l_tmp LONG;
d_tmp DATE;
l_tmp2 LONG;
BEGIN
FOR part_rec IN get_parts
LOOP
l_tmp := part_rec.high_value;
EXECUTE IMMEDIATE 'SELECT ' || SUBSTR(l_tmp, 1, 90) || ' FROM DUAL' INTO d_tmp;
DBMS_OUTPUT.PUT_LINE( to_char(d_tmp, 'DD-MM-YYYY'));
for i in (
select subpartition_name, high_value
from ALL_TAB_SUBPARTITIONS
where table_name = 'TEMPTABLE'
and partition_name = get_parts.partition_name
)
loop
l_tmp2 := i.high_value;
--
-- your checks
--
if [checks passed] then
execute immediate 'alter table TEMPTABLE drop subpartition '||i.subpartition_name;
end loop;
END LOOP;
END;
I've number of tables that i want to drop some columns and add some another columns again. (oracle database)
All of tables are empty.
does it work??
DECLARE
CURSOR cursor_name
IS
SELECT TABLE_NAME
FROM SYS.ALL_TABLES
WHERE OWNER = 'username';
TN NVARCHAR2 (30);
TABLE_COUNT NUMBER (3);
TCDROP NVARCHAR2 (1000);
TCADD NVARCHAR2 (1000);
BEGIN
SELECT COUNT (1)
INTO TABLE_COUNT
FROM SYS.ALL_TABLES
WHERE OWNER = 'username';
OPEN cursor_name;
FOR i IN 1 .. TABLE_COUNT
LOOP
FETCH cursor_name INTO TN;
TCDROP := 'ALTER TABLE ' || TN || ' DROP (*columns list*);';
EXECUTE IMMEDIATE TCDROP;
TCADD :=
'ALTER TABLE ' || TN || ' ADD (*columns and datatype list*);';
EXECUTE IMMEDIATE TCADD;
EXIT WHEN cursor_name%NOTFOUND;
END LOOP;
CLOSE cursor_name;
END;
/
Yes, this will certainly work. I'd recommend to put in the owner/schema of the tables, though. Besides, it is unusual or unsafe to query the number of tables and the loop through the list. I'd put it in a simple for loop:
DECLARE
stmt VARCHAR2(1000);
BEGIN
FOR t IN (SELECT table_name FROM all_tables WHERE owner='XYZ') LOOP
stmt := 'ALTER TABLE '||owner||'.'||table_name||' DROP (*columns list*);';
DBMS_OUTPUT.PUT_LINE(stmt);
EXECUTE IMMEDIATE (stmt);
stmt:= 'ALTER TABLE '||owner||'.'||table_name||' ADD (*columns and datatype list*);';
DBMS_OUTPUT.PUT_LINE(stmt);
EXECUTE IMMEDIATE (stmt);
END LOOP;
END;
/
EXECUTE immediate 'SELECT COUNT(*) INTO var_total_rows FROM '||v_table_name || ' where ENQUIRY_NO = :enq_no'
--INTO var_total_rows USING enq_no;
this is giving error of missing keyword . I am trying to count the number of rows fetched after passing the enquiry number for each table . approx 68 tables are in the output
thanks
You can try this way:
DECLARE
v_table_name VARCHAR2 (20) := 'emp';
enq_no NUMBER := 5;
var_total_rows NUMBER;
BEGIN
EXECUTE IMMEDIATE
'SELECT COUNT(*) FROM ' || v_table_name || ' where EMPNO = :enq_no'
INTO var_total_rows
USING enq_no;
DBMS_OUTPUT.put_line (var_total_rows);
END;
Edit:
This works for me. I just replaced ENQUIRY_NO with EMPLOYEE_ID since my schema doesnot have that table. Please make sure to assign value to variable enq_no
declare
var_total_rows number;
enq_no number:=1;
BEGIN
FOR c1 IN (SELECT DISTINCT table_name
FROM user_tab_columns
WHERE column_name = 'EMPLOYEE_ID' ) --'ENQUIRY_NO')
LOOP
DBMS_OUTPUT.PUT_LINE (' ');
DBMS_OUTPUT.PUT_LINE ('Table = ' || c1.table_name);
DBMS_OUTPUT.PUT_LINE ('=========================');
EXECUTE IMMEDIATE
'SELECT COUNT(*) FROM '
|| c1.table_name
|| ' where EMPLOYEE_ID = :enq_no'
INTO var_total_rows
USING enq_no;
DBMS_OUTPUT.PUT_LINE ('total count:' || var_total_rows);
END LOOP;
END;
Output:
Table = EMPLOYEE
=========================
total count:1
Table = BONUSES
=========================
total count:1
Table = BONUSESS
=========================
total count:0