Insert/Update, ignore rows which would violate FK - sql-server

If someone performs an update or insert on a table which has a foreign key to another, if a nonexistent value appears, an error is thrown. Is there an automated enough way to just ignore the faulty columns and continue with the others?
I can only think of an instead-of trigger, but it sounds messy.

You should check the rows in your INSERT query, by joining the source data with the referenced table.
For example, let's suppose that you have the following data in the SourceData table and you want to insert it in the Sales.CurrencyRate table in the AdventureWorks2019 database:
CREATE TABLE SourceData (
CurrencyRateDate datetime,
FromCurrencyCode char(3),
ToCurrencyCode char(3),
PRIMARY KEY (CurrencyRateDate, FromCurrencyCode, ToCurrencyCode),
Rate money NOT NULL
)
INSERT INTO SourceData (CurrencyRateDate, FromCurrencyCode, ToCurrencyCode, Rate) VALUES
('20200429','EUR','RON',4.8425),
('20200429','EUR','ROL',48425),
('20200430','EUR','RON',4.8421),
('20200430','EUR','ROL',48421)
INSERT INTO Sales.CurrencyRate (CurrencyRateDate, FromCurrencyCode, ToCurrencyCode, AverageRate, EndOfDayRate)
SELECT sd.CurrencyRateDate, sd.FromCurrencyCode, sd.ToCurrencyCode, sd.Rate, sd.Rate
FROM SourceData sd
If you simply run the INSERT statement mentioned above, you would get an error saying "The INSERT statement conflicted with the FOREIGN KEY constraint "FK_CurrencyRate_Currency_ToCurrencyCode". The conflict occurred in database "AdventureWorks2019", table "Sales.Currency", column 'CurrencyCode'.", because the currency "RON" is not present in the Sales.Currency table.
To avoid this error and insert only the data that has corresponding rows in the referenced table, you would simply use a JOIN for each FK, like this:
INSERT INTO Sales.CurrencyRate (CurrencyRateDate, FromCurrencyCode, ToCurrencyCode, AverageRate, EndOfDayRate)
SELECT sd.CurrencyRateDate, sd.FromCurrencyCode, sd.ToCurrencyCode, sd.Rate, sd.Rate
FROM SourceData sd
INNER JOIN Sales.Currency c1 ON c1.CurrencyCode=sd.FromCurrencyCode
INNER JOIN Sales.Currency c2 ON c2.CurrencyCode=sd.ToCurrencyCode

Related

SQL Server merge and pk violation

I'm trying to understand why in a scenario like the following
DECLARE #source TABLE
(
orderId NVARCHAR(50),
customerId NVARCHAR(50)
)
DECLARE #target TABLE
(
orderId NVARCHAR(50) PRIMARY KEY,
customerId NVARCHAR(50) NOT NULL
)
INSERT INTO #source
VALUES ('test', '123'), ('test', '234')
MERGE #target AS TRG
USING (SELECT DISTINCT orderId, customerId
FROM #source) AS SRC ON SRC.orderId = TRG.orderId
WHEN MATCHED THEN
UPDATE SET TRG.customerId = SRC.customerId
WHEN NOT MATCHED BY TARGET THEN
INSERT (orderId, customerId)
VALUES (orderId, customerId);
I'm getting a duplicate key violation error:
Msg 2627, Level 14, State 1, Line 21
Violation of PRIMARY KEY constraint 'PK__#B3D7759__0809335D4BE1521F'. Cannot insert duplicate key in object 'dbo.#target'. The duplicate key value is (test).
What I expect is that the update statement finds the existing key and updates the customerId so that at the end I have in #target 1 row with orderId = 'test' and customerId = '234'.
For what I can assume, it instead tries to insert all records as it first doesn't find any key match at the beginning of the merge, causing the violation as the source contains the key multiple times.
Is this right? Is there any way to achieve what I am expecting using the merge function?
#user1443098
I've read your link, thanks. However I have a massive data insertion coming from a source table and going into 10 different tables; I tried to implement the procedure with a cursor and it took like 0.5s per record (with all the if exists statements). With merge statement, 300 rows have been inserted in the 10 different tables in less than one sec. So in my case it does a lot of difference in performance terms.
There are two records in #source with the same OrderID. There is not a match for either record in #target so the NOT MATCHED clause is trying to insert both of these records. But it can not do this because the primary key on OrderID in the #target table requires that all inserted records have unique values for OrderID. The duplication of values into the primary key causes the primary key violation.
If you are expecting duplicates are possible in the source... you should eliminate them in your USING sub-query. Something like this:
(SELECT orderId, max(customerId) customerId
FROM #source
group by orderId)

How to create connection between two tables in SQL Server 2008?

I have database that has Student table with information about each student in the system. Each record in that table has unique identifier that was created with NEWID() in SQL 2008. Then I have three other tables that use the same ID to link the student and the record. I'm wondering if I need to set any kind of property that will link these tables/records. Here is example of my Student table:
st_id -> Auto increment id
st_studentGUID -> Primary key
st_firstName
st_lastName
st_dob
st_gender
st_uid
st_udt
st_utime
And here is example of my other three tables:
Table 1 Table 2 Table 3
tb1_id -> auto increment tb2_id -> auto increment tb3_id -> auto increment
tb1_studentGUID tb2_studentGUID tb3_studentGUID
I have tried to create foreign key for each Table 1,2 and 3 on studentGUID but I got an error:
The ALTER TABLE statement conflicted with the FOREIGN KEY constraint "FK_Table1_Student". The conflict occurred in database "testDB", table "Student", column 'st_studentGUID'.
After I have done some research seems like this error occurred because Table 1 doesn't have all st_studentGUID. This make sense because student record might exist in Table 3 but not in Table 1. I'm wondering if there is any other sort of relation that I can set between the tables? Should I maybe use the Indexes on studentGUID fields? This is the reason why I'm asking this, in one of my update queries I use auto incremented id in where clause and for some reason I received an error that looks like this:
ErrorCode 1205
Message [Macromedia][SQLServer JDBC Driver][SQLServer]Transaction (Process ID 111) was deadlocked on lock | communication buffer resources with another process and has been chosen as the deadlock victim. Rerun the transaction.
SQLState 40001
and error was pointing to the line that is end of the update query that looks like this:
<cfquery name="updateTable1" datasource="testDB">
UPDATE Table1
SET
tb1_testdt = <cfqueryparam value="#FORM.frm_testdt#" cfsqltype="cf_sql_date" maxlength="10" null="#yesNoFormat(!len(FORM.frmhs_testdtgr))#" />
FROM Table1 AS Table
LEFT OUTER JOIN studentLocked AS Locked
ON Locked.lk_studentGUID = Table.tb1_studentGUID
WHERE tb1_id = <cfqueryparam value="#FORM.frm_id#" cfsqltype="cf_sql_integer" />
AND lk_active = '1'
AND lk_staffID = <cfqueryparam value="#trim(appStaff)#" cfsqltype="cf_sql_char" maxlength="10" />
</cfquery>
Query above is checking if studentGUID exists in Lock table, reason why I'm doing this is because we do not want two different users updating the same record at the same time. Query updates single record and should be pretty fast. I'm confused whit the error message above deadlock victim. Is there something in my database design that is off or my update query is causing this error? I couldn't find anything on the web that could help me fix this issue. if anyone has experience with this kind of problem please let me know.
If all StudentGUIDs exist in the Student table, then the correct design is to have the StudentGUID in Table1, Table2 and Table3 be a Foreign Key to the Student table.
Now that you've sorted out your foreign key issue with the missing record from Student, try setting up your FKs on all of your tables, then re-try your update query.
If the deadlock issue persists and is reproducible, submit a new question for that, including the schema for the table you're updating and the values you're trying to pass.
Here's a script that demonstrates you can create the relationships you described provided all the keys are present in the students table:
CREATE TABLE students (st_id AS NEWID(),
st_studentGUID UNIQUEIDENTIFIER PRIMARY KEY)
INSERT INTO students (st_studentGUID) VALUES ('3B9F59DD-BF0A-4A09-BF4E-A396E2978B24')
INSERT INTO students (st_studentGUID) VALUES ('7CC5FF67-DAB8-426A-B9F7-E9F041718B6B')
INSERT INTO students (st_studentGUID) VALUES ('84B80D3E-44C4-4291-857D-B6CA6552369D')
CREATE TABLE t1 (tb1_id AS NEWID(), tb1_studentGUID UNIQUEIDENTIFIER)
ALTER TABLE t1
ADD CONSTRAINT FK_t1_tbl_studentGUID FOREIGN KEY (tb1_studentGUID) REFERENCES students(st_studentGUID)
INSERT INTO t1 (tb1_studentGUID) VALUES ('3B9F59DD-BF0A-4A09-BF4E-A396E2978B24')
CREATE TABLE t2 (tb2_id AS NEWID(), tb2_studentGUID UNIQUEIDENTIFIER)
ALTER TABLE t2
ADD CONSTRAINT FK_t2_tbl_studentGUID FOREIGN KEY (tb2_studentGUID) REFERENCES students(st_studentGUID)
INSERT INTO t2 (tb2_studentGUID) VALUES ('7CC5FF67-DAB8-426A-B9F7-E9F041718B6B')
CREATE TABLE t3 (tb3_id AS NEWID(), tb3_studentGUID UNIQUEIDENTIFIER)
ALTER TABLE t3
ADD CONSTRAINT FK_t3_tbl_studentGUID FOREIGN KEY (tb3_studentGUID) REFERENCES students(st_studentGUID)
INSERT INTO t3 (tb3_studentGUID) VALUES ('84B80D3E-44C4-4291-857D-B6CA6552369D')
Now create a 4th table, add a UUID that isn't in the students table, and try to create the foreign key constraint:
CREATE TABLE t4 (tb4_id AS NEWID(), tb4_studentGUID UNIQUEIDENTIFIER)
INSERT INTO t4 (tb4_studentGUID) VALUES ('897925F1-BE92-44EB-82C3-88E1C33C7792')
ALTER TABLE t4
ADD CONSTRAINT FK_t4_tbl_studentGUID FOREIGN KEY (tb4_studentGUID) REFERENCES students(st_studentGUID)
Which reproduces your error message:
The ALTER TABLE statement conflicted with the FOREIGN KEY constraint "FK_t4_tbl_studentGUID". The conflict occurred in database "IconERP", table "dbo.students", column 'st_studentGUID'.

SQL Server DB Trigger

I need to insert a row into a table if a particular row in another table is updated.
How do I do an IF statement in the the DB trigger on Table1, saying if Table1.column1 = 'TC' then INSERT a row in Table2.
You would do this in an update trigger on the 'other' table.
There are two special tables in triggers: inserted and deleted. You join these two tables in such a way that the result set is the rows you wish to insert. Ergo -
create trigger [after_update_on_Table1] on [Table1] for update
as
...
insert
into [Table2] (...)
select
...
from
inserted as i
inner join
deleted as d
on (i.<*pk*> = d.<*pk*>)
where
<*other conditions if applicable*>
...
<pk> is whatever the appropriate primary key would be. If this is a compound primary key then AND together the different primary key components.
For what you describe thus far you do not require an if statement.

Insert records from one table to another without violating any constraint in SQL Server database

There are 2 databases DB_Main and DB_Backup in SQL Server 2008.
I want to copy the data of Table1 from DB_Backup into Table1 of DB_Main.
The structure of all the tables in both the database is same. Both the tables in both the database have foreign key, primary key constraint.
When I try to copy data of Table1 from DB_Backup into Table1 of DB_Main with this query:
Insert into [DB_Main].[Table1]
Select *
from [DB_Backup].[Table1];
I get this foreign key error.
The INSERT statement conflicted with the FOREIGN KEY constraint
"FK_Table1_Table3". The conflict occurred in database "DB_Main",
table "Table3", column 'RequestID'.
Please let me know any simple way to copy all the records of Table1 from DB_Backup into Table1 of DB_Main, without violating any constraint?
Please reply
What this means is that for example you are trying to insert a record into Table1 which has for example RequestID = 75. The foreign key constraint means that there must be a record with RequestID = 75 in Table3.... and there currently isn't.
So this means you also need to load data into Table3
To find the records actually causing the issue run
Select DISTINCT RequestID from [DB_Backup].[Table1]
Some of these request ID's need a 'parent' record in Table3
To find the specific ones run
Select RequestID from [DB_Main].[Table3]
WHERE Request_ID NOT IN (
Select DISTINCT RequestID from [DB_Backup].[Table1]
)
You need to insert these into Table3:
insert into Table3(Request_ID, OtherColumn)
Select RequestID, OtherColumn from [DB_Backup].[Table3]
WHERE Request_ID NOT IN (
Select DISTINCT RequestID from [DB_Main].[Table3]
)
Then you can load your other records.
Drop or Disable constraint FK_Table1_Table3 before inserting and enable after it.
To Disable:
ALTER TABLE Table1 NOCHECK CONSTRAINT ALL
or
ALTER TABLE Table1 NOCHECK CONSTRAINT FK_Table1_Table3
Source
to Enable
ALTER TABLE Table1 CHECK CONSTRAINT ALL
or
ALTER TABLE Table1 CHECK CONSTRAINT FK_Table1_Table3

Ignore Duplicate Values on Index prevents FOREIGN KEY constraint

Due to some architectural reasons I have to ignore duplicate values on the index. It works perfectly well - except, when I am inserting wrong data. I am trying to insert value to the FK column that is supposed to throw:
The INSERT statement conflicted with the FOREIGN KEY constraint "FK__constrainName".
The conflict occurred in database, table "someTable", column 'FKColumn'.
Is there logic behind ignoring duplicate values that prevents insert statement from throwing this exception?
I assume you are talking about this kind of situation?
CREATE TABLE T2(T2_ID INT PRIMARY KEY)
INSERT INTO T2 VALUES (1),(2)
CREATE TABLE T1 (T1_ID INT, T2_ID INT REFERENCES T2)
CREATE UNIQUE CLUSTERED INDEX IX ON T1(T1_ID) WITH IGNORE_DUP_KEY
INSERT INTO T1 VALUES (1,2),(1,2),(2,3),(2,3)
/*FK Violation - No rows inserted*/
SELECT *
FROM T1
/*Duplicate key violation and potential FK Violation - one row inserted*/
INSERT INTO T1 VALUES (1,2),(1,2),(1,3),(1,3)
SELECT *
FROM T1
DROP TABLE T1
DROP TABLE T2
If a row is not inserted because it would cause a duplicate key violation then the FK constraint is not violated following the insert, hence no error.

Resources