I got the task to update a rather big file that consists of many update and delete statements.
First things first would be to check, which statements are actually needed / used.
I would like to spool this into a separate file, but it is difficult to get it into a nice format.
For example:
set serveroutput on
spool xxx.csv
update xx set yy where a = b;
Creates a file like:
sql: update xx.....
1100 rows updated.
The closest i got to my desired output is to use something like:
spool xxx.csv
select 'update xx set yy where a = b;' query, count(x) count from xx where (update where clause)
This would work mostly well (except for multiline queries), but it would require me to rewrite all update / delete statements and there are a lot.
Does anyone have an idea how I could solve this the best way?
The best outcome would be a file like:
Query Count
update xx ... 1100
Thanks in advance!
You can use SQL%ROWCOUNT and the below select to achieve your requirement
DECLARE
l_sql_text VARCHAR2(32767);
l_sql_count NUMBER;
BEGIN
insert into tttt values(4);
l_sql_count:= SQL%rowcount;
SELECT
(
SELECT t2.sql_fulltext
FROM v$sql t2
WHERE t1.prev_sql_id = t2.sql_id
AND t1.prev_child_number = t2.child_number ) prev_sql_fulltext
INTO l_sql_text
FROM v$session t1
WHERE t1.audsid = Sys_context('userenv', 'sessionid');
dbms_output.put_line('Query,Count');
dbms_output.Put_line(l_sql_text
||','
||l_sql_count);
END;
Output
Query,Count
INSERT INTO TTTT VALUES(4),1
Related
The view V$FIXED_VIEW_DEFINITION contains the definition of all V$ views. However, the datatype is VARCHAR2(4000), so only the first 4000 characters of the view's body are displayed:
SELECT column_name, data_type, data_length
FROM dba_tab_cols
WHERE table_name = 'V_$FIXED_VIEW_DEFINITION';
COLUMN_NAME DATA_TYPE DATA_LENGTH
--------------- --------- -----------
VIEW_NAME VARCHAR2 128
VIEW_DEFINITION VARCHAR2 4000
CON_ID NUMBER 22
This works fine for views which are shorter, but for others, like V$SESSION, the query text stops right after character 4000:
SELECT * FROM v$fixed_view_definition WHERE view_name = 'GV$SESSION';
VIEW_NAME VIEW_DEFINITION
---------- --------------------------------------------
GV$SESSION select s.inst_id, ... ,decode(bitand(s.ksuse
here it stops abruptly ^
Now, obviously, Oracle has stored the full text somewhere, and the function DBMS_UTILITY. EXPAND_SQL_TEXT T reconstructs the full query, but not without doing strange things to the query like quoting all columns, introducing alias etc:
DECLARE
c CLOB;
i NUMBER := 1;
linelen CONSTANT NUMBER := 100;
BEGIN
DBMS_UTILITY.EXPAND_SQL_TEXT('SELECT * FROM V$SESSION', c);
LOOP
EXIT WHEN i > DBMS_LOB.GETLENGTH(c);
DBMS_OUTPUT.PUT_LINE(DBMS_LOB.SUBSTR(c, linelen, i));
i := i + linelen;
END LOOP;
END;
/
SELECT "A1"."SADDR" "SADDR","A1"."SID" "SID","A1"."SERIAL#" "SERIAL#","A1"."AUDS
ID" "AUDSID","A1"."PADDR" "PADDR","A1"."USER#" "USER#","A1"."USERNAME" "USERNAME
...
So, where is the rest of the V$ query bodies?
For the sake of having a clean answer, it sounds like you discovered the full fixed view source queries compiled into the Oracle binary by using
strings $ORACLE_HOME/bin/oracle
and grepping the output for a specific view.
Burleson had provided a hint that the fixed view synonyms are created by the $ORACLE_HOME/rdbms/admin/ catalog scripts (specifically cdfixed.sql).
try this,
spool C:\Users\test\Downloads\VW_DDL.txt
SET LONG 200000 LONGCHUNKSIZE 500000 PAGESIZE 0 LINESIZE 1000 FEEDBACK OFF VERIFY OFF TRIMSPOOL ON
SELECT DBMS_METADATA.get_ddl ('VIEW', view_name, 'SYSADM')
FROM all_views
WHERE owner = 'SYSADM'
AND view_name IN (' ');
SET PAGESIZE 14 LINESIZE 100 FEEDBACK ON VERIFY ON
spool off
I have an Oracle (11g) database with a table with about 200 millions of records. I have to modify two columns of each row in the table. What would be the most efficient solution for this?
I tried using a code similiar to the following, but after a while the execution crashed due to ORA-04030 - Out of process memory.
BEGIN
FOR i IN (SELECT * FROM FOO WHERE BAR > 2000)
LOOP
-- Do the processing and update row
END LOOP;
END;
I can't change any OS-level parameters. I have to solve this using the code.
Since I don't know the exact column modifications, I'll just make something up; lets say I need to change the name to be lowercase but the first letter in caps and that I have to give a salary rise depending on job.
This is the most efficient way.
Let's assume I have a 200M row MYEMP table.
create table temp_emp
as select * from myemp
where 1=2;
alter session enable parallel dml
insert /*+ APPEND */ into temp_emp
select
EMPNO
, initcap(ENAME)
, JOB
, MGR
, HIREDATE
, case when job = 'MANAGER' then SAL * 1.1 else SAL * 1.05 end
, COMM
, DEPTNO
from myemp
;
drop table myemp
;
you could rename rather than drop MYEMP, if you need to be "safe" and
double check
rename temp_emp to myemp;
A quick test, with 14million rows, took 16s, using serial execution. By employing database parallelism, the runtime could be reduced even further.
One way is using bulk collect with limit. Only limited rows (as defined by the limit clause) as fetched into the memory, which you can loop over to do the required processing.
declare
cursor cur_foo is
select * from foo where bar > 2000;
type tab is table of foo%rowtype;
v_tab tab;
BEGIN
open cur_foo;
loop
fetch cur_foo bulk collect into v_tab limit 100;
exit when v_tab.count = 0;
for idx in 1..v_tab.count loop
-- do something with v_tab(idx)
end loop;
end loop;
close cur_foo;
END;
You can also use FORALL to do bulk insert, update or delete operations.
See this article on these features on Oracle website
http://www.oracle.com/technetwork/issue-archive/2012/12-sep/o52plsql-1709862.html
and this question on AskTOM:
https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::p11_question_id:5918938803188
I am looking for a solution to create the SQL INSERT statements using PL/SQL script for the select statement result set. Looking for similar feature available in the SQL Developer tool (export --> format insert) but I want the solution as script rather than using any tool(s).
I have referred the below solution. However, I would like to know whether any better way to do it as the solution is old and not very simple.
EXPORT AS INSERT STATEMENTS: But in SQL Plus the line overrides 2500 characters!
I just found a simple solution for my problem using oracle hint ("insert"). This automatically take care the data type as well. My table has only string and numeric data types, so it works fine for me. I have not tested the solution for other data types. However, I hope it would work for other data types as well.
set linesize 2000
set pagesize 10
spool "c:\myoutput.txt";
select /*insert*/ * from SAMPLE_TABLE;
spool off;
as a script you say...
Use SQLcl - it's a command-line interface for SQL Developer.
You can create a .bat or .sh script to launch SQLcl, run your query, spool it to a file, no need to write any code.
set pagesize...
set head...
set feedback...
spool...
set sqlformat insert
select * from sample_table;
spool off
exit
set sqlformat insert
In my case initially it didn't work in SQL developer as the version was version 3.2 and resulted with message in the script output:
oracle unknown set option sqlformat insert
As i upgraded to the SQL developer 4.2 and above and using "Run Script" (F5) i was able to export the select records as the insert scripts.
You can do it with an for loop and dbms output as you want, even as merge statements like Toad, just make the buffer big enough:
BEGIN
FOR r_cur IN ( SELECT column_one,
column_two,
column_three,
column_value
FROM some_table
WHERE column_one LIKE 'something%'
ORDER BY column_one, column_two, column_three)
LOOP
DBMS_OUTPUT.put_line (
'MERGE INTO some_table A USING
(SELECT\n
''' || r_cur.column_one || ''' as column_one,
''' || r_cur.column_two || ''' as column_two,
''' || r_cur.column_three || ''' as column_three,
''' || r_cur.column_value || ''' as column_value
FROM DUAL) B
ON (A.column_one = B.column_one and A.column_two = B.column_two and A.column_three = B.column_three)
WHEN NOT MATCHED THEN
INSERT (
column_one, column_two, column_three, column_value)
VALUES (
B.column_one, B.column_two, B.column_three, B.column_value)
WHEN MATCHED THEN
UPDATE SET
A.column_value = B.column_value;
' );
END LOOP;
END;
/
Not really PL/SQL, but basic SQL can handle a request as simple as this:
set linesize 2000
set pagesize 0
spool c:\myoutput.txt
select 'INSERT INTO SAMPLE_TABLE VALUES ('''||
C01||''','||C02||','||C03||','||C04||','''||C05||''','''||
C06||''','''||C07||''','||C08||','||C09||','''||C10||''','''||
C10||''','''||C11||''','''||C12||''','''||C13||''','''||C14||''','''||
C15||''','''||C16||''');'
from sample_table;
spool off
Note: This is using the table example from the URL you referenced in your post. In the example in the URL, C02, C03, C04, C08, and C09 are all NUMBER, the rest are CHAR or VARCHAR2
I have to move the data from table A to table B (they have almost the same fields).
What I have now is a cursor, that iterates over the records that has to be moved, insert one record in the destination table and updates the is_processed field in the source table.
Something like:
BEGIN
FOR i IN (SELECT *
FROM A
WHERE A.IS_PROCESSED = 'N')
LOOP
INSERT INTO B(...) VALUES(i....);
UPDATE A SET IS_PROCESSED = 'Y' WHERE A.ID = i.ID;
COMMIT;
END LOOP;
END;
The questions is, how to do the same using INSERT FROM SELECT(without the loop) and then update IS_PROCESSED of all the moved rows?
There is no BULK COLLECT INTO for INSERT .. SELECT
May be you can try this. I don't think it's better than your LOOP.
DECLARE
TYPE l_src_tp IS TABLE OF t_source%ROWTYPE;
l_src_rows l_src_tp;
BEGIN
SELECT *
BULK COLLECT INTO l_src_rows
FROM t_source;
FORALL c IN l_src_rows.first .. l_src_rows.last
INSERT INTO t_dest (td_id, td_value)
VALUES (l_src_rows(c).ts_id, l_src_rows(c).ts_value);
FORALL c IN l_src_rows.first .. l_src_rows.last
UPDATE t_source
SET ts_is_proccesed = 'Y'
WHERE ts_id = l_src_rows(c).ts_id;
END;
If you reverse the order and first make update and then insert you can use:
DECLARE
ids sys.odcinumberlist;
BEGIN
UPDATE a SET is_processed = 'Y' WHERE is_processed = 'N' RETURNING id BULK COLLECT INTO ids;
INSERT INTO b (id) SELECT column_value id FROM TABLE(ids);
COMMIT;
END;
In the SELECT you can join the ids table and get other data from other tables you want to insert into b.
Hello I should prefer pure SQL rather than PLSQL. I don't know why another update statement is requires for this simpler task. Let me know if this helps.
INSERT INTO <new table>
SELECT col1,col2,
.....,'Y'
FROM <old_table>
WHERE processed_in = 'N';
I don't have access, right now, to test this, but is the following -- or something like it, as my off-the-top-of-my-head code mightn't be perfect! -- possible in Oracle:
declare
myRecord myTable%ROWTYPE;
begin
select * into myRecord from myTable where key = 123;
myRecord.key := 456;
myRecord.value := myRecord.value + 50;
insert into myTable select * from table(myRecord);
end;
i.e., We copy the record from myTable -- which may have, say, 100 fields -- with key 123 into a variable with the same schema, then update a few of the copied record's fields (e.g., here, a new key and an updated value) before inserting it back into the original table: Effectively, duplicating the original record with some modifications, where necessary?
I know there are other ways to do this, but this seems quite neat, in comparison to what else I've seen...if it were to work, of course!
INSERT INTO myTable VALUES myRecord;
Some good examples on this site: http://psoug.org/reference/insert.html I guess you don't want to do something like this because you have a lot of columns?
INSERT INTO myTable
SELECT 456, value+50, colx, coly
FROM myTable
WHERE key = 123;
It's actually even easier than your proposed syntax
Set up the table and the data
CREATE TABLE foo(
col1 NUMBER,
col2 VARCHAR2(100)
);
INSERT INTO foo( col1, col2 )
VALUES( 1, 'Justin' );
The PL/SQL block to select, modify, and then re-insert a record
declare
l_foo_rec foo%rowtype;
begin
select *
into l_foo_rec
from foo
where col1 = 1;
l_foo_rec.col2 := 'Michael';
l_foo_rec.col1 := l_foo_rec.col1 + 1;
insert into foo
values l_foo_rec;
end;
Which, when you run it, will produce this data in FOO
1* select * from foo
SQL> /
COL1 COL2
---------- --------------------
1 Justin
2 Michael
Of course, as Glenn points out, it is generally going to be more efficient to write a SQL statement that inserts a new row by selecting data from an existing row without needed to use PL/SQL at all. Depending on how complex your logic is, however, a PL/SQL approach may be easier to debug and maintain.