Creating an insert query (error on foreign key constraint) - sql

I want to move data from one database's table to another database's table. I am getting a foreign key error. How can I insert all the data which is valid except those rows who don't have a foreign key?
My query is :
SET IDENTITY_INSERT City ON
INSERT INTO City ([cityid],[city],[country],[state],[cityinfo]
,[enabled],[countryid],[citycode],[stateid],[latitude],[longitude])
SELECT [cityid],[city],[country],[state],[cityinfo]
,[enabled],[countryid],[citycode],[stateid],[latitude],[longitude]
FROM TD.DBo.City
getting this error:
The INSERT statement conflicted with the FOREIGN KEY constraint "FK__city__countryid__3E52440B". The conflict occurred in database "schoolHigher", table "dbo.country", column 'countryId'.

INNER JOIN the other database's table with the country table. Only those records with an existing country will get selected.
Note: you should check that the corresponding countryid's in both databases match.
SET IDENTITY_INSERT City ON
INSERT INTO City (
[cityid]
,[city]
,[country]
,[state]
,[cityinfo]
,[enabled]
,[countryid]
,[citycode]
,[stateid]
,[latitude]
,[longitude])
SELECT ct.[cityid]
,ct.[city]
,ct.[country]
,ct.[state]
,ct.[cityinfo]
,ct.[enabled]
,ct.[countryid]
,ct.[citycode]
,ct.[stateid]
,ct.[latitude]
,ct.[longitude]
FROM TD.DBo.City ct
INNER JOIN dbo.Country cnt ON cnt.CountryID = ct.CountryID

Related

How to check for clustered unique key while inserting in SQL table

I am trying to insert rows from another database table to new database table getting the below error if there is no where condition in the query.
Violation of UNIQUE KEY constraint 'NK_LkupxViolations'. Cannot insert duplicate key in object 'dbo.LkupxViolation'. The duplicate key value is (00000000-0000-0000-0000-000000000000, (Not Specified)).
Then I wrote the below query adding where conditions it worked but it didn't insert the expected no. of rows.
IF EXISTS(SELECT 1 FROM sys.tables WHERE name = 'LkupxViolation')
BEGIN
INSERT INTO dbo.[LkupxViolation] SELECT * FROM [DMO_DB].[dbo].[LkupxViolation] where CGRootId not in (select CGRootId from dbo.[LkupxViolation])
and Name not in (select name from dbo.[LkupxViolation])
END
ELSE
PRINT 'LkupxViolation table does not exist'
The unique key in the table is created as:
CONSTRAINT [NK_LkupxViolations] UNIQUE CLUSTERED
(
[CGRootId] ASC,
[Name] ASC
)
Try using NOT EXISTS:
INSERT INTO dbo.[LkupxViolation]
SELECT *
FROM [DMO_DB].[dbo].[LkupxViolation] remove_l
WHERE NOT EXISTS (SELECT 1
FROM dbo.[LkupxViolation] local_l
WHERE local_l.Name = remote_l.Name AND
local_l.CGRootId = remote_l.CGRootId
);
This checks for both values in the same row. In addition, NOT IN is not NULL-safe. If any values generated by the subquery are NULL then all rows are filtered out.

How can I insert a row that references another postgres table via foreign key, and creates the foreign row too if it doesn't exist?

In Postgres, is there a way to atomically insert a row into a table, where one column references another table, and we look up to see if the desired row exists in the referenced table and inserts it as well if it is not?
For example, say we have a US states table and a cities table which references the states table:
CREATE TABLE states (
state_id serial primary key,
name text
);
CREATE TABLE cities (
city_id serial,
name text,
state_id int references states(state_id)
);
When I want to add the city of Austin, Texas, I want to be able to see whether Texas exists in the states table, and if so use its state_id in the new row I'm inserting in the cities table. If Texas doesn't exist in the states table, I want to create it and then use its id in the cities table.
I tried this query, but I got an error saying
ERROR: WITH clause containing a data-modifying statement must be at the top level
LINE 2: WITH inserted AS (
^
WITH state_id AS (
WITH inserted AS (
INSERT INTO states(name)
VALUES ('Texas')
ON CONFLICT DO NOTHING
RETURNING state_id),
already_there AS (
SELECT state_id FROM states
WHERE name='Texas')
SELECT * FROM inserted
UNION
SELECT * FROM already_there)
INSERT INTO cities(name, state_id)
VALUES
('Austin', (SELECT state_id FROM state_id));
Am I overlooking a simple solution?
Here is one option:
with inserted as (
insert into states(name) values ('Texas')
on conflict do nothing
returning state_id
)
insert into cities(name, state_id)
values (
'Dallas',
coalesce(
(select state_id from inserted),
(select state_id from states where name = 'Texas')
)
);
The idea is to attempt to insert in a CTE, and then, in the main insert, check if a value was inserted, else select it.
For this to work properly, you need a unique constraint on states(name):
create table states (
state_id serial primary key,
name text unique
);
Demo on DB Fiddlde
You can force the insert statement to return a value:
WITH inserted AS (
INSERT INTO states (name)
VALUES ('Texas')
ON CONFLICT (name) DO UPDATE SET name = EXCLUDED.NAME
RETURNING state_id
)
. . .
The DO UPDATE SET forces the INSERT to return something.
I notice that you don't have a unique constraint, so you also need that:
ALTER TABLE states ADD CONSTRAINT unq_state_name
UNIQUE (name);
Otherwise the ON CONFLICT doesn't have anything to work with.

The INSERT statement conflicted with the FOREIGN KEY constraint problem

I have the script below, which gives me an error:
"The INSERT statement conflicted with the FOREIGN KEY constraint "FK_dbo.PlanShiftAssignments_dbo.User_UserId". The conflict occurred in database "SWS", table "dbo.User", column 'Id'.
The statement has been terminated."
As you can see, in WHERE clause I check if UserId exists in dbo.User. What are the other possible reasons of the error?
UPDATED: I am also want to know what row from select statement causes the error. Any advices on debugging this query will be appreciated. I am using MS SQL Server Management Studio.
CREATE TABLE [dbo].[PlanShiftAssignments] (
[PlanShiftId] [uniqueidentifier] NOT NULL,
[Status] [int] NOT NULL,
[UserId] [int],
CONSTRAINT [PK_dbo.PlanShiftAssignments] PRIMARY KEY ([PlanShiftId])
)
CREATE INDEX [IX_PlanShiftId] ON [dbo].[PlanShiftAssignments]([PlanShiftId])
CREATE INDEX [IX_UserId] ON [dbo].[PlanShiftAssignments]([UserId])
ALTER TABLE [dbo].[PlanShiftAssignments] ADD CONSTRAINT [FK_dbo.PlanShiftAssignments_dbo.PlanShifts_PlanShiftId] FOREIGN KEY ([PlanShiftId]) REFERENCES [dbo].[PlanShifts] ([Id])
ALTER TABLE [dbo].[PlanShiftAssignments] ADD CONSTRAINT [FK_dbo.PlanShiftAssignments_dbo.User_UserId] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id])
insert into dbo.PlanShiftAssignments
select ps.Id as PlanShiftId, ISNULL(ps.AssigneeId, psi.UserId) as UserId, ISNULL(psi.[Status], 1) as [Status] from dbo.PlanShifts ps
left join
dbo.PlanShiftInvitations psi
on ps.Id = psi.PlanShiftId
where (psi.UserId is not null and psi.UserId IN (select Id from dbo.[User]))
or (ps.AssigneeId is not null and ps.AssigneeId IN (select Id from dbo.[User]))
Make sure that you always include the target's column list on each INSERT statement.
insert into dbo.PlanShiftAssignments (
PlanShiftId,
UserId,
Status)
SELECT
ps.Id as PlanShiftId,
ISNULL(ps.AssigneeId, psi.UserId) as UserId,
ISNULL(psi.[Status], 1) as [Status]
...
Your table is created with the order PlanShiftId, Status, UserId and the column order from your current SELECT is PlanShiftId, UserId, Status, hence the confusion.
You have a strange data model, if UserId and AssigneeId do not already refer to User in the underlying tables.
In any case, your where clause is
where (psi.UserId is not null and psi.UserId IN (select Id from dbo.[User])) or
(ps.AssigneeId is not null and ps.AssigneeId IN (select Id from dbo.[User]))
This leaves open the possibility that psi.UserId matches but ps.AssigneeId does not.
To ensure that the logic matches, use the same expression as in the select:
where coalesce(ps.AssigneeId, psi.UserId) in (select Id from dbo.[User])
Could it be for the fact you've specified an OR in your WHERE clause, therefore either the AssigneeId or UserId is in the User table but not the other and therefore invalidates the FK constraint.

Postgresql Deleting Records Matching Primary Key

We are getting data via a batch process from a third-party data provider. For the schema they provide, they issue delimited text files indicating records to delete, followed by a different text file of records to add.
My plan for removing the records is to load the records to delete into a temporary table, and then delete records through an inner join. For example:
-- main table
CREATE TABLE main_table (
city VARCHAR(40) NOT NULL,
state CHAR(2) NOT NULL,
PRIMARY KEY (city, state));
-- removing records
CREATE TEMPORARY TABLE delete_table (
city VARCHAR(40) NOT NULL,
state CHAR(2) NOT NULL,
PRIMARY KEY (city, state));
INSERT INTO main_table (city, state) VALUES
('Chicago', 'IL'),
('Seattle', 'WA'),
('New York', 'NY'),
('Springfield', 'IL'),
('Springfield', 'MA');
INSERT INTO delete_table (city, state) VALUES
('Chicago', 'IL'),
('New York', 'NY'),
('Springfield', 'MA');
-- Delete statement
DELETE FROM main_table t
USING delete_table d
WHERE t.city=d.city AND t.state=d.state;
Is this the best way to typically delete records in this case? The record deletions can range from a few rows to a million depending on the table and day and one hundred tables. Additionally, any table with composite primary keys requires a customized where clause. Is there a way to use a natural join, or a USING (column list) predicate instead?

How can I insert a set of child records while updating the parent?

I'm using SQL Server 2005 and wish to create a number address records, updating the contact records with the new Id's:
Take the following tables
create table contact(id int primary key identity, home_address_id int, work_address_id int)
create table address(id int primary key identity, street varchar(25), number int)
And foreign keys:
ALTER TABLE dbo.contact ADD CONSTRAINT FK_contact_address1 FOREIGN KEY (home_address_id) REFERENCES dbo.address(id)
ALTER TABLE dbo.contact ADD CONSTRAINT FK_contact_address2 FOREIGN KEY (work_address_id) REFERENCES dbo.address(id)
some dummy data
insert into contact default values
insert into contact default values
insert into contact default values
How can I insert a default empty address record for all contacts who have no home address, and update the home_address_id in one go?
The first part is simple:
insert into address(street) select null from contact where home_address_id is null
I can even get the newly create address id's:
declare #addressTable table(id int)
insert into address(street)
OUTPUT INSERTED.Id INTO #addressTable
select null from contact where home_address_id is null
Here's the new id's
select * from #addressTable
But how to update the contact table with these new Id's?
If possible, I would suggest normalizing your database by adding a Contact_Addresses table:
CREATE TABLE Contact_Addresses
(
contact_id INT NOT NULL,
address_id INT NOT NULL,
address_type VARCHAR(10) NOT NULL,
CONSTRAINT PK_Contact_Addresses PRIMARY KEY CLUSTERED (contact_id, address_id, address_type),
CONSTRAINT FK_ContactAddresses_Contacts (contact_id) REFERENCES Contacts (id),
CONSTRAINT FK_ContactAddresses_Addresses (address_id) REFERENCES Addresses (id),
CONSTRAINT CK_ContactAddresses_address_type CHECK address_type IN ('HOME', 'WORK')
)
Next, I would suggest not putting "dummy" records in your database. It's going to end up causing headaches down the road. The database should contain an accurate record of the data in your system. If you want to display some value by default when no address exists in the system for a contact then handle that in your UI.
If you really must though, then the following code should do the trick:
;WITH C_CTE AS
(
SELECT
id,
home_address_id,
ROW_NUMBER() OVER(ORDER BY id) AS seq
FROM
Contacts
),
(
SELECT
id,
ROW_NUMBER() OVER(ORDER BY id) AS seq
FROM
Addresses
)
UPDATE
C_CTE
SET
home_address_id = A.id
FROM
C_CTE C
INNER JOIN A_CTE A ON A.seq = C.seq
I would do it from the moment you get a new contact, thusly:
[receive contact information]
//prior to inserting contact
declare #homeAddress int, #workAddress int
[insert home address here (real or default based on input)]
set #homeAddress = ##Identity
[insert work address here (real or default)]
set #workAddress = ##Identity
[insert contact here referencing #homeAddress & #workAddress]
For the stuff already in your table, you're going to have to associate all of your null value ids to a contact id. Or, you could clear out your null value addresses, and modify the above statement to an update somehow (brain's not working at the moment, so all I'm coming up with is a cursor, and cursors are evil).