SQL Update statement that deletes rows if key clash - sql

I have a table that has two columns Hotel_Guest_ID and Guest_ID that links Guest records to the Hotel details of that guest. The table has the constraint that each pair needs to be unique.
I now have a second table of Prime_ID and Duplicate_ID that was generated after cleaning up the Guest table of duplicates. I would like to go through the Booking table and if the Hotel.Guest_ID is found as a Duplicate_ID, to then replace it with Prime_ID.
update b
set h.Guest_ID = gd.Prime_ID
from Hotel as h
join Guest_Duplicates as gd
on h.Guest_ID = gd.Duplicate_ID
However this fails as often a Prime_ID already has a record with a specific Guest, at which point I want to just delete this row instead of updating it.
Is there a nice way to do this in a single pass or would I have to delete potential clash rows first, then update in a second query?

What you are looking for is a MERGE statement. You can insert, update and delete using a single statement. Here's an example -
MERGE Table1 AS t1
USING Table2 AS t2
ON t1.GuestID = t2.DuplicateID
WHEN MATCHED AND (any condition)
THEN DELETE
WHEN MATCHED
THEN UPDATE SET (assign statement)
WHEN NOT MATCHED
THEN
INSERT(column names)
VALUES(values to be inserted);

Related

How to upsert when using data from a sub-query (Postgres)

I have two tables:
assignments {recceptacleId, assignedCarrier}
rls_permissions {receptacleId, rlsUserId}
An assignment in this context is any receptacle to airline carrier relationship.
Whenever a new assignment comes into the assignments table, I'd like to upsert (insert if new row or update if it's an existing receptacle being assigned to a new airline carrier) my rls_permissions table.
The issue I'm having with upsert, specifically ON CONFLICT ON CONSTRAINT, is that my insert statement contains a sub-query for the data to be inserted and therefore I'm not sure how to write the DO UPDATE SET part of the statement
I've tried using 'excluded' to try and single out the assignedCarrier that I want to update based on the previous conflict however I keep receiving "ERROR: column excluded.receptacleId does not exist"
My pkey looks like this:
CREATE UNIQUE INDEX rls_permissions_pkey ON rls_permissions("receptacleId" text_ops);
Dummy data could be:
receptacleID assignedCarrier
aaaaaaaaaa00 AA
Where AA is "American Airlines"
INSERT INTO rls_permissions ("receptacleId","rlsUserId")
SELECT DISTINCT assignments."receptacleId", assignments."assignedCarrier"
FROM assignments
ON CONFLICT ON CONSTRAINT rls_permissions_pkey
DO UPDATE SET "rlsUserId" = (SELECT DISTINCT assignments."assignedCarrier"
FROM assignments
WHERE assignments."receptacleId" = excluded."receptacleId");
The excepted result is that if no conflict, the data returned from the sub-query is inserted into a new row on the permissions table.
If there is a conflict, I'd like to update ONLY the newly assigned carrier, and not update or insert a new line since that receptacle already exists.
You don't need a subquery in the UPDATE part. You can access the values for the INSERT part through the excluded keyword.
INSERT INTO rls_permissions ("receptacleId","rlsUserId")
SELECT DISTINCT assignments."receptacleId", assignments."assignedCarrier"
FROM assignments
ON CONFLICT ON CONSTRAINT rls_permissions_pkey
DO UPDATE SET "rlsUserId" = excluded."rlsUserId";
the reference to excluded."rlsUserId" refers to the value that would have been inserted into the column rlsUserId and thus it's the value retrieved through assignments."assignedCarrier" from your SELECT statement.

How to Update a Single record despite multiple Occurances of the same ID Number?

I have a table that looks like the below table:
Every time the user loan a book a new record is inserted.
The data in this table is derived or taken from another table which has no dates.
I need to update this tables based on the records in the other table: Meaning I only need to update this table based on what changes.
Example: Lets say the user return the book Starship Troopers and the book return is indicated to Yes.
How do I update just that column?
What I have tried:
I tried using the MERGE Statement but it works only with unique rows of data, meaning you get an error if the same ID appears more than once.
I also tried using a basic UPDATE Statement and a JOIN but that's not going well.
I am asking because I have ran out of ideas.
Thanks for reading
If you need to update BooksReturn in target table based on the same column in source table
UPDATE t
SET t.booksreturn = s.booksreturn
FROM target t JOIN source s
ON t.userid = s.userid
AND t.booksloaned = s.booksloaned
Here is SQLFiddle demo
You can do this by simple Update & Insert statement.....
Two table A & B
From B you want to insert data into A if not exists other wise Update that data....
,First Insert into temp table....
SELECT *
INTO #MYTEMP
FROM B
WHERE BOOKSLOANED NOT IN (SELECT BOOKSLOANED
FROM A)
,Second Check data and insert into A.
INSERT INTO A
SELECT *
FROM #MYTEMP
And at last write one simple update statement which update all data of A. If any change then it also reflect to that data otherwise data as it is.
You can also update from #MYTEMP table.

Creating a Trigger which will insert record in a table on update of another table

Suppose I have tables T1 and T2
Columns of T1 -->Value
Columns of T2 -->OldValue NewValue
What I require is a trigger which will insert a record in T2 on updation of T1 , I need to know the old value and new value also , I have never used triggers before , so can any help me with this , how do I go about creating this trigger.Is it possible ,thanks.
Well, you start writing a trigger with CREATE TRIGGER:
CREATE TRIGGER NameOfTheTriggerPlease
…
The table that should trigger the additional action is T1 so the trigger should be defined ON that table:
CREATE TRIGGER T1OnUpdate /* that's just an example,
you can use a different name */
ON T1
…
The action that the trigger should be invoked on is UPDATE and the timing is AFTER the update, so…
CREATE TRIGGER T1OnUpdate
ON T1
AFTER UPDATE
…
Now's the time to introduce the body of the trigger, i.e. the statements that should actually be executed by the trigger. You introduce the body with the AS keyword followed by the statements themselves.
In your case, there would be just one statement, INSERT, which is obvious. What's not so obvious is how we are going to access the old and the new values. Now, SQL Server offers you two virtual tables, INSERTED and DELETED, and you can easily guess that the former contains all the new values and the latter the old ones.
These tables have the same structure as the table the trigger is assigned to, i.e. T1. They only contain rows that were affected by the particular UPDATE statement that invoked the trigger, which means there may be more than one. And that, in turn, means that you need to have some primary key or a unique column (or a set of columns) in your T1 table that you can use in the trigger to match deleted and inserted rows. (In fact, you might also need your T2 table to have a column that would reference the T1's primary key, so you could later establish which row of T1 had which values stored in T2.)
For the purposes of this answer, I'm going to assume that there's a primary key column called PK and a foreign key column of the same name in T2. And the INSERT statement then might look like this:
CREATE TRIGGER T1OnUpdate
ON T1
AFTER UPDATE
AS
INSERT INTO T2 (PK, OldValue, NewValue)
SELECT i.PK, i.Value, d.Value
FROM INSERTED i INNER JOIN DELETED d ON i.PK = d.PK
One last (but not least) thing to remember: the entire CREATE TRIGGER statement should be the only one in the batch, i.e. there should be no statements preceding the CREATE TRIGGER keywords (but you can put comments there) and, likewise, everything after the AS keyword is considered part of the trigger's body (but you can put the GO delimiter to indicate the end of the statement if you are running the script in SQL Server Management Studio, for instance).
Useful reading:
CREATE TRIGGER (Transact-SQL)
I'm not going to build the whole thing for you (no fun, right?) but I can point you in the right direction
create trigger logUpdate
on T1
After update
as
begin
insert into T2...
--here is just an example
select * from deleted --the DELETED table contains the OLD values
select * from inserted --the INSERTED table contains the NEW values
end
remember that DELETED and INSERTED are internal tables that contains old and new values. On a update trigger, they both exist. On a insert trigger, DELETED will be null because there is nothing being delete. Same logic on a delete trigger, the INSERTED will be empty
EDIT:
answering your question: no matter how many fields you update, your DELETED and INSERTED tables you have all the columns of all the rows affected. Of course, if you update only one column, all the other will have the same value on DELETED and INSERTED
create trigger T_UPD_T1
on T1 FOR update
as
insert into T2 select deleted.value, inserted.value from inserted, deleted

SQL Server : Changing an ID to an already existing one (merge) HOW TO?

I have two records that are the same in a table (entered by mistake). Both IDs are used as foreign key in other tables. I want to update the foreign keys to one "orignal" element and delete the other one. The problem is that it's possible that the UPDATE of the foreign key will generate a constraint exception (if the foreign key with the original element already exists).
So I would do something like :
UPDATE foreignTable SET id=1 WHERE id=2
DELETE FROM firstTable WHERE id=2
The problem is with the UPDATE, I would like to do the UPDATE if the row doesn't already exists, if yes just DELETE the row. How do you do that?
UPDATE ft
SET id = 1
FROM foreignTable ft
LEFT JOIN foreignTable ft2
ON ft.PrimaryKey = ft2.PrimaryKey
AND ft2.id = 1
WHERE ft.id = 2
AND ft2.PrimaryKey IS NULL
DELETE FROM foreignTable
WHERE id = 2
If you are using SQL Server 2008, have a look at the MERGE statement.
It allows you to insert the missing rows, update the existing one and delete those who have to be deleted.
http://technet.microsoft.com/en-us/library/bb510625.aspx
If you use an older version, you will have to copy your data to a temporary table, delete the data from the existing one and reinsert from the temp table.
Be sure to use a transaction and make a backup of your table to avoid data loss.

Foreign Key reference in mysql innodb

Simply put: I have a table with 2 columns: one is username and other is nickname.
I have another table where I have 2 columns: one is username and other is countNicknames.
I want countNicknames to contain the number of nicknames each user has (and whenever I insert a new nickname to the table1, the value under table2.countNicknames will automatically update.
Can you please write down how to construct the second table to reference the first one?
Why not just count when you need the value?
I want countNicknames to contain the number of nicknames each user has (and whenever I insert a new nickname to the table1, the value under table2.countNicknames will automatically update.
This is effectively what a view will do.
CREATE OR REPLACE VIEW user_nickname_count AS
SELECT t.username,
COUNT(*) 'countNicknames'
FROM TABLE t
GROUP BY t.username
A view looks like a table, so you cah join to it if needed. And the only overhead is that it effectively is just a SELECT query being run - the values are calculated on demand, rather than having to setup triggers.
For more info on views, see the documentation.
Well, #Lasse's suggestion is better, but ... there is two other another options...
Does MySql have triggers? If it does, then you would add an Insert, Update, Delete trigger on the first table that updates (or inserts or deletes as necessary) the second table's CountNickNames attribute every time a record is inserted, Updated or deleted from the first table...
Create Trigger NickNameCountTrig On NickNameCountTable
For Insert, Update, Delete
As
Update nct Set
CountNickNames =
(Select Count() From FirstTable
Where Name = nct.Name)
From NickNameCountTable nct
Where Name In (Select Distinct Name from inserted
Union
Select Distinct Name From deleted)
-- -----------------------------------------------
Insert NickNameCountTable (Name, CountNickNames)
Select name, count() from FirstTable ft
Where Not Exists
(Select * From NickNameCountTable
Where Name = ft.Name)
-- ------ This is optional -----------------------
Delete NickNameCountTable
Where CountNickNames = 0
Does MySql have indexed views (or some equivilent)? apparently not - so never mind this option ....