INSERT if NOT EXISTS, But DELETE if it EXISTS - sql

I have the following query to update a table record setting new foreignKey if that foreignKey and foreignKey2 did not already exist. This should work great, however, how can I modify to delete that particular pkID record if it DOES exist?
table structure:
+----------------+
| table |
+----------------+
| pkID |
| foreignKey |
| foreignKey2 |
+----------------+
query:
UPDATE table a
SET a.foreignKey = 2
WHERE a.pkID = 1234
AND NOT EXISTS (
SELECT 1
FROM table b
WHERE b.foreignKey = 2
AND b.foreignKey2 = a.foreignKey2
)

You can delete if it exists, and only insert (instead of update since the record doesn't exist to be deleted) otherwise. But it is not clear what the 3rd value should be.
DELETE tbl where pkID = 1234;
if ##ROWCOUNT = 0
INSERT tbl(foreignKey, pkID, foreignKey2)
VALUES (2, 1234, ??)

You need MERGE. Take a look here(there is an example with the same task)

Related

Audited table and foreign key

I have a database with multiples tables that must be audited.
As an example, I have a table of objects defined with an unique ID, a name and a description.
The name will always be the same. It is not possible to update it. "ObjectA" will always be "ObjectA".
As you see the name is not unique in the database but only in the logic.
The rows "from", "to" and "creator_id" are used to audit the changes. "from" is the date of the change, "to" is the date when a new row has been added and is null when it is the latest row. "creator_id" is the ID of the user that made the change.
+----+----------+--------------+----------------------+----------------------+------------+
| id | name | description | from | to | creator_id |
+----+----------+--------------+----------------------+----------------------+------------+
| 1 | ObjectA | My object | 2021-05-30T00:05:00Z | 2021-05-31T05:04:36Z | 18 |
| 2 | ObjectB | My desc | 2021-05-30T02:07:25Z | null | 15 |
| 3 | ObjectA | Super object | 2021-05-31T05:04:36Z | null | 20 |
+----+----------+--------------+----------------------+----------------------+------------+
Now I have another table that must have a foreign key to this object table based on the "unique" object name.
+----+---------+-------------+
| id | foo | object_name |
+----+---------+-------------+
| 1 | blabla | ObjectA |
| 2 | wawawa | ObjectB |
+----+---------+-------------+
How can I create this link between those 2 tables ?
I already tried to create another table with a uuid and add a column "unique_identifier" in the object table. The foreign key will be then linked to this uuid table and not the object table. The issue is that I have multiple tables with this problem and I will have to create the double number of table.
It is also possible to use the object ID as the FK instead of the name but it would mean that I must update every table with that FK with the new ID when updating an object.
By the SQL standard, a foreign key must reference either the primary key or a unique key of the parent table. If the primary key has multiple columns, the foreign key must have the same number and order of columns. Therefore the foreign key references a unique row in the parent table; there can be no duplicates.
Another solution is to use trigger, you can check the existence of the object in objects table before you insert into another table.
Update : Adding code
Prepare the tables and create trigger: (I have only included 3 columns in Objects table for simplicity. In trigger, I am just printing the error in else part, you could raise error suing RAISEERROR function to return the error to client)
Create table AuditObjects(id int identity (1,1),ObjectName varchar(20), ObjectDescription varchar(100) )
Insert into AuditObjects values('ObjectA','description ObjectA Test')
Insert into AuditObjects values('ObjectB','description ObjectB Test')
Insert into AuditObjects values('ObjectC','description ObjectC Test')
Insert into AuditObjects values('ObjectB','description ObjectB Test')
Insert into AuditObjects values('ObjectB','description ObjectB Test')
Insert into AuditObjects values('ObjectA','description ObjectA Test')
Create table ObjectTab2 (id int identity (1,1),foo varchar(200), ObjectName varchar(20))
go
CREATE TRIGGER t_CheckObject ON ObjectTab2 INSTEAD OF INSERT
AS BEGIN
Declare #errormsg varchar(200), #ObjectName varchar(20)
select #ObjectName = objectname from INSERTED
if exists(select 1 from AuditObjects where objectname = #ObjectName)
Begin
INSERT INTO ObjectTab2 (foo, Objectname)
Select foo, Objectname
from INSERTED
End
Else
Begin
Select #errormsg = 'Object '+objectname+ ' does not exists in AuditObjects table'
from Inserted
print(#errormsg)
End
END;
Now if you try to insert a row in ObjectTab2 with object name as "ObjectC", insert will be allowed as "objectC" is present in audit table.
Insert into ObjectTab2 values('blabla', 'ObjectC')
Select * from ObjectTab2
id foo ObjectName
----------- ------ --------------------
1 blabla ObjectC
However, if you try to enter "ObjectD", it will not make an insert and give error msg in output.
Insert into ObjectTab2 values('Inserting ObjectD', 'ObjectD')
Object ObjectD does not exists in AuditObjects table
Well its not what you asked for but give you the same functionality and results.
Can you not still go ahead with linking the two tables based on 'object name'. The only difference would be - when you join the two tables, you would get multiple records from table1 (the first table you were referencing). You may then add filter condition based on from and to, as per your requirements.
Post Edit -
What I meant is, you can still achieve the desired results without introducing Foreign Key in this scenario -
Let's call your tables - Table1 and Table2
--Below will give you all records from Table1
SELECT T2.*, T1.description, T1.creator_id, T1.from, T1.to
FROM TABLE2 T2
INNER JOIN TABLE1 T1 ON T2.OBJECT_NAME = T1.NAME;
--Below will give you ONLY those records from Table1 whose TO is null
SELECT T2.*, T1.description, T1.creator_id, T1.from, T1.to
FROM TABLE2 T2
INNER JOIN TABLE1 T1 ON T2.OBJECT_NAME = T1.NAME
WHERE T1.TO IS NULL;
I decided to go with an additional table to have this final design:
Table "Object"
+-------+--------------------------------------+---------+--------------+----------------------+----------------------+------------+
| id PK | identifier FK | name | description | from | to | creator_id |
+-------+--------------------------------------+---------+--------------+----------------------+----------------------+------------+
| 1 | 123e4567-e89b-12d3-a456-426614174000 | ObjectA | My object | 2021-05-30T00:05:00Z | 2021-05-31T05:04:36Z | 18 |
| 2 | 123e4567-e89b-12d3-a456-524887451057 | ObjectB | My desc | 2021-05-30T02:07:25Z | null | 15 |
| 3 | 123e4567-e89b-12d3-a456-426614174000 | ObjectA | Super object | 2021-05-31T05:04:36Z | null | 20 |
+-------+--------------------------------------+---------+--------------+----------------------+----------------------+------------+
Table "Object_identifier"
+--------------------------------------+
| identifier PK |
+--------------------------------------+
| 123e4567-e89b-12d3-a456-426614174000 |
| 123e4567-e89b-12d3-a456-524887451057 |
+--------------------------------------+
Table "foo"
+-------+--------+--------------------------------------+
| id PK | foo | object_identifier FK |
+-------+--------+--------------------------------------+
| 1 | blabla | 123e4567-e89b-12d3-a456-426614174000 |
| 2 | wawawa | 123e4567-e89b-12d3-a456-524887451057 |
+-------+--------+--------------------------------------+

How to get IDs of my batch update SQL Server

How can I get the IDs of affected rows on my batch update? As I'm trying to insert on table tbl.history of all the update/transactions.
Below is my sample table:
table tbl.myTable
+------+-----------+------------+
| ID | Amount | Date |
+------+-----------+------------+
| 1 | 100 | 01/01/2019 |
+------+-----------+------------+
| 2 | 200 | 01/02/2019 |
+------+-----------+------------+
| 3 | 500 | 01/01/2019 |
+------+-----------+------------+
| 5 | 500 | 01/05/2019 |
+------+-----------+------------+
Here's my batch update query:
Update tbl.myTable set Amount = 0 where Date = '01/01/2019'
with the query it will update/affect the two data with ID 1 and 3. How can I get those ID to insert it in another table (tbl.history)?
Use the OUTPUT clause. It provides you with a "table" named deleted which contains the values before the update, and a "table" named inserted which contains the new values.
So, you can run
Update tbl.myTable set Amount = 0
output inserted.*,deleted.*
where Date = '01/01/2019'
To understand how it works, succeeding this, you can now create a temporary table and OUTPUT the fields you want INTO it:
Update tbl.myTable set Amount = 0
output inserted.*,deleted.* into temp_table_with_updated
where Date = '01/01/2019'
You can do this by using OUTPUT
declare #outputIDs as TABLE
(
ID int
)
Update tbl.MyTable Set [Amount] = 0
OUTPUT INSERTED.ID into #outputIDs
WHERE [Date] = '01/01/2019'
The #outputIDs table will have the two updated IDs.
Use a caching mechanism (table variable, cte etc)
declare #temp table (id int)
insert into #temp select id from tbl.myTable where Date = '01/01/2019'
update tbl.myTable set Amount=0 where id in (select id from #temp)
-- do more stuff with the id's

update a table from another table and add new values

How would I go about updating a table by using another table so it puts in the new data and if it doesnt match on an id it adds the new id and the data with it. My original table i much bigger than the new table that will update it. and the new table has a few ids that aren't in the old table but need to be added.
for example I have:
Table being updated-
+-------------------+
| Original Table |
+-------------------+
| ID | Initials |
|------+------------|
| 1 | ABC |
| 2 | DEF |
| 3 | GHI |
and...
the table I'm pulling data from to update the other table-
+-------------------+
| New Table |
+-------------------+
| ID | Initials |
|------+------------|
| 1 | XZY |
| 2 | QRS |
| 3 | GHI |
| 4 | ABC |
then I want my Original table to get its values that match up to be updated by the new table if they have changed, and add any new ID rows if they aren't in the original table so in this example it would look like the New Table.
+-------------------+
| Original Table |
+-------------------+
| ID | Initials |
|------+------------|
| 1 | XZY |
| 2 | QRS |
| 3 | GHI |
| 4 | ABC |
You can use MERGE statement to put this UPSERT operation in one statement but there are issues with merge statement I would split it into two Statements, UPDATE and INSERT
UPDATE
UPDATE O
SET O.Initials = N.Initials
FROM Original_Table O INNER JOIN New_Table N
ON O.ID = N.ID
INSERT
INSERT INTO Original_Table (ID , Initials)
SELECT ID , Initials
FROM New_Table
WHERE NOT EXISTS ( SELECT 1
FROM Original_Table
WHERE ID = Original_Table.ID)
Important Note
Reason why I suggested to avoid using merge statement read this article Use Caution with SQL Server's MERGE Statement by Aaron Bertrand
You need to use the MERGE statement for this:
MERGE original_table AS Target
USING updated_table as Source
ON original_table.id = updated_table.id
WHEN MATCHED THEN UPDATE SET Target.Initials = Source.Initials
WHEN NOT MATCHED THEN INSERT(id, Initials) VALUES(Source.id, Source.Initials);
You have not specified, what happens in case the valuesin original table are not found in the updated one. But, just in case, you can add this to remove them from original table:
WHEN NOT MATCHED BY SOURCE
THEN DELETE
if you can use loop in PHP and go through all tables and copy one by one to another table.
another option
DECLARE #COUT INT
SET #COUT = SELECT COUNT(*) FROM New_Table
WHILE (true)
BEGIN
IF #COUT = 0
BREAK;
SET #COUT = #COUT - 1
DECLARE #id INT
DECLARE #ini VARCHAR(20)
SET #id = (SELECT id FROM New_Table);
SET #ini = (SELECT Initials FROM New_Table);
IF (SELECT COUNT(*) FROM Original_Table WHERE id=#id ) > 0
UPDATE SET ID = #id,Initials = #ini FROM Original_Table WHERE id = #id;
insert into Original_Table values(#id,#ini);
END
GO

Inserting a row at the specific place in SQLite database

I was creating the database in SQLite Manager & by mistake I forgot to mention a row.
Now, I want to add a row in the middle manually & below it the rest of the Auto-increment keys should be increased by automatically by 1 . I hope my problem is clear.
Thanks.
You shouldn't care about key values, just append your row at the end.
If you really need to do so, you could probably just update the keys with something like this. If you want to insert the new row at key 87
Make room for the key
update mytable
set key = key + 1
where key >= 87
Insert your row
insert into mytable ...
And finally update the key for the new row
update mytable
set key = 87
where key = NEW_ROW_KEY
I would just update IDs, incrementing them, then insert record setting ID manually:
CREATE TABLE cats (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name VARCHAR
);
INSERT INTO cats (name) VALUES ('John');
INSERT INTO cats (name) VALUES ('Mark');
SELECT * FROM cats;
| 1 | John |
| 2 | Mark |
UPDATE cats SET ID = ID + 1 WHERE ID >= 2; -- "2" is the ID of forgotten record.
SELECT * FROM cats;
| 1 | John |
| 3 | Mark |
INSERT INTO cats (id, name) VALUES (2, 'SlowCat'); -- "2" is the ID of forgotten record.
SELECT * FROM cats;
| 1 | John |
| 2 | SlowCat |
| 3 | Mark |
Next record, inserted using AUTOINCREMENT functionality, will have next-to-last ID (4 in our case).

Create SQL table with the data from another table

How do I create a table using data which is already present in another table (copy of table)?
The most portable means of copying a table is to:
Create the new table with a CREATE TABLE statement
Use INSERT based on a SELECT from the old table:
INSERT INTO new_table
SELECT * FROM old_table
In SQL Server, I'd use the INTO syntax:
SELECT *
INTO new_table
FROM old_table
...because in SQL Server, the INTO clause creates a table that doesn't already exist.
If you are using MySQL, you may want to use the CREATE TABLE ... AS SELECT syntax to create a table defined by the columns and data types of another result set:
CREATE TABLE new_table AS
SELECT *
FROM old_table;
Example:
CREATE TABLE old_table (id int, value int);
INSERT INTO old_table VALUES (1, 100), (2, 200), (3, 300), (4, 400);
CREATE TABLE new_table AS
SELECT *
FROM old_table;
SELECT * FROM new_table;
+------+-------+
| id | value |
+------+-------+
| 1 | 100 |
| 2 | 200 |
| 3 | 300 |
| 4 | 400 |
+------+-------+
4 rows in set (0.00 sec)
DESCRIBE new_table;
+-------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id | int(11) | YES | | NULL | |
| value | int(11) | YES | | NULL | |
+-------+---------+------+-----+---------+-------+
2 rows in set (0.03 sec)
For other DBMSes, that do not support this syntax, you may want to check out #OMG Ponies' answer for a more portable solution.
For SQL Server
SELECT *
INTO NewTable
FROM OldTable
For Sql server:
Create new table from existing table :
CREATE TABLE new_table_name AS
SELECT [col1,col2,...coln] FROM existing_table_name [WHERE condition];
Insert values into existing table form another existing table using Select command :
SELECT * INTO destination_table FROM source_table [WHERE conditions];
SELECT *
INTO newtable [IN externaldb]
FROM oldtable
[ WHERE condition ];
Insert values into existing table form another existing table using
Insert command :
INSERT INTO table2 (column1, column2, column3, ...)
SELECT column1,column2, column3, ... FROM table1 [WHERE condition];
if u want exact schema of existing table to new table and all values of existing table want to be inserted into your new table then execute below two queries :
create table new_table_name like Old_table_name;
select * into new_table_name from Old_table_name;
LIKE works only for base tables, not for views.
If the original table is a TEMPORARY table, CREATE TABLE ... LIKE does not preserve TEMPORARY. To create a TEMPORARY destination table, use CREATE TEMPORARY TABLE ... LIKE.
for more details click here