Removing some records from table in Oracle, how to get statistics (e.g. NUM_ROWS, LAST_ANALYZED) updated?
Gather them, for example
begin
dbms_stats.gather_table_stats('SCOTT', 'EMP');
end;
/
Related
I have created a trigger and was expecting a mutating table error in below case but didn't get one through normal insert but getting an error while inserting using a query. I am not sure which concept I am missing here.
drop table temp;
create table temp (id number,name varchar2(500),is_active number);
create or replace trigger temp_trg before insert on temp
for each row
declare
v_count number;
begin
select count(1) into v_count from temp;
update temp set is_active=0 where is_active=1 and id=:new.id;
end;
/
select * from temp;
insert into temp values (1,'xyz',1);
insert into temp values (1,'xyz',1);
insert into temp select 1,'xyz',1 from dual;
getting an error while inserting using a query.
Mutating table occurs when we query the table which owns the trigger. Specifically it happens when Oracle can't guarantee the outcome of the query. Now when you insert a single row into the table Oracle can predict the outcome of the query when the FOR EACH ROW trigger fires, because it's a single row.
But with an INSERT FROM query Oracle is confused: should the count be the final figure including all the rows selected by the query or just a rolling count? The answer seems straightforward in this case but it's easy to imagine other queries where the answer is not clear cut. Rather than evaluate each query on its predictability Oracle enforces a plain fiat and hurls ORA-04091 for all query driven inserts.
The restriction on Mutating Tables applies to all triggers that use FOR EACH ROW clause except when either of the following is true:
the trigger fires on BEFORE event, i.e. the data wasn't actually changed
it is known that only one row will be affected - INSERT ... VALUES is the only DML that meets this condition
In PLSQL stored procedure, I would like to drop indexes and truncate the table
before inserting data into the table. Then I want to recreate the indexes afterwards . What is the best way of achieving this?
I need something similar like this
Begin:
Truncate Table
Drop index1
Drop index2
loop
--- other code
Insert data
commit;
end loop;
Create index1
Create index2
End;
We can't run DDL directly in PL/SQL.
The best approach is to use the Oracle built-in DBMS_UTILITY.EXEC_DDL_STATEMENT(). It's covered in the docs Find out more. But basically:
begin
DBMS_UTILITY.EXEC_DDL_STATEMENT('drop index index1')
DBMS_UTILITY.EXEC_DDL_STATEMENT('drop index index2')
DBMS_UTILITY.EXEC_DDL_STATEMENT('truncate table your_table')
....
Note that the commands are strings not SQL.
If you're inserting enough data to make it worthwhile dropping and re-creating the indexes then you probably want set operations rather than doing it in a loop. Set operations work on chunks of data rather then individual rows. Quite what approach is appropriate depends on how much data you have.
"I want to insert data from staging table and then joining with other table"
So the most efficient route is likely to be
insert into your_table
select /*+ append */ ....
from staging_table
join other_table
on ...
However, depending on your definition of "huge" you may need to use a PL/SQL bulk operation, with a FORALL insertion. That would require reading from your staging table in chunks, so it would entail a loop. Find out more
You can use dynamic SQL , specifically, execute immediate, based on your requirements:
Begin:
execute immediate 'truncate table statement';
execute immediate 'DROP index statement';
loop
--- other code
Insert data
commit;
end loop;
execute immediate 'Create index1 statement';
execute immediate 'Create index2 statement';
End;
In one of my Windows form application in asp .net, I am creating tables on daily basis sharp at 00:00 am with name as "data_YYYY_MM_DD" in Oracle database. A large amount of data exist in each table as after every 5 seconds I am writing some useful data into these.
Now consider all tables of month Oct 2016. All will have name like 'data_2016_10%'. How can I take backup of only these tables (not backup of entire database) and then drop these tables from the database.
You can take logical backup of these tables using data pump. It creates dump file(binary file) which can be imported as per your needs.
Export:Table Mode
A table mode export is specified using the TABLES parameter. In table mode, only a specified set of tables, partitions, and their dependent objects are unloaded.
Example:
expdp hr TABLES=hr.employees VERSION=LATEST DIRECTORY=dpump_dir1 DUMPFILE=emp.dmp NOLOGFILE=YES
Reference:
Data Pump Export
Try like below,
you can schedule it in dbms_scheduler jobs,So that every day it will run and create backup table and drop existing table.Also please add exceptions like table does not exist...if you want
create or replace procedure backup_monthly_table
as
prev_date varchar2(20) := to_char(sysdate-1, 'yyyy-mm-dd');
begin
for i in (select table_name from dba_tables where upper(table_name) like '%'||'"'||prev_date||'_%')
loop
dbms_output.put_line('working');
dbms_output.put_line(i.table_name);
execute immediate 'create table sysman.'||'"'||i.table_name||'_bkp'||'"'||' as select * from sysman.'||'"'||i.table_name||'"';
execute immediate 'drop table sysman.'||'"'||i.table_name||'"';
end loop;
end;
verification output:
select owner,table_name from dba_tables where upper (table_name) like '%2017-%'
SYSMAN 2017-02-01_test1_bkp
SYSMAN 2017-02-01_test2_bkp
SYSMAN 2017-02-01_test3_bkp
SYSMAN 2017-02-01_test4_bkp
I have a query to delete some records from a table, but take too much time.
The table is use it in a stored procedure to match another table.
Every time that the SP is executed the table is truncated and filled with 2 or 3 millions of records depending of the received parameters.
The table doesn't have any FK or constraints
The query to delete the records that I am using is:
DELETE FROM TABLE1
WHERE (fecha,hora_ini,origen,destino,tipo,valor,rowsm1) IN (
SELECT fecha_t,hora_t,origen_t,destino_t,tipo,valor,id_t
FROM TABLE2)
I try to decrease the time in execute the query creating an index based in the same columns of the query
CREATE INDEX smb1 ON table1 (fecha,hora_ini,origen,destino,tipo,valor,rowsm1);
And the query take more time to execute.
How can improve the performance of this "DELETE" query.
UPDATE
EXPLAIN PLAN OUTPUT
DELETE TABLE1
TABLE ACCESS TABLE1
TABLE ACCESS FULL TABLE1
TABLE ACCESS FULL TABLE2
TABLE ACCESS FULL TABLE2
The index you created looks like a quite big index:
CREATE INDEX smb1
ON table1 (fecha,hora_ini,origen,destino,tipo,valor,rowsm1);
Sure, this depends on the amount of data but generally I would rather look for one or two selective columns - if possible.
Don't forget, that the index data has to be read as well and if it doesn't help to speed up the query, you even loose performance.
This might for instance happen, if the table is very small, because the database reads data block by block (I think it was about 8K). A small table can be read in one step - no need to use an index here.
Or, if more or less all records are selected. In this case the table has to be read anyway.
If you want to speed up the query you should create the same index (with a good selectivity) on table2. This way the EXPLAIN PLAN will look somewhat lie this:
DELETE STATEMENT
DELETE
NESTED LOOPS SEMI
INDEX FULL SCAN
INDEX RANGE SCAN
You can switch off logging and delete the rows,
Here is an example, you can do it 2 ways,
1.) Physically chaning the table to Nologging
2.) Using Nologging hint in the delete statement.
1.) First approach
both testemp and testemp2 are same tables with same data while testemp takes over a minute , testemp2 takes only 1 second
SQL> delete from testemp;
14336 rows deleted.
Elapsed: 00:01:04.12
SQL>
SQL>
SQL> alter table testemp2 nologging;
Table altered.
Elapsed: 00:00:02.86
SQL>
SQL> delete from testemp2;
14336 rows deleted.
Elapsed: 00:00:01.26
SQL>
The table needs to be put back to logging only when we physically change the table using "Alter" command, if you are using as hint not required please see the example below
2.) Second approach
SQL> set timing on;
SQL> delete from testemp2;
14336 rows deleted.
Elapsed: 00:00:01.51
Deleting data after reinserting same data into table now with nologging;
SQL> delete /*+NOLOGGING*/ from testemp2;
14336 rows deleted.
Elapsed: 00:00:00.28
SQL> select logging from user_Tables where table_name='TESTEMP2';
LOG
---
YES
I have pairs of tables in the format TABLE and TABLE_TWIN now
TABLE is the main table with lots of data
TABLE_TWIN is a table with the exact same fields with a little data (different data)
Now I would like to copy all rows from TABLE_TWIN to TABLE using a stored procedure. I have many such tables and could like the stored procedure to take the table name(s) as parameter(s) so that I can use the same procedure for each table pair. I do not want to write long INSERT statements because these tables have around 50 attributes each.
I am not good with PL/SQL so I need some help here.
Thanks!
SQL is not so long... But if you prefer a procedure, here it is:
create or replace procedure table_copy(
p_tab_from varchar2,
p_tab_to varchar2)
is
begin
execute immediate 'insert into '||p_tab_to||' (select * from '||p_tab_from||')';
end;
insert into table_twin (select * from table)
should do it