How to truncate any table using its synonym in oracle? - sql

How to truncate any table using its synonym in oracle?
-- in Server_A
Create Table table_a ( col int);
-- in server_B
CREATE SYNONYM syn_table_a FOR table_a#dblink_server_a;
--insert into
INSERT INTO syn_table_a values (1);
--Truncate
How to truncate table using synonym only?.

A truncate statement cannot be used on a synonym.
Synonyms cannot be used in a drop table, drop view or truncate
table/cluster statements. If this is tried, it results in a ORA-00942:
table or view does not exist
For example,
SQL> CREATE TABLE t(col NUMBER);
Table created.
SQL>
SQL> CREATE SYNONYM t_syn FOR t;
Synonym created.
SQL>
SQL> TRUNCATE TABLE t_syn;
TRUNCATE TABLE t_syn
*
ERROR at line 1:
ORA-00942: table or view does not exist
SQL>

You could use dynamic SQL to do it, e.g.:
declare
d varchar2(1000);
begin
select 'TRUNCATE TABLE "' || table_owner || '"."' || table_name || '"'
into d
from all_synonyms
where synonym_name = 'MYSYNONYM';
execute immediate d;
end;
If the table is accessed via a database link, this will not work. In that case, you could create a procedure on the remote instance that does the truncate, then call that procedure across the database link, e.g.
begin
truncate_my_table#dblinkname;
end;

In Oracle, you can also get ORA-14410 while trying to drop/truncate a table using synonym.
The alert log:
ORA-00604: error occurred at recursive SQL level 1
ORA-14410: RPI LOCK TABLE issued to table referenced through synonym
Follow the above dynamic SQl to drop/truncate it.

Related

Oracle SQL only drop a column if a table exists

For Microsoft SQL Server I have following statement to only drop a column if the table exist.
IF EXISTS(SELECT 1
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'TEST_TABLE')
ALTER TABLE TEST_TABLE DROP COLUMN LEGACY_VALUE
GO
I was wondering if there was a related IF-EXISTS mechanism is present in Oracle.
All the metadata about the columns in Oracle Database is accessible using one of the following views.
user_tab_cols; -- For all tables owned by the user
all_tab_cols ; -- For all tables accessible to the user
dba_tab_cols; -- For all tables in the Database.
So, if you are looking for a column that exists and want to drop it, your code may look something like this ( see below).
Since this appears to be a one time task, is the effort really worth it?
DECLARE
v_column_exists number := 0;
BEGIN
Select count(*) into v_column_exists
from user_tab_cols
where upper(column_name) = 'LEGACY_VALUE''
and upper(table_name) = 'TEST_TABLE';
--and owner = 'SCOTT --*might be required if you are using all/dba views
if (v_column_exists = 1) then
execute immediate 'alter table test_table drop column legacy_value';
end if;
end;
/

Trigger on the create table as sql

I want to save the SQL create statement as following:
CREATE TABLE tbl AS
SELECT
*
FROM
tbl_info;
Is there a way to save the previous query in a table like this:
table_name query
tbl CREATE TABLE tbl AS SELECT * FROM tbl_info;
which table_name, query are the columns of this table,
i tried to look up for triggers but there is nothing about trigger before or after on create statement,
please your help.
Indeed there is a way of doing this, but it is not recommendable. Oracle offers out of the box audit capabilities much better than any custom solution you can think of.
Having said that, one option would be to use a DDL TRIGGER. In the example below you have an audit table to store the create event and a trigger to record them.
Keep in mind that I use ON SCHEMA, so it will only affect the CREATE events for the schema which the trigger belongs to.
Base code
CREATE TABLE AUDIT_DDL (
D DATE,
OSUSER VARCHAR2(255),
CURRENT_USER VARCHAR2(255),
SYSEVENT VARCHAR2(30),
STATEMENTS VARCHAR2(1000)
);
CREATE OR REPLACE TRIGGER AUDIT_DDL_TRG
AFTER DDL ON SCHEMA
DECLARE
sql_text ora_name_list_t;
v_stmt VARCHAR2(2000);
n PLS_INTEGER;
BEGIN
n := ora_sql_txt(sql_text);
FOR i IN 1 .. n LOOP
v_stmt := v_stmt || sql_text(i);
END LOOP;
v_stmt :=regexp_replace(v_stmt,
'rename[[:space:]]+.*[[:space:]]+to[[:space:]]+([a-z0-9_]+)',
'\1',
1,
1,
'i');
IF (ORA_SYSEVENT = 'CREATE')
THEN
INSERT INTO AUDIT_DDL
(D,
OSUSER,
CURRENT_USER,
SYSEVENT,
STATEMENTS)
VALUES
(SYSDATE,
SYS_CONTEXT('USERENV', 'OS_USER'),
SYS_CONTEXT('USERENV', 'CURRENT_USER'),
ORA_SYSEVENT,
v_stmt);
END IF;
END;
/
Let's check how it works
sqlplus test1/Oracle_123
SQL*Plus: Release 19.0.0.0.0 - Production on Sun Oct 3 11:13:21 2021
Version 19.6.0.0.0
Copyright (c) 1982, 2019, Oracle. All rights reserved.
Last Successful login time: Wed Sep 22 2021 08:08:57 +02:00
Connected to:
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.6.0.0.0
SQL> create table t1 as select * from all_objects ;
create table t1 as select * from all_objects
*
ERROR at line 1:
ORA-00955: name is already used by an existing object
SQL> drop table t1 purge;
Table dropped.
SQL> create table t1 as select * from all_objects ;
Table created.
SQL> select d,statements from AUDIT_DDL ;
D
---------
STATEMENTS
--------------------------------------------------------------------------------
03-OCT-21
create table t1 as select * from all_objects
As you can see above ( on purpose ) , I did several statements ( a create failing, a drop table, and finally a CTAS statement ). However, our event trigger is only looking for the sysevent create, and because it is after ddl on schema, it will only store the data after the command is executed successfully.
You can store many other properties of the default context sys_context.
Nevertheless, this has a huge impact in performance and it is not recommendable

Postgresql - Union table values from different schemas in a database into one table

I have a Database called Knowledge in postgres. It has multiple schemas and every schema has same number of tables, table has same columns as well.
Now I want to create a new schema called Aggregate, table called aggregate.table1 and put values from schema1.table1 and schema2.table1 in it.
I need to add another column in Aggregate.table1 which holds the value representing schema.
If any value in schema1.table1 is updated then aggregate.table1 should get the updated values.
Question,
Is it possible in Postgresql? if so please help me with this.
I need this aggregated table for further processing
You can try writing an anonymous code block to iterate over all schemas and tables, so that you can import your data into the aggregate schema. The following block search for all tables contained in the schemas s1 and s2, creates a corresponding table in the schema s_agg and finally copies its records.
DO $$
DECLARE row record;
BEGIN
FOR row IN SELECT * FROM pg_tables WHERE schemaname IN ('s1','s2') LOOP
EXECUTE 'CREATE TABLE IF NOT EXISTS s_agg.'||quote_ident(row.tablename)||
' AS TABLE ' || quote_ident(row.schemaname)||'.'|| quote_ident(row.tablename) ||
' WITH NO DATA;';
EXECUTE 'INSERT INTO s_agg.' || quote_ident(row.tablename)
|| ' SELECT * FROM '||quote_ident(row.schemaname)||'.'||quote_ident(row.tablename);
END LOOP;
END;
$$;
Demo
CREATE SCHEMA s1;
CREATE SCHEMA s2;
CREATE SCHEMA s_agg;
CREATE TABLE s1.t1 (id int);
INSERT INTO s1.t1 VALUES (1);
CREATE TABLE s2.t1 (id int);
INSERT INTO s2.t1 VALUES (42);
DO $$
DECLARE row record;
BEGIN
FOR row IN SELECT * FROM pg_tables WHERE schemaname IN ('s1','s2') LOOP
EXECUTE 'CREATE TABLE IF NOT EXISTS s_agg.'||quote_ident(row.tablename)||
' AS TABLE ' || quote_ident(row.schemaname)||'.'|| quote_ident(row.tablename) ||
' WITH NO DATA;';
EXECUTE 'INSERT INTO s_agg.' || quote_ident(row.tablename)
|| ' SELECT * FROM '||quote_ident(row.schemaname)||'.'||quote_ident(row.tablename);
END LOOP;
END;
$$;
-- contains values of t1 from s1 and s2
SELECT * FROM s_agg.t1;
id
----
1
42
Note: This code works with the assumption that the aggregate schema is either empty or it has empty tables, otherwise data will be duplicated. If you run this periodically and the size of your tables isn't too large, you can add a DROP TABLE before the CREATE TABLE statement. To make it work on every commit on all tables of all schemas you have to take a look at TRIGGERS or even logical replication.

Teradata: replace an existing table with "CREATE TABLE"

What setting should I change to make Teradata replace existing tables with CREATE TABLE query?
Currently, if the table exists, an attemps to CREATE it results in error. So I have to DROP the table before CREATing it.
thx
REPLACE PROCEDURE DROP_IF_EXISTS(IN table_name VARCHAR(60),IN db_name VARCHAR(60))
BEGIN
IF EXISTS(SELECT 1 FROM dbc.tables WHERE databasename=db_name AND tablename=table_name)
THEN
CALL DBC.SysExecSQL('DROP TABLE ' || db_name ||'.'|| table_name);
END IF;
END;
And in your DDL script:
call drop_if_exists('$your_table_name','$your_db_name')
;
database $your_db_name;
create table $your_table_name ...
;

Change table name with sysdate

I want to change a table name by appending SYSDATE to it. For example, I want to change table EXAMPLE_TABLE to EXAMPLE_TABLE_05_01_2015, but I want to get the date from SYSDATE.
I prepared the following but it is not working:
ALTER TABLE "MYDB"."EXAMPLE_TABLE" rename to (SELECT 'EXAMPLE_TABLE' || TO_CHAR(SYSDATE, '_dd_MM_yyyy') FROM DUAL);
How can I make it work?
Here is the error:
SQL Error: ORA-14047: ALTER TABLE|INDEX RENAME may not be combined with other operations
14047. 00000 - "ALTER TABLE|INDEX RENAME may not be combined with other operations"
*Cause: ALTER TABLE or ALTER INDEX statement attempted to combine
a RENAME operation with some other operation which is illegal
*Action: Ensure that RENAME operation is the sole operation specified in
ALTER TABLE or ALTER INDEX statement;
Use execute immediate.
begin
execute immediate
'alter table mydb.example_table rename to ' ||
'example_table_' || to_char(sysdate, 'dd_mm_yyyy');
end;
/
That said, I have the hunch that you'd be better off using partitioned tables.
In SQL*Plus, you could use the variable substitution.
Just another way :
SQL> CREATE TABLE t(ID NUMBER)
2 /
Table created.
SQL>
SQL> COLUMN new_tablename NEW_VALUE new_tablename
SQL> SELECT 't_' || to_char(sysdate, 'dd_mm_yyyy') AS new_tablename from dual
2 /
NEW_TABLENAM
------------
t_05_01_2015
SQL>
SQL> RENAME t TO &new_tablename
2 /
old 1: RENAME t TO &new_tablename
new 1: RENAME t TO t_05_01_2015
Table renamed.
SQL>
SQL> select * from t_05_01_2015;
no rows selected
SQL>
So, now the table T is renamed to T_05_01_2015.