Insert then delete rows out of an oracle table - sql

I wanted to see if there is a better way to do this. The statement takes all rows that happened further out than 6 months and puts them into an archive table. Then it takes all the ids present in both tables and removes them from the main table. Any Ideas?
INSERT INTO ArchiveTable
SELECT *
FROM MainTable
WHERE DateItHappened < (SYSDATE - 180);
DELETE FROM MainTable a
WHERE a.ID IN (
SELECT b.ID
FROM ArchiveTable b
JOIN MainTable a
ON b.ID =
a.ID);
Update:
I went ahead and implemented Joe's suggestion below to make this the final code. If anyone has any changes that should be made please let me know.
INSERT INTO ArchiveTable
SELECT *
FROM MainTable
WHERE DateItHappened < (SYSDATE - 180);
DELETE FROM maintable a
WHERE EXISTS (SELECT 1
FROM archivetable b
WHERE a.id = b.id)

The DELETE can be simplified:
DELETE FROM maintable a
WHERE EXISTS (SELECT 1
FROM archivetable b
WHERE a.id = b.id)

My usual approach is to use a global temporary table to store the rowid's of the rows that I'm moving from the source table to the target table, then using that set to perform the deletes. It makes it 100% safe for such issues as an unstable initial data set.
insert all
into archivetable (col1, col2 ...)
values (col1, col2 ...)
into maintable_deletes (delete_rowid)
values (rowid)
select rowid, *
from maintable;
delete from maintable
where rowid in (select delete_rowid from maintable_deletes);
commit;
The GTT is useful because it can delete its own rows on commit.

In SQL Server it would be like, but oracle does not have a built in method to do this:
delete m
output deleted.ColumnA, deleted.ColumnB
into ArchiveTable
from
MainTable m
where DateItHappened < (SYSDATE - 180);

Related

Deleting rows from a join of two tables and rownum condition in Oracle DB

Learning PL/SQL with Oracle DB and trying to accomplish the following:
I have two tables a and b. I am joining them on id, add several conditions and then try removing resulting rows only from table a in a batch size of 1000. Base query looks like this:
DELETE (SELECT *
FROM SCHEMA.TABLEA a
INNER JOIN SCHEMA.TABLEB b ON a.b_id = b.id
WHERE par=0 AND ROWNUM <= 1000);
This obviously doesn’t work as I am trying to manipulate a view: “data manipulation operation not legal on this view”
How can I rewrite this?
you can only remote from a table, there now Need to do a join. you can handle it in a where clause if you Need
you delete Statement could be e.g.
DELETE from SCHEMA.TABLEA a
where a.id in (select b.id from SCHEMA.TABLEB b)
and par=0 AND ROWNUM <= 1000
You can write simple query which checks if the rows in TABLEA that are required to be deleted exists in TABLEB.
DELETE
FROM schema.tablea a
WHERE par = 0
AND EXISTS (SELECT 1 FROM schema.tableb b WHERE a.b_id = b.id)
AND rownum <= 1000;

Making an INSERT INTO as error/duplicate proof as possible without ID

I have a stored procedure that inserts from tableA to tableB. Each time the procedure is executed (because tableA will get updated constantly so when the procedure is run it will add missing data into B), it will insert the entire contents of A into B no matter what.
So in the insert I added a WHERE statement to filter "older data" based on the timestamp of when the record was added (insert new records where the timestamp from tableA is greater than the one on tableB is). The table has no unique identifiers so I'm kinda stuck making the filter as safe as it can, basically the only thing I could consider is the timestamp. I've tried some ways around it but all of them have their cons (commented in each code block).
INSERT INTO TABLEB (VALUES)
SELECT A.VALUES
FROM A
WHERE (SELECT MAX(TIMESTAMP) FROM TABLEB) < (SELECT MAX(TIMESTAMP) FROM TABLEA)
--THIS DOESN'T EVEN WORK
INSERT INTO TABLEB (VALUES)
SELECT A.VALUES
FROM A
WHERE A.TIMESTAMP BETWEEN (SELECT MAX(TIMESTAMP) FROM TABLEB) AND (SELECT MAX(TIMESTAMP) FROM TABLEA)
--IF RUN TWICE IT WILL RUN ON RECORDS CONTAINING TIMESTAMPS WITH THOSE VALUES
INSERT INTO TABLEB (VALUES)
SELECT A.VALUES
FROM A
WHERE A.TIMESTAMP BETWEEN (SELECT MAX(TIMESTAMP) FROM TABLEB) AND (SELECT MAX(TIMESTAMP) FROM TABLEA)
AND A.TIMESTAMP <> (SELECT MAX(TIMESTAMP) FROM TABLEA)
--THIS WILL EFFECTIVELY SKIP RECORDS WITH THE TIMESTAMP FROM TABLEA BUT RUN TWICE WILL DUPLICATES THE ONES WITH TIMESTAMP FROM B
INSERT INTO TABLEB (VALUES)
SELECT A.VALUES
FROM A
WHERE A.TIMESTAMP BETWEEN (SELECT MAX(TIMESTAMP) FROM TABLEB) AND (SELECT MAX(TIMESTAMP) FROM TABLEA)
AND A.TIMESTAMP <> (SELECT MAX(TIMESTAMP) FROM TABLEA)
AND A.TIMESTAMP <> (SELECT MAX(TIMESTAMP) FROM TABLEB)
--THIS WILL SKIP DATA WITH THE TIMESTAMP FROM B
Any help on the logic to make in run ONCE and skip duplicated rows is appreciated.
I'm not sure what is your problem, but this should work. You check what was the last one inserted in Table B and then got all the new records.
INSERT INTO TABLEB (VALUES)
SELECT A.VALUES
FROM A
WHERE TIMESTAMP > (SELECT MAX(TIMESTAMP) FROM TABLEB)
Inserting all the records on each insert of table A is pretty much costly. You can use "Scope_Identity" command if you are using T-SQL (sql server). You need to add each record in both tables in same call. Add values to table A then add to table B.
Here is an example:
INSERT INTO TABLEA (column-names)
VALUES (values)
DECLARE #LAST_RECORD_ID INT=0
SELECT #LAST_RECORD_ID = SCOPE_IDENTITY()
INSERT INTO TABLEB (column-names)
SELECT
(column-names)
FROM TABLEA WHERE TABLEA.ID = #LAST_RECORD_ID
You need to have a version column in table A with the type of timestamp and a corresponding column in table B with the type of varbinary(8). With this assumption, the following code must work:
INSERT INTO TABLEB (VALUES)
SELECT A.VALUES
FROM A
WHERE A.Version > (SELECT MAX(Version) FROM TABLEB)

SQL: How to insert/update multiple tables by checking duplicates in one table?

MS SQL: I want to insert/update data in a table based on below conditions.
I have tried using IF EXISTS as shown below. Can anyone correct me if I'm wrong here or send an alternative one which does this job better?
IF NOT EXISTS (select 1 from TableA where col1 in (select col1 from tableA))
BEGIN
INSERT INTO TableA
SELECT * FROM TableB
WHERE some condition
DELETE FROM TableB
WHERE some condition
DELETE l FROM TableC l
INNER JOIN #temptable s on l.col1 = s.col1 WHERE l.col1 = s.col1
END
ELSE
BEGIN
UPDATE TableA set (columns which are changed in TableA) where condition which pulls up exisiting query
END
Its very simple using new MERGE operator provide in sql server 2008.
In MERGE operation you can define source and target table and based on matching and un-matching rows you can perform delete , update or insert in one shot.
please refer below link for more detail - https://www.red-gate.com/simple-talk/sql/learn-sql-server/the-merge-statement-in-sql-server-2008/

Update table values from another table with the same user name

I have two tables, with a same column named user_name, saying table_a, table_b.
I want to, copy from table_b, column_b_1, column_b2, to table_b1, column_a_1, column_a_2, respectively, where the user_name is the same, how to do it in SQL statement?
As long as you have suitable indexes in place this should work alright:
UPDATE table_a
SET
column_a_1 = (SELECT table_b.column_b_1
FROM table_b
WHERE table_b.user_name = table_a.user_name )
, column_a_2 = (SELECT table_b.column_b_2
FROM table_b
WHERE table_b.user_name = table_a.user_name )
WHERE
EXISTS (
SELECT *
FROM table_b
WHERE table_b.user_name = table_a.user_name
)
UPDATE in sqlite3 did not support a FROM clause for a long time, which made this a little more work than in other RDBMS. UPDATE FROM was implemented in SQLite 3.33 however (2020-08-14) as mentioned at: https://stackoverflow.com/a/63079219/895245
If performance is not satisfactory, another option might be to build up new rows for table_a using a select and join with table_a into a temporary table. Then delete the data from table_a and repopulate from the temporary.
Starting from the sqlite version 3.15 the syntax for UPDATE admits a column-name-list
in the SET part so the query can be written as
UPDATE table_a
SET
(column_a_1, column_a_2) = (SELECT table_b.column_b_1, table_b.column_b_2
FROM table_b
WHERE table_b.user_name = table_a.user_name )
which is not only shorter but also faster
the last "WHERE EXISTS" part
WHERE
EXISTS (
SELECT *
FROM table_b
WHERE table_b.user_name = table_a.user_name
)
is actually not necessary
It could be achieved using UPDATE FROM syntax:
UPDATE table_a
SET column_a_1 = table_b.column_b_1
,column_a_2 = table_b.column_b_2
FROM table_b
WHERE table_b.user_name = table_a.user_name;
Alternatively:
UPDATE table_a
SET (column_a_1, column_a_2) = (table_b.column_b_1, table_b.column_b_2)
FROM table_b
WHERE table_b.user_name = table_a.user_name;
UPDATE FROM - SQLite version 3.33.0
The UPDATE-FROM idea is an extension to SQL that allows an UPDATE statement to be driven by other tables in the database. The "target" table is the specific table that is being updated. With UPDATE-FROM you can join the target table against other tables in the database in order to help compute which rows need updating and what the new values should be on those rows
There is an even much better solution to update one table from another table:
;WITH a AS
(
SELECT
song_id,
artist_id
FROM
online_performance
)
UPDATE record_performance
SET
op_song_id=(SELECT song_id FROM a),
op_artist_id=(SELECT artist_id FROM a)
;
Update tbl1
Set field1 = values
field2 = values
Where primary key in tbl1 IN ( select tbl2.primary key in tbl1
From tbl2
Where tbl2.primary key in tbl1 =
values);
The accepted answer was very slow for me, which is in contrast to the following:
CREATE TEMPORARY TABLE t1 AS SELECT c_new AS c1, table_a.c2 AS c2 FROM table_b INNER JOIN table_a ON table_b.c=table_a.c1;
CREATE TEMPORARY TABLE t2 AS SELECT t1.c1 AS c1, c_new AS c2 FROM table_b INNER JOIN t1 ON table_b.c=t1.c2;

How to delete items that exist in one table from another?

I have tables A and B. Items of table B might exist also in table A, and I want to delete those items. What would the SQL statements to do this look like?
this is an option
delete from a
where a.key in (select key from b)
Either:
DELETE a
WHERE a.some_field IN (SELECT some_field FROM b)
or
DELETE A
WHERE EXISTS (SELECT 1 FROM b WHERE b.field1 = a.field2)
Depending on your database, you may find one works particularly better than the other. IIRC Oracle tends to prefer WHERE EXISTS to IN but this can depend on a number of factors.
In certain DBs the rather exotic looking DELETE FROM FROM is very efficient
delete from foo from foo as f
where exists
(
select 1 from bar as b where b.field = f.field
)
Something like:
DELETE
FROM TableA as A
WHERE A.ID IN (SELECT ID
FROM TableB AS B
WHERE [your condition here])
If your tables use InnoDB, the easiest way would be to setup table A with foreign keys from table B, and use ON DELETE CASCADE. That way no code changes are necessary, and the integrity of your database is guaranteed.
This is allowed according to the standard.
delete a from a join b on a.id = b.id;
DELETE FROM FIRST_TABLE FT
WHERE EXISTS(
SELECT 1
FROM SECOND_TABLE ST
WHERE ST.PRIMARY_KEY = FT.PRIMARY_KEY
);
delete a
--select a.*
from tablea a
join tableb b on a.someid = b.someid
Make sure that you run the select part first to ensure that you are getting the records you want.