SQL - Insert Where Not Exists - sql

I have what I thought to be a completely trivial query - insert values into a table if a value with a matching ID does not exist:
BEGIN
INSERT INTO [dbo].[Contact_Categories](Contact_Category_ID, Description)
VALUES (1, 'Internal')
WHERE NOT EXISTS( SELECT * FROM [dbo].[Contact_Categories] WHERE Contact_Category_ID = 1)
END
I get an error around the where statement. Why? How do I accomplish my goal?

Your problem comes from WHERE being valid for UPDATE/SELECT but INSERT just doesn’t understand what it means.
But you can get around this. Change your code to be like:
BEGIN
INSERT INTO [dbo].[Contact_Categories](Contact_Category_ID, Description)
SELECT 1, 'Internal'
WHERE NOT EXISTS( SELECT * FROM [dbo].[Contact_Categories] WHERE Contact_Category_ID = 1)
END

The correct way to handle this is by using a unique index/constraint:
create unique index unq_Contact_Categories_Category_Id on Contact_Categories(Contact_Category_ID);
The database will then guarantee the uniqueness for the column. This prevents race conditions.
You can catch this using try/catch:
BEGIN TRY
INSERT INTO [dbo].[Contact_Categories](Contact_Category_ID, Description)
SELECT 1, 'Internal';
END TRY
BEGIN CATCH
PRINT 'Ooops'; -- you can even raise an error if you like.
END CATCH;

Why not an If statement?
IF NOT EXISTS
(select * from [dbo].[Contact_Categories] WHERE Contact_Category_ID = 1)
begin
insert into [dbo].[Contact_Categories] (Contact_Category_ID, Description)
values (1, 'Internal')
end
This has the advantage of not doing anything if the value exists. Similar to answer provided here: SQL Server IF NOT EXISTS Usage?

I would do:
INSERT INTO [dbo].[Contact_Categories](Contact_Category_ID, Description)
VALUES (1, 'Internal')
WHERE 1 NOT IN ( SELECT Contact_Category_ID FROM [dbo].[Contact_Categories])

Try to replace your query with:
BEGIN
IF NOT EXISTS (SELECT * FROM [dbo].[Contact_Categories] WHERE Contact_Category_ID = 1)
INSERT INTO [dbo].[Contact_Categories](Contact_Category_ID,Description) VALUES (1, 'Internal')
END

I also had the same problem, this is my solution.
insert into Contact_Categories (Contact_Category_ID, Description)
select 1, 'Internal'
where not exists
(select * from Contact_Categories where Contact_Category_ID = 1 and Description = 'Internal');

Related

After INSERT INTO in DO block query an SELECT

I would like to insert stuff into two different tables. This works also everything. However, I would like to make a SELECT at the end of the two INSERT INTO. However, this does not work. Does anyone have an idea how to put this into a statement?
DO $$
DECLARE
myid tbl_groupchat.id%TYPE;
BEGIN
INSERT INTO public.tbl_groupchat(
chatname, createdate)
VALUES ('test4', CURRENT_DATE)
RETURNING id INTO myid;
INSERT INTO public.tbl_participants (
id_groupchat, id_profile)
VALUES (myid, 2);
SELECT *
FROM public.tbl_participants
WHERE id_profile = 2;
END $$
output
ERROR: query has no destination for result data
HINT: If you want to discard the results of a SELECT, use PERFORM instead.
CONTEXT: PL/pgSQL function inline_code_block line 13 at SQL statement
SQL state: 42601
tbl_participants
tbl_groupchat
I tried this
RETURN(SELECT id_profile
FROM public.tbl_participants
WHERE id_profile = 2);
output
ERROR: RETURN cannot have a parameter in function returning void
LINE 13: RETURN(SELECT id_profile
^
SQL state: 42804
Character: 279
No need for a DO block. This can be done in plain SQL using a data modifying CTE
with new_group_chat as (
INSERT INTO public.tbl_groupchat(
chatname, createdate)
VALUES ('test4', CURRENT_DATE)
RETURNING id
)
INSERT INTO public.tbl_participants (id_groupchat, id_profile)
select id, 2
from new_group_chat
returning *;
The returning * in the final select will return the complete row that was just inserted, making an additional select unnecessary.
Alternatively just use lastval()
INSERT INTO public.tbl_groupchat(
chatname, createdate)
VALUES ('test4', CURRENT_DATE);
INSERT INTO public.tbl_participants (id_groupchat, id_profile)
values (lastval(), id);
SELECT *
FROM public.tbl_participants
WHERE id_profile = 2;
Online example
Here is the solution using cursor:
Assign the output of your select query output in cursor and fetch it out of DO block but with in same session.
Considering your insert and select queries are working fine individually then write your do block like below.
DO $$
DECLARE
myid tbl_groupchat.id%TYPE;
ref_cursor REFCURSOR := 'mycursor';
BEGIN
INSERT INTO public.tbl_groupchat(
chatname, createdate)
VALUES ('test4', CURRENT_DATE)
RETURNING id INTO myid;
INSERT INTO public.tbl_participants (
id_groupchat, id_profile)
VALUES (myid, 2);
OPEN ref_cursor for SELECT *
FROM public.tbl_participants
WHERE id_profile = 2;
END $$;
FETCH ALL from mycursor;

simple SQL Statement wrong

I want to INSERT my Values only if the 2 conditions are not correct.
Insert INTO ArtikelNr VALUE (…)
WHERE "3434" NOT IN (Select AriktelNr From Artikel) AND
isa_id = "3847"
What is wrong with this Code?
INSERT ... WHERE ... is not valid SQL.
WHERE only goes with SELECT.
If you want to validate both values for a single record:
if not exists (select 1 from Artikel where AriktelNr='3434' and isa_id='3847')
begin
insert into Artikel (<columns>) values (<values>)
end
If you want to validate both values on different records:
if not exists (select 1 from Artikel where AriktelNr='3434')
begin
if not exists (select 1 from Artikel where isa_id='3847')
begin
insert into Artikel (<columns>) values (<values>)
end
end
you can try and convert your "Values" part of the insert statement,
to an "Insert into Select" format, should work.
Sample:
drop table if exists test;
create table test (x int);
insert into test (x) values (1),(2),(3);
insert into test (x)
select 2
where 2 not in (select x from test)
;

SQL Server : INSERT with a condition

I want to insert into my Files-Favorites table only if the values I'm trying to pass isn't in there already.
I tried:
INSERT INTO [Files-Favorites](fileID,auditorID)
VALUES ('1', '34')
WHERE (fileID != '1'
AND auditorID != '34')
This doesn't work. I'm trying not to INSERT duplicate values. How do I pull this off? This is for a Microsoft SQL Server 2005.
Thank you
Try using if not exists
IF NOT EXISTS(SELECT * FROM [Files-Favorites] WHERE fileID = '1' AND auditorID = '34')
BEGIN
INSERT INTO [Files-Favorites](fileID, auditorID)
VALUES('1', '34')
END
I would use a composite (multi-column) key rather than checking on each insert
ALTER TABLE [Files-Favorites] ADD CONSTRAINT unique_1 UNIQUE(fileID,auditorID)
We can use EXISTS with sub query to check the data existence and insert accordingly:
INSERT INTO [Files-Favorites](fileID,auditorID)
SELECT fileID, auditorID FROM (
SELECT '1' fileID,'34' auditorID) t
WHERE NOT EXISTS(SELECT tm.fileID FROM [Files-Favorites] tm
WHERE tm.fileID = t.fileID AND tm.auditorID = t.auditorID)

Insert into 2 tables from a single select query using TSQL

I am trying to insert into 3 tables from one single select statement. Here is what I am trying to do:
insert into dbo.temp1 (name, location, city)
select name, location, city from mytable.
I want to be able to insert into 3 tables once I run the select statement like inserting into temp1, temp2 and temp3.
How can I do this? Thanks.
You can do it maximum for 2 tables with using output:
insert into dbo.temp1 (name, location, city)
output inserted.name, inserted.location, inserted.city into temp2
select name, location, city from mytable
You can't do this in one step*
What you can do is to insert the initial query into a #temp table (or a #table variable) as a staging area, and then insert into the tables from there. Wrap the steps in a transaction to retain ACID:
BEGIN TRAN
select name, location, city
into #TEMP
from mytable;
insert into temp1(name, location, city)
select name, location, city
from #TEMP;
-- Same for temp2 and temp3.
COMMIT TRAN
* Excluding hacks such as a view with an Instead-of Trigger.
The staging table is important from a concurrency point of view, as repeating the original query 3 times may result in different results if there are interim concurrent changes to the source table.
You can.
With a trick.
Create a view, then create an 'instead of' trigger for insert on that view where you insert the stuff into your tables. If you now insert into your view, you finally insert data in 3 tables. Here's a demo
-- 1. create 3 test tables
create table t1( id int, f1 varchar(20))
create table t2( id int, f2 varchar(20))
create table t3( id int, f3 varchar(20))
go
-- 2. create the view
create view Tt as
select t1.ID, t1.f1, t2.f2,t3.f3
from t1
join t2 on t1.ID=t2.ID
join t3 on t1.ID=t3.id
go
-- 3. create the trigger
create trigger Tr_Test on Tt INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON;
insert into t1 select id,f1 from inserted
insert into t2 select id,f2 from inserted
insert into t3 select id,f3 from inserted
END
GO
-- 4. now do your insert with a single select
insert into tt
select 1,'A','B','C'
-- 5. and watch the 3 tables
select * from t1
select * from t2
select * from t3
voilá, one insert, 3 tables got modified. Wwe don't count the hidden trigger, do we ;-)
There is no way to insert into X tables with one query (Ok it its with insert and output to table).
So you have to write 3 queries.
Or you can generate SQL statments with dynamic queries.
I don't believe you can insert into multiple tables in one statement. You can definitely do it in one transaction, however.
BEGIN TRANSACTION
INSERT INTO dbo.temp1 (name, location, city)
SELECT name, location, city
FROM myTable
INSERT INTO dbo.temp2 (name, location, city)
SELECT name, location, city
FROM myTable2
COMMIT TRANSACTION
You can insert into multiple tables with one select statement using a TRIGGER.
CREATE TRIGGER TEMP2_TEMP3_INSERT ON TEMP1
AFTER INSERT AS
BEGIN
/* create your insert statements for TEMP2 and TEMP3 here
referencing the data from the first insert */
END;
GO
MySQL doesn't support multi-table insertion in a single INSERT statement. Oracle is the only one I'm aware of that does, oddly...
However, you CAN use a transaction and have both of them be contained within one transaction.
MySQL:
START TRANSACTION;
INSERT INTO table1 VALUES ('1','2','3');
INSERT INTO table2 VALUES ('1','2','3');
COMMIT;
SQL Server:
BEGIN TRAN;
INSERT INTO table1 VALUES ('1','2','3');
INSERT INTO table2 VALUES ('1','2','3');
COMMIT;
SQL Server with error catching/rollback:
BEGIN TRANSACTION [Tran1]
BEGIN TRY
INSERT INTO table1 VALUES ('1','2','3')
INSERT INTO table2 VALUES ('1','2','3')
COMMIT TRANSACTION [Tran1]
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION [Tran1]
END CATCH
GO

Get the wrong line ID in ms sql

I have an INSERT statment wich inserts large amount of data into tableA from tableB.
Here is a very simple code example:
INSERT [dbo].[tableA]
SELECT field1 [field_1]
FROM [dbo].[tableB]
WHERE [codeID] IN (SELECT [codeID] FROM #tempTable WHERE RecordMarker = 1)
There is a temporary table wich holds codeIDs (at least 1 or more) needed to insert to tableA.
But there would be incorrent data in tableB what cannot be inserted into tableA. For example an numberic(30,2) field cannot map to numeric(13,2). In this case I get an excetpion and the statement has been terminated.
How can I get the CodeID or the wrong line number in tableB if I get an error? Now I have just the error message but no line number.
For example:
Msg 8115, Level 16, State 8, Line 1
Arithmetic overflow error converting numeric to data type numeric.
The statement has been terminated.
EDIT: There are more than one field in the table with different field types. So the numeric type is just an example.
Please try the following:
INSERT [dbo].[tableA]
SELECT field1 [field_1]
FROM [dbo].[tableB]
WHERE [codeID] IN (SELECT [codeID] FROM #tempTable WHERE RecordMarker = 1)
AND [codeID] <= 9999999999999.99;
INSERT ErrorLog
SELECT *
FROM [dbo].[tableB]
WHERE [codeID] > 9999999999999.99;
If you know the type of the destination field you're having the issue with, in this case a numeric of (13,2) precision, you can run a SELECT with a TRY_CONVERT on the potential problem field against your temp table and filter for NULL results. You could add a WHERE clause to your insert statement if you wanted to ensure that it would run successfully and not try to insert those "bad" rows.
CREATE TABLE #t (x NUMERIC(30,2),field2 varchar(10))
INSERT INTO #t
SELECT 123456789.23,'x'
UNION
SELECT 12345678901212343.23,'y'
UNION
SELECT 12345678923523523235.23,'z'
UNION
SELECT 42.0, 'a'
SELECT *, TRY_CONVERT(NUMERIC(13,2),x,1) [Converted to numeric(13,2)] FROM #t
Reference: http://msdn.microsoft.com/en-us/library/hh230993.aspx