Conditional delete and insert in postgres - postgresql

I have two tables Table_A and Table_B. How can I write a conditional SQL that does the following logic
If table A records match table B records on id
then
delete records from table A and Insert records into Table B
How can I do this with SQL most likely using with
delete from Table_A where Exists (select a.id from TABLE_A
join TABLE_B as b on a.id = b.id)
The Insert is:Insert into Table_A (id) select id from TABLE_B

Use a CTE to catch the ids of the deleted records, and re-join these with the b records:
WITH del AS (
DELETE FROM a
WHERE EXISTS ( SELECT *
FROM b
WHERE b.id = a.id
)
returning *
)
INSERT INTO a (id, x, y, z)
SELECT id, x, y, z
FROM b
WHERE EXISTS (
SELECT *
FROM del
WHERE del.id = b.id
);
BTW: you should have very good reasons (such as wanting to activate the triggers) to prefer delete+insert to a update.

Related

Postgres join involving tables having join condition defined on an text array

I have two tables in postgresql
One table is of the form
Create table table1(
ID serial PRIMARY KEY,
Type []Text
)
Create table table2(
type text,
sellerID int
)
Now i want to get all the rows from table1 which are having type same that in table2 but the problem is that in table1 the type is an array.
In case the type in the table has an identifiable delimiter like ',' ,';' etc. you can rewrite the query as regexp_split_to_table(type,',') or versions later than 9.5 unnest function can be use too.
For eg.,
select * from
( select id ,regexp_split_to_table(type,',') from table1)table1
inner join
select * from table2
on trim(table1.type) = trim(table2.type)
Another good example can be found - https://www.dbrnd.com/2017/03/postgresql-regexp_split_to_array-to-split-string-using-different-delimiters/
SELECT
a[1] AS DiskInfo
,a[2] AS DiskNumber
,a[3] AS MessageKeyword
FROM (
SELECT regexp_split_to_array('Postgres Disk information , disk 2 , failed', ',')
) AS dt(a)
You can use the ANY operator in the JOIN condition:
select *
from table1 t1
join table2 t2 on t2.type = any (t1.type);
Note that if the types in the table1 match multiple rows in table2, you would get duplicates (from table1) because that's how a join works. Maybe you want an EXISTS condition instead:
select *
from table1 t1
where exists (select *
from table2 t2
where t2.type = any(t1.type));

SQL - UPSERT LOOP through SELECT's resultset

I have the following query that is returning two columns of data UserID & ServiceID from tables a & b
SELECT a.UserID, b.ServiceID FROM TableA a
JOIN TableB b
ON b.EventID = a.EventID
WHERE a.EventID = 1892286
AND a.Attendance IN (1,2)
The above query returns the following data set:
UserID ServiceID
1 33
2 44
3 55
I need to UPSERT into table c the following way:
If UserID & ServiceID exist in table c then take current value of column Scheduled from table c and add 1
If UserID & ServiceID do NOT exist in table c then insert record into table c with selected UserID and ServiceID from the above select statement and add 1 into the Scheduled column.
My question is how to do this for every single row that is returned by my select statement to ensure all results are UPSERTED into table c
I'm looking through looping case or if-else statements as possible solutions but any input is appreciated.
My attempt at the above:
IF EXISTS (SELECT sa.UserID as S_UserID, ce.ServiceID as S_ServiceID FROM TableA a
JOIN TableB b
ON b.EventID = a.EventID
WHERE a.EventID = 1892286
AND a.Attendance IN (1,2))
UPDATE TableC c
SET
UserID = S_UserID
ServiceID = S_ServiceID
Scheduled = Scheduled + 1
WHERE UserID = S_UserID
AND ServiceID = S_ServiceID
ELSE
INSERT INTO TableC
(UserID,ServiceID,Scheduled)
VALUES
(S_UserID,S_ServiceID,1)
END
If you are on newer version SQL Server (this question is tagged with sql-server-2016), then you can use the MERGE statement.
Here is a somewhat simplified example based on your post:
--create test tables
CREATE TABLE #a (UserID int);
CREATE TABLE #b (UserID int, ServiceID int);
CREATE TABLE #c (UserID int, ServiceID int, Scheduled int);
--insert some data matching the question
INSERT INTO #a
VALUES(1), (2), (3), (4), (5);
INSERT INTO #b
VALUES (1, 33), (2, 44), (3, 55)
INSERT INTO #c
VALUES(2, 44, 3), (4, 66, 1)
--ensure we have data
SELECT * FROM #a
SELECT * FROM #b
SELECT * FROM #c
--use the MERGE statement
MERGE #c AS tgt
USING(SELECT a.UserID, b.ServiceID FROM #a a JOIN #b b ON a.UserID = b.UserID) src
ON (tgt.UserID = src.UserID AND tgt.ServiceID = src.ServiceID)
WHEN NOT MATCHED THEN
INSERT (UserID, ServiceID, Scheduled)
VALUES(src.UserID, src.ServiceID, 1)
WHEN MATCHED THEN
UPDATE
SET Scheduled += 1;
--check what's been inserted/updated
SELECT * FROM #c
Be aware that MERGE sometimes gave performance implications, so use it with caution. More info about MERGE here.
Hope this helps!
One way to do this is by executing different statements for inserting and updating.
-- INSERT INTO Temporary Table
SELECT sa.UserID,
ce.ServiceID
INTO #tempC
FROM TableA a
JOIN TableB b ON b.EventID = a.EventID
WHERE a.EventID = 1892286
AND a.Attendance IN (1,2)
-- UPDATE Existing ROWS
UPDATE c
SET c.Scheduled = c.Scheduled + 1
FROM #tempC c
INNER JOIN TableC d
ON c.UserID = d.UserID
AND c.ServiceID = d.ServiceID
-- INSERT new rows
INSERT INTO TableC (UserID, ServiceID,Scheduled)
SELECT c.UserID, c.ServiceID, 1
FROM #tempC c
LEFT JOIN TableC d
ON c.UserID = d.UserID
AND c.ServiceID = d.ServiceID
WHERE d.UserID IS NULL
-- DROP temporary table
DROP TABLE #tempC
A loop is not efficient
ca and ce are not valid alias so I had to guess the valid alias
Start with a backup in case you do it wrong.
update c
set c.serviceID = c.serviceID + 1
FROM TableA a
JOIN TableB b
ON b.EventID = a.EventID
and a.EventID = 1892286
AND a.Attendance IN (1,2)
join TableC c
on c.UserID = a.UserID
and c.ServiceID = b.ServiceID;
INSERT INTO TableC (UserID, ServiceID, Scheduled)
SELECT a.UserID, b.ServiceID, 1
FROM TableA a
JOIN TableB b
ON b.EventID = a.EventID
and a.EventID = 1892286
AND a.Attendance IN (1,2)
where not exists (select 1
from TableC c
where c.UserID = a.UserID
and c.ServiceID = b.ServiceID
);

How to retrieve duplicate records and delete them in table A, also insert these duplicate records in another table B (in postgres)

how to retrieve duplicate records and delete them in table A, also insert these retrieved duplicate records in another table B (in postgres db)
SQL query's are required for my project.
To delete duplicates without having a unique column you can use the ctid virtual column which is essentially the same thing as the rowid in Oracle:
delete from table_A t1
where ctid <> (select min(t2.ctid)
from table_A t2
where t1.unique_column = t2.unique_column);
You can use the returning clause to get the deleted rows and insert them into the other table:
with deleted as (
delete from table_A x1
where ctid <> (select min(t2.ctid)
from table_A t2
where t1.unique_column = t2.unique_column);
returning *
)
insert into table_B (col_1, col_2)
select unique_column, some_other_column
from deleted;
If you further want to see those deleted rows, you can throw in another CTE:
with deleted as (
delete from table_A x1
where ctid <> (select min(t2.ctid)
from table_A t2
where t1.unique_column = t2.unique_column);
returning *
), moved as (
insert into table_B (col_1, col_2)
select unique_column, some_other_column
from deleted
returning *
)
select *
from moved;

Postgresql how to select values in the column from one table that are only available in another table?

I am using Postgresql and need to query two tables like this:
Table1
ID Bill
A 1
B 2
B 3
C 4
Table2
ID
A
B
I want a table with all the columns in Table1 but keeping only the records with IDs that are available in Table2 (A and B in this case). Also, Table2's ID is unique.
ID Bill
A 1
B 2
B 3
Which join I should use or if I can use WHERE statement?
Thanks!
SELECT Table1.*
FROM Table1
INNER JOIN Table2 USING (ID);
or
SELECT *
FROM Table1
WHERE ID IN (SELECT ID FROM Table2);
but the first one is better for performance reason.
SELECT *
FROM Table1
WHERE EXISTS (
SELECT 1 FROM Table2 WHERE Table2.ID = Table1.ID LIMIT 1
)

Transfer existing data after database model changes (PostgreSQL)

I'm trying to remove an association table from my database. It contains two columns (a_id and b_id) referencing the tables a and b.
This table is unnecessary because in fact it's an OneToMany relation. So I added column a_id in table b.
My problem: How can I transfer the existing entrys from assoc_a_b to b.a_id?
SELECT DISTINCT b.id, a.id FROM table_a AS a
JOIN assoc_a_b AS assoc ON a.id = assoc.a_id
JOIN table_b AS b ON b.id = assoc.b_id;
This select statement works. Can it be combined with an UPDATE statement? The UPDATE statement would look someway like:
UPDATE b SET a_id = a.id WHERE id = b.id;
using a.id and b.id from the select statement above.
update b set a_id = (SELECT assoc_a_b.a_id from assoc_a_b where assoc_a_b.b_id = b.id)