Optimising changing column types - sql

I've got an ETL script which changes the column types of a table to desired type and updates the data. The script works perfectly but I was wondering if there was a better & quicker way of doing it? Working in Redshift.
Current procedure;
CREATE or REPLACE PROCEDURE p_alter_staging_tbls() AS $$
DECLARE
row RECORD;
BEGIN
FOR row IN select * from
(
select distinct table_name, column_name,data_type from staging.staging_col_info_v a order by a.table_name asc
)
loop
EXECUTE 'ALTER TABLE staging.' || row.table_name || ' ' || 'ADD COLUMN ' || concat('new_',row.column_name) || ' ' || row.data_type ;
EXECUTE 'UPDATE staging.' || row.table_name || ' ' || 'SET ' || concat('new_',row.column_name) || ' ' || '=' || ' ' || row.column_name || '::' || row.data_type ;
EXECUTE 'ALTER TABLE staging.' || row.table_name || ' ' || 'DROP COLUMN ' || row.column_name ;
execute 'ALTER TABLE staging.' || row.table_name || ' ' || 'RENAME COLUMN '|| concat('new_',row.column_name) || ' ' || 'TO ' || row.column_name;
END LOOP;
RETURN;
END;
$$ LANGUAGE plpgsql;

For optimization, you can check if the data type needs to be changed or if you already have the correct type and size.
For this purpose you can use a query like this:
SELECT table_schema, table_name, column_name, data_type,
column_default, character_maximum_length, numeric_precision
FROM information_schema.columns
WHERE table_schema = 'staging'
AND table_name = row.table_name
AND column_name = row.column_name
and then check if "data_type" AND "numeric_precision" is different, in this case you can alter the column.
You can also consider to not create a new column, but you can use:
ALTER TABLE table_name
ALTER COLUMN column_name [SET DATA] TYPE new_data_type;
instead of:
EXECUTE 'ALTER TABLE staging.' || row.table_name || ' ' || 'ADD COLUMN ' || concat('new_',row.column_name) || ' ' || row.data_type ;
EXECUTE 'UPDATE staging.' || row.table_name || ' ' || 'SET ' || concat('new_',row.column_name) || ' ' || '=' || ' ' || row.column_name || '::' || row.data_type ;
EXECUTE 'ALTER TABLE staging.' || row.table_name || ' ' || 'DROP COLUMN ' || row.column_name ;
execute 'ALTER TABLE staging.' || row.table_name || ' ' || 'RENAME COLUMN '|| concat('new_',row.column_name) || ' ' || 'TO ' || row.column_name;
you can use this:
EXECUTE 'ALTER TABLE staging.' || row.table_name || ' ' || 'ALTER COLUMN '|| row.column_name || ' ' || 'TYPE ' || || row.data_type;

Related

Execute immediate UPDATE Statement

I try to make an update table CHECK_COMPRESSER into PROCEDURE and I use EXECUTE IMMEDIATE :
EXECUTE immediate 'update CHECK_COMPRESSER
set NEW_SIZE_MB = '||''''||TABLE_P_ENTRY.NEW_SIZE_MB || '''' ||
' WHERE EXEC_ID = ' || '''' || EXEC_ID || '''' || ' AND TABLE = ' || '''' || TABLE_P_ENTRY.SEGMENT_NAME || '''' || ' AND PARTITION = ' || '''' || TABLE_P_ENTRY.PARTITION_NAME || '''';
dbms_output.put_line shows:
update CHECK_COMPRESSER set NEW_SIZE_MB = '182' WHERE EXEC_ID = '43' AND TABLE = 'MA_CONTACT_COMPRESS' AND PARTITION = 'P_OLD'
but there is an error:
ORA-00936: missing expression ORA-06512: at "SASDBA.COMPRESS_TABLE",
line 50
so, how should I edit this code?
TABLE is a keyword. It can be used as identifier only if quoted: "TABLE".
P.S. PARTITION is the same.

Search all columns, all tables for a specific value

I know this is a duplicate question, but I couldn't find a way to reopen the discussion. Im trying to create a stored proc that will search all columns in all tables for a value. This is what I created so far:
CLEAR SCREEN
SET VERIFY OFF
ACCEPT val CHAR PROMPT 'What value do you want to search for: '
CLEAR SCREEN;
DECLARE
match_count integer;
v_search_string varchar2(4000) := <<val>>;
BEGIN
FOR t IN (SELECT owner,
table_name,
column_name
FROM all_tab_columns
WHERE data_type in ('CHAR', 'VARCHAR2', 'NCHAR', 'NVARCHAR2',
'CLOB', 'NCLOB') )
LOOP
BEGIN
EXECUTE IMMEDIATE
'SELECT COUNT(*) FROM '||t.owner || '.' || t.table_name||
' WHERE '||t.column_name||' = :1'
INTO match_count
USING v_search_string;
IF match_count > 0 THEN
dbms_output.put_line( t.owner || '.' || t.table_name ||' '||t.column_name||' '||match_count );
END IF;
EXCEPTION
WHEN others THEN
dbms_output.put_line( 'Error encountered trying to read ' ||
t.column_name || ' from ' ||
t.owner || '.' || t.table_name );
END;
END LOOP;
END;
/
but I get errors. Any help would be highly appreciated!!
See the comments:
CLEAR SCREEN
SET VERIFY OFF
ACCEPT val CHAR PROMPT 'What value do you want to search for: '
CLEAR SCREEN;
DECLARE
match_count INTEGER;
v_search_string VARCHAR2(4000) := '&val'; /* this was <<val>> */
BEGIN
FOR t IN (SELECT owner,
table_name,
column_name
FROM all_tab_columns
WHERE data_type IN ('CHAR',
'VARCHAR2',
'NCHAR',
'NVARCHAR2',
'CLOB',
'NCLOB'))
LOOP
BEGIN
EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM ' || t.owner || '.' || t.table_name || ' WHERE ' || t.column_name || ' = :1' INTO match_count USING v_search_string;
IF match_count > 0
THEN
DBMS_OUTPUT.put_line(t.owner || '.' || t.table_name || ' ' || t.column_name || ' ' || match_count);
END IF;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line('Error encountered trying to read ' || t.column_name || ' from ' || t.owner || '.' || t.table_name);
END;
END LOOP;
END;
/

oracle dynamic query fetch cursor variables through sql

Little help needed. i have a dynamic query that outputs 4 column names and two table names into 6 cursor variables. Now i need to use the cursor variables to select the first 4 columns and then from the two table names using the cursor variables since those contain the data think something with a fetch through query using a variable that contains the query but i don’t know how to go about that. here’s what i have now i just need to fetch the cursor variables and runt hem into a query
DECLARE
arow VARCHAR2 (1000);
column1 VARCHAR2 (50);
column2 VARCHAR2 (50);
column3 VARCHAR2 (50);
column4 VARCHAR2 (50);
table1 VARCHAR2 (50);
table2 VARCHAR2 (50);
match VARCHAR2 (50);
match1 VARCHAR2 (50);
sql_statement VARCHAR2 (500);
BEGIN
FOR arow IN (SELECT column_name_old,
column_name_new,
column_name_old_2,
column_name_new_2,
table_name_old,
table_name_new
FROM A550003.META_DATA_TABLE)
LOOP
sql_statement :=
'INSERT'
|| ' '
|| 'INTO'
|| ' '
|| 'a550003.MATCH_TABLE'
|| ' '
|| 'SELECT '
|| arow.column_name_old
|| ', '
|| arow.column_name_new
|| ', '
|| 'DECODE( '
|| arow.column_name_old
|| ', '
|| arow.column_name_new
|| ','
|| '1'
|| ','
|| '0)'
|| 'AS'
|| ' '
|| 'MATCH'
|| ','
|| arow.column_name_old_2
|| ', '
|| arow.column_name_new_2
|| ','
|| 'DECODE( '
|| arow.column_name_old_2
|| ', '
|| arow.column_name_new_2
|| ','
|| '1'
|| ','
|| '0)'
|| 'AS'
|| ' '
|| 'MATCH1'
|| ' FROM '
||' '
|| arow.table_name_old
|| ', '
|| arow.table_name_new
|| ' WHERE '
|| arow.column_name_old
|| '='
|| arow.column_name_new
|| '(+)';
DBMS_OUTPUT.PUT_LINE (sql_statement);
EXECUTE IMMEDIATE sql_statement;
COMMIT;
END LOOP;
END;
First of all you sql_statement is wrong. You should set the value of this variable to sth like this (when you will get all needed names):
sql_statement := 'SELECT '
|| column1
|| ', '
|| column2
|| ', '
|| column3
|| ', '
|| column4
|| ' FROM '
|| table1
|| ', '
|| table2
|| ' WHERE '
|| -- JOIN_CONDITION
And then you can use EXECUTE IMMEDIATE statement:
EXECUTE IMMEDIATE sql_statement
INTO col1_val
,col2_val
,col3_val
,col4_val
;
Of course variables col[1..4]_val have to be declared.
Also watch out for SQL Injection.
It must be this:
sql_statement := 'SELECT '
|| column1 || ', ' || column2 || ', ' || column3 || ', ' || column4
|| ' FROM ' || table1 || ', ' || table2
|| ' WHERE ' || column1 ||'=' ||column2||'(+)';
EXECUTE IMMEDIATE sql_statement INTO col1_val, col2_val, col3_val, col4_val;
or preferably
sql_statement := 'SELECT '
|| column1 || ', ' || column2 || ', ' || column3 || ', ' || column4
||' FROM '||table1||' LEFT OUTER JOIN '||table2||' ON '||column1||'='||column2;
EXECUTE IMMEDIATE sql_statement INTO col1_val, col2_val, col3_val, col4_val;
For debugging DBMS_OUTPUT.PUT_LINE (sql_statement); will be usefull!
Then you don't need a Ref-Cursor, simply do:
BEGIN
For aRow in (SELECT * FROM a550003.meta_data_table) LOOP
sql_statement := 'SELECT '||aRow.column1||','||aRow.column2 ...
END LOOP;
END;

Oracle PL/SQL cursor update

I'm using oracle. My SQL skills are very bad, I want to update information from a query that I have obtained through the use of a cursor, I've read about using the WHERE CURRENT OF statement, but I don't see how that can fit into my current code. Does anyone mind lending a helping hand? I want to allow a calling program to update a row in the cursor (I want to update the race location) returned by the query in my current code. Here's my code so far:
DECLARE
l_race_rec race%rowtype;
CURSOR Query1
IS
SELECT *
FROM RACE
WHERE Race_Time='22-SEP-14 12.00.00.000000000';
BEGIN
OPEN Query1;
LOOP
FETCH query1 INTO l_race_rec;
EXIT WHEN query1%notfound;
dbms_output.put_line( l_race_rec.raceid || ', ' || l_race_rec.race_location || ', ' ||
l_race_rec.race_type || ', ' || l_race_rec.race_time || ', ' || l_race_rec.sex || ', ' ||
l_race_rec.minage || ', ' || l_race_rec.maxage );
END LOOP;
CLOSE Query1;
END;
Here's an example to get you going:
DECLARE
l_race_rec race%rowtype;
CURSOR Query1 IS
SELECT *
FROM RACE
WHERE Race_Time = '22-SEP-14 12.00.00.000000000';
nSome_value NUMBER := 42;
BEGIN
OPEN Query1;
LOOP
FETCH query1 INTO l_race_rec;
EXIT WHEN query1%notfound;
dbms_output.put_line(l_race_rec.raceid || ', ' ||
l_race_rec.race_location || ', ' ||
l_race_rec.race_type || ', ' ||
l_race_rec.race_time || ', ' ||
l_race_rec.sex || ', ' ||
l_race_rec.minage || ', ' ||
l_race_rec.maxage );
UPDATE RACE
SET SOME_FIELD = nSome_value
WHERE CURRENT OF QUERY1;
END LOOP;
CLOSE Query1;
END;
Share and enjoy.
Why don't you use a cursor for loop.
...
for row in query1
loop
dbms_output.put_line(row.raceid || ', ' ||
row.race_location || ', ' ||
row.race_type || ', ' ||
row.race_time || ', ' ||
row.sex || ', ' ||
row.minage || ', ' ||
row.maxage );
UPDATE RACE
SET SOME_FIELD = nSome_value
WHERE CURRENT OF QUERY1;
end loop;
...
In this way there no need to open and to close a cursor.
Keep in mind that a cursor for loop works better for a cursor with more than 1 row as result.
Good luck.

Formatted Input in Table in plsql

I want to input the following things -
ACCEPT p_cname PROMPT 'Enter Customer Name: '
ACCEPT p_cyear PROMPT 'Enter Car Year: '
ACCEPT p_color PROMPT 'Enter Car Color: '
ACCEPT p_make PROMPT 'Enter Car Make: '
ACCEPT p_model PROMPT 'Enter Car Model: '
ACCEPT p_trim PROMPT 'Enter Car Trim: '
ACCEPT p_enginetype PROMPT 'Enter Car Engine Type: '
ACCEPT p_option PROMPT 'Enter Option Name: '
ACCEPT p_ocode PROMPT 'Enter Option Code: '
then add it in table as
NAME - WANT
Name - year color model trim enginetype 'w/' Option ( ocode )
I tried to format it using -
INSERT INTO table
VALUES ('&p_cname', '&p_cyear' || ' ' || '&p_color' || ' ' || '&p_make' || ' ' || '&p_model' ||
|| ' ' || '&p_trim' || ' ' || '&p_enginetype' || ' ' || '&p_option' || '(' || '&p_ocode' || ')');
BUT IT DOED NOT WORK.
The error you pasted into comments doesn't correspond to the code in your question. If I execute the code I get:
SQL> #test
Enter Customer Name: a
Enter Car Year: b
Enter Car Color: c
Enter Car Make: d
Enter Car Model: e
Enter Car Trim: f
Enter Car Engine Type: g
Enter Option Name: h
Enter Option Code: i
old 2: VALUES ('&p_cname', '&p_cyear' || ' ' || '&p_color' || ' ' || '&p_make' || ' ' || '&p_model' ||
new 2: VALUES ('a', 'b' || ' ' || 'c' || ' ' || 'd' || ' ' || 'e' ||
old 3: || ' ' || '&p_trim' || ' ' || '&p_enginetype' || ' ' || '&p_option' || '(' || '&p_ocode' || ')')
new 3: || ' ' || 'f' || ' ' || 'g' || ' ' || 'h' || '(' || 'i' || ')')
|| ' ' || 'f' || ' ' || 'g' || ' ' || 'h' || '(' || 'i' || ')')
*
ERROR at line 3:
ORA-00936: missing expression
SQL>
That's because you have two concatenation operators missing it's second operand:
'&p_model' || || ' ' || '&p_trim'
Eliminate the extra operator and the insert works:
'&p_model' || ' ' || '&p_trim'