I have a table XX_LOCATION with 20 columns. Out of this I want data in only 4 columns in rest 16 I want to update the column values to null.
How can 1 update statement be used for this i.e. I don't want to write each column name of 16 columns in the update statement. Is there any other way out?
Ok, I admit. I am having a little bit of fun with this one.
BEGIN
FOR eachrec IN (SELECT column_name
FROM user_tab_cols a
WHERE table_name = 'MYTABLE'
AND column_name NOT IN ('COL1', 'COL2', 'COL3'
, 'COL4'))
LOOP
execute immediate 'update XX_LOCATION set ' || eachrec.column_name || ' = null';
END LOOP;
END;
Here is another possibility, this one with only a single execute immediate:
DECLARE
l_cmd VARCHAR2 (2000);
l_comma VARCHAR2 (1);
BEGIN
l_cmd := 'update xx_location set ';
FOR eachrec IN (SELECT column_name
FROM user_tab_cols a
WHERE table_name = 'MYTABLE'
AND column_name NOT IN ('COL1', 'COL2', 'COL3'
, 'COL4'))
LOOP
l_cmd := l_cmd || l_comma || eachrec.column_name || ' = null';
l_comma := ',';
END LOOP;
execute immediate l_cmd;
END;
And finally, a completely automated solution to exclude N columns, without having to type the non-specified column names:
CREATE TYPE column_tt IS TABLE OF VARCHAR2 (30);
CREATE OR REPLACE PROCEDURE nullify_columns (
p_owner IN all_tab_cols.owner%TYPE
, p_table IN all_tab_cols.table_name%TYPE
, p_exclude_columns IN OUT NOCOPY column_tt
)
AS
l_cmd VARCHAR2 (2000);
l_comma VARCHAR2 (1);
BEGIN
l_cmd := 'update ' || p_owner || '.' || p_table || ' set ';
FOR eachrec IN (SELECT column_name
FROM all_tab_cols a
WHERE owner = p_owner
AND table_name = p_table)
LOOP
IF NOT eachrec.column_name MEMBER OF p_exclude_columns
THEN
l_cmd := l_cmd || l_comma || eachrec.column_name || ' = null';
l_comma := ',';
END IF;
END LOOP;
EXECUTE IMMEDIATE l_cmd;
END;
CREATE TABLE totally_bogus_dude
(
col1 VARCHAR2 (10)
, col2 VARCHAR2 (10)
, col3 VARCHAR2 (10)
, col4 VARCHAR2 (10)
, col5 VARCHAR2 (10)
);
DECLARE
l_exclude column_tt := column_tt ();
BEGIN
l_exclude.EXTEND;
l_exclude (l_exclude.COUNT) := 'COL1';
l_exclude.EXTEND;
l_exclude (l_exclude.COUNT) := 'COL2';
l_exclude.EXTEND;
l_exclude (l_exclude.COUNT) := 'COL3';
l_exclude.EXTEND;
l_exclude (l_exclude.COUNT) := 'COL4';
FOR eachrec IN (SELECT COLUMN_VALUE
FROM TABLE (l_exclude))
LOOP
DBMS_OUTPUT.put_line (eachrec.COLUMN_VALUE);
END LOOP;
nullify_columns (p_owner => USER, p_table => 'TOTALLY_BOGUS_DUDE', p_exclude_columns => l_exclude);
END;
Related
Let's assume following table:
drop table test1;
create table test1(
A number(10)
);
insert into test1 values (1);
insert into test1 values (10);
So as you can see table TEST1 is already populated. What i need to do is to change types of column A to varchar2. Since this column has values we can't just use following code:
alter table test1 modify A varchar2(10);
So i have wrote stored procedure which:
Renames column A to A1 ->
Then adds new column called A of type varchar2 ->
Then updates column A with values from column A1 ->
And ultimately drops old column A1.
Code which runs this process is following:
create or replace procedure change_col_type_to_varchar2(p_tab in varchar2, p_col in varchar2)
is
v_string clob;
cursor cur is
select column_name
from all_tab_columns
where table_name = upper(p_tab) and
column_name in (select regexp_substr(p_col,'[^,]+', 1, level) from dual
connect by regexp_substr(p_col, '[^,]+', 1, level) is not null);
begin
for i in cur loop
v_string := 'alter table ' || p_tab || ' rename column ' || i.column_name || ' to ' || i.column_name || '1' || ';';
dbms_lob.append(v_string,''||chr(10)||'');
dbms_lob.append(v_string, 'alter table ' || p_tab || ' add ' || i.column_name || ' varchar2(10);');
dbms_lob.append(v_string,''||chr(10)||'');
dbms_lob.append(v_string, 'update ' || p_tab || ' set ' || i.column_name || ' = ' || i.column_name || '1' || ';');
dbms_lob.append(v_string,''||chr(10)||'');
dbms_lob.append(v_string, 'alter table ' || p_tab || ' drop column ' || i.column_name || '1' || ';');
EXECUTE IMMEDIATE v_string;
DBMS_OUTPUT.PUT_LINE(v_string);
v_string := NULL;
end loop;
end;
I'am trying to apply this procedure to TEST1:
begin
change_col_type_to_varchar2('TEST1', 'A');
end;
And get error:
Error report -
ORA-23290: This operation may not be combined with any other operation
ORA-06512: at "YAVORSKYIY_DM.CHANGE_COL_TYPE_TO_VARCHAR2", line 19
ORA-06512: at "YAVORSKYIY_DM.CHANGE_COL_TYPE_TO_VARCHAR2", line 19
ORA-06512: at line 2
23290. 00000 - "This operation may not be combined with any other operation"
*Cause: ALTER TABLE RENAME COLUMN/CONSTRAINT operation was given in
conjunction with another ALTER TBALE Operation. This is not
allowed.
*Action: Ensure that RENAME COLUMN/CONSTRAINT is the only operation
specified in the ALTER TABLE.
But just typing :
alter table test1 rename column A to A1;
alter table test1 add A varchar2(100);
update test1 set A = A1;
alter table test1 drop column A1;
Works perfect.
Does anybody have any ideas about how to overcome this problem?
Appreciate your help.
the below will do what you asked for.
declare
procedure change_col_type_to_varchar2(p_tab in varchar2, p_col in varchar2)
is
v_string clob;
cursor cur is
select column_name
from all_tab_columns
where table_name = upper(p_tab) and
column_name in (select regexp_substr(p_col,'[^,]+', 1, level)
from dual
connect by regexp_substr(p_col, '[^,]+', 1, level) is not null);
begin
for i in cur loop
v_string := 'alter table ' || p_tab || ' rename column ' || i.column_name || ' to ' || i.column_name || '1';
execute immediate v_string;
v_string := 'alter table ' || p_tab || ' add ' || i.column_name || ' varchar2(10)';
execute immediate v_string;
v_string := 'update ' || p_tab || ' set ' || i.column_name || ' = ' || i.column_name || '1' ;
execute immediate v_string;
v_string := 'alter table ' || p_tab || ' drop column ' || i.column_name || '1' ;
execute immediate v_string;
v_string := NULL;
end loop;
end;
begin
DBMS_OUTPUT.PUT_LINE('Before calling');
change_col_type_to_varchar2('TEST1','A');
DBMS_OUTPUT.PUT_LINE('After calling');
end;
Well, execute each statement alone, instead of concatenating them. And you don't need LOBs, varchar2 for each one should be enough.
I propose other algorithm.
create new table 'table2' with varchar column;
select all values from table1.A and insert to table2 with to_char() conversion;
drop table1;
rename table2 to table1;
profit!
DECLARE
CURSOR c_ddy (p_table_name VARCHAR2, p_table_name_2 VARCHAR2)
IS
(SELECT column_name
FROM ( SELECT column_name
FROM user_tab_columns
WHERE table_name = p_table_name
ORDER BY column_name));
TYPE t_content IS TABLE OF VARCHAR2 (9000)
INDEX BY BINARY_INTEGER;
a_content t_content;
l_sql VARCHAR2 (1000);
BEGIN
FOR r_ddy IN c_ddy ('TABLE_a', 'TABLE_B')
LOOP
l_sql :=
'SELECT '
|| r_ddy.column_name
|| ' FROM TABLE_A '
|| 'MINUS'
|| 'SELECT '
|| r_ddy.column_name
|| 'FROM TABLE_B';
EXECUTE IMMEDIATE l_sql BULK COLLECT INTO a_content;
DBMS_OUTPUT.put_line (l_sql);
END LOOP;
END;
Trying to output every column in TABLE_A minus every column in TABLE_B.
A space is missing in the SQL string.
Try this:
l_sql :=
'SELECT '
|| r_ddy.column_name
|| ' FROM TABLE_A '
|| ' MINUS '
|| ' SELECT '
|| r_ddy.column_name
|| ' FROM TABLE_B';
UPD: For some reason the spaces were gone before. Now I put them again.
I have an existing new table that has no columns yet. I want to copy all 10 columns in my old table. How do i do that? I don't want to drop the table so I could perform: create table newTable as select * from oldTable.
I don't want to drop the table so I could perform:
create table newTable as select * from oldTable.
Assuming your new table has at least 1 column BobC told you cannot have a table without any column, you can alter your table.
Alter new_table add (col1 varchar2(10)) , col2 ...);
Note that you need to mention all the column same as old table manually here.
If you don't want to do this manually then probably you would need a PLSQL block to do this.
CREATE OR REPLACE PROCEDURE creat_tbl_frm_tbl (tablname VARCHAR2)
AS
db_user VARCHAR2 (100) := USER;
TYPE t_rec IS RECORD
(
COLUMN_NAME VARCHAR2 (30 BYTE),
DATA_TYPE VARCHAR2 (106 BYTE),
DATA_LENGTH NUMBER
);
TYPE t_record IS TABLE OF t_rec;
-- initialization of record
v_record t_record := t_record ();
v_cntr NUMBER := 0;
v_status NUMBER;
sql_stmt VARCHAR2 (2000);
BEGIN
---dropping Already Existing temporary table
BEGIN
SELECT 1
INTO v_status
FROM all_objects
WHERE UPPER (object_name) = UPPER (tablname) AND owner = db_user;
--dbms_output.put_line( v_status );
IF v_status = 1
THEN
sql_stmt := 'drop table ' || tablname || '_new';
EXECUTE IMMEDIATE sql_stmt;
END IF;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
DBMS_OUTPUT.put_line ('Table not found--' || tablname);
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line (
'Error while dropping table-' || SQLCODE || SQLERRM);
END;
---------------------------------------------------------------------------
---- retrieving the columns of the table
BEGIN
SELECT column_name, data_type, data_length
BULK COLLECT INTO v_record
FROM all_tab_columns
WHERE table_name = UPPER (tablname)
ORDER BY column_id;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line (
'Error while retrieving the column details-' || SQLCODE || SQLERRM);
END;
FOR i IN 1 .. v_record.COUNT
LOOP
v_cntr := v_cntr + 1;
IF v_cntr = 1
THEN
IF v_record (i).data_type = 'DATE'
OR UPPER (v_record (i).data_type) = 'TIMESTAMP(6)'
THEN
sql_stmt :=
'create table '
|| ' '
|| tablname
|| '_new'
|| '('
|| v_record (i).column_name
|| ' '
|| v_record (i).data_type
|| ')';
ELSE
sql_stmt :=
'create table '
|| ' '
|| tablname
|| '_new'
|| '('
|| v_record (i).column_name
|| ' '
|| v_record (i).data_type
|| '('
|| v_record (i).data_length
|| '))';
END IF;
--dbms_output.put_line(sql_stmt);
EXECUTE IMMEDIATE sql_stmt;
ELSE
IF v_record (i).data_type = 'DATE'
OR UPPER (v_record (i).data_type) = 'TIMESTAMP(6)'
THEN
sql_stmt :=
'alter table'
|| ' '
|| tablname
|| '_new'
|| ' '
|| 'add ('
|| v_record (i).column_name
|| ' '
|| v_record (i).data_type
|| ')';
ELSE
sql_stmt :=
'alter table'
|| ' '
|| tablname
|| '_new'
|| ' '
|| 'add ('
|| v_record (i).column_name
|| ' '
|| v_record (i).data_type
|| '('
|| v_record (i).data_length
|| '))';
END IF;
--dbms_output.put_line(sql_stmt);
EXECUTE IMMEDIATE sql_stmt;
END IF;
END LOOP;
DBMS_OUTPUT.put_line ('Process Completed Successfully..!!');
END;
Output:
SQL> exec creat_tbl_frm_tbl('table2')
Process Completed Successfully..!!
PL/SQL procedure successfully completed.
SQL> select * from table2;
no rows selected
-- This is created with same columns as of passed table table2
SQL> select * from table2_new;
no rows selected
SELECT col1,col2,col3 --...
INTO newTable
FROM oldTable
I try to search a number from multiple columns (datatype number), but get ORA-01722: invalid number error.
My Query:
SELECT *
FROM CAMPAIGN
WHERE 1481125 IN (select column_name
from all_tab_columns
where table_name = 'CAMPAIGN'
AND data_type = 'NUMBER');
What is wrong with it?
You are comparing your number 1481125 with the names of the each column, not the values of each column in your table.
To go from a column's name (from dba_tab_columns) to the values in that column, you need to use some form of dynamic SQL. Here's a relatively simple example:
DECLARE
-- Since I don't have your CAMPAIGN table or data, I'm using DBA_OBJECTS in it's place.
l_table_name VARCHAR2 (30) := 'DBA_OBJECTS';
l_search_number NUMBER := 20; -- 1481125 in your example
l_record dba_objects%ROWTYPE;
l_sql VARCHAR2 (32000);
l_column_number NUMBER := 0;
l_cur SYS_REFCURSOR;
BEGIN
-- First: build dynamic SQL statement of the form:
-- SELECT * FROM table_name WHERE
-- ( ( col_name_a = 20 ) OR ( col_name_b = 20 ) OR ... )
l_sql := 'SELECT * FROM dba_objects WHERE ( ';
FOR r_number_column IN (SELECT column_name
FROM dba_tab_columns
WHERE table_name = l_table_name
AND data_type = 'NUMBER'
ORDER BY column_id) LOOP
IF l_column_number > 0 THEN
l_sql := l_sql || ' OR ';
END IF;
l_column_number := l_column_number + 1;
l_sql := l_sql || '(' || r_number_column.column_name || ' = ' || l_search_number || ')';
END LOOP;
IF l_column_number = 0 THEN
-- No number columns in table, so there should be no matches
l_sql := l_sql || ' 1=0';
END IF;
l_sql := l_sql || ')';
DBMS_OUTPUT.put_line (l_sql);
OPEN l_cur FOR l_sql;
LOOP
FETCH l_cur INTO l_record;
EXIT WHEN l_cur%NOTFOUND;
DBMS_OUTPUT.put_line ('Object Name ' || l_record.object_name || ' has search number ' || l_search_number);
END LOOP;
END;
Your query is:
SELECT * FROM CAMPAIGN WHERE 1481125 IN
(select column_name from all_tab_columns where table_name = 'CAMPAIGN' AND data_type='NUMBER')
Breaking that down we have:
SELECT * FROM CAMPAIGN WHERE 1481125 IN (<a set of numbers>)
and the subquery:
select column_name from all_tab_columns
where table_name = 'CAMPAIGN'
AND data_type='NUMBER'
That subquery is going to return a list of column names e.g.
CAMPAIGN_COUNT
CAMPAIGN_ID
CAMPAIGN_NUMBER_OF_SOMETHINGS
Your query is thus equivalent to:
SELECT * FROM CAMPAIGN WHERE 1481125 IN
('CAMPAIGN_COUNT', 'CAMPAIGN_ID', 'CAMPAIGN_NUMBER_OF_SOMETHINGS')
You can see why you would get the ORA-01722 error there?
You would need to write dynamic SQL to achieve your aim.
Can you tell me what is wrong with this procedure that I'm trying to create?
CREATE OR REPLACE PROCEDURE create_audit_tables (table_owner VARCHAR2)
IS
CURSOR c_tables (
table_owner VARCHAR2)
IS
SELECT ot.owner AS owner, ot.table_name AS table_name
FROM all_tables ot
WHERE ot.owner = table_owner
AND ot.table_name NOT LIKE 'AUDIT_%'
AND ot.table_name <> 'EXAUDIT'
AND NOT EXISTS
(SELECT 1
FROM EXAUDIT efa
WHERE ot.table_name = efa.tname)
AND NOT EXISTS
(SELECT 1
FROM all_tables at
WHERE at.table_name = 'AUDIT_'||ot.table_name);
v_sql VARCHAR2 (8000);
v_count NUMBER := 0;
v_aud VARCHAR2 (30);
BEGIN
FOR r_table IN c_tables (table_owner)
LOOP
BEGIN
v_aud := 'AUDIT_'||r_table.table_name;
v_sql :=
'create table '
|| v_aud
|| ' as select * from '
|| r_table.owner
|| '.'
|| r_table.table_name
|| ' where 1 = 1';
DBMS_OUTPUT.put_line ('Info: ' || v_sql);
EXECUTE IMMEDIATE v_sql;
v_sql :=
'alter table '
|| v_aud
|| ' add ( AUDIT_ACTION char(1), AUDIT_BY varchar2(50), AUDIT_AT TIMESTAMP)';
EXECUTE IMMEDIATE v_sql;
v_count := c_tables%ROWCOUNT;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line (
'Failed to create table '
|| v_aud
|| ' due to '
|| SQLERRM);
END;
END LOOP;
IF v_count = 0
THEN
DBMS_OUTPUT.put_line ('No audit tables created');
ELSE
DBMS_OUTPUT.put_line (v_count || ' audit tables created.');
END IF;
END;
/
I checked it with show errors function and it gave me this:
LINE/COL ERROR
-------- --------------------------------------------
6/1 PL/SQL: SQL Statement ignored
16/3 PL/SQL: ORA-00906: missing left parenthesis
Perhaps the problem is quite simple, but I can't see it, so please help me if you can
I can compile procedure without problems after creating table
create table EXAUDIT (tname varchar2(100));
Check status of procedure:
select status from user_objects where lower(object_name) = 'create_audit_tables';