Update elements because foreign key column has changed - sql

I have the following (simplified) structure of a device and log messages which are attached to a device:
CREATE TABLE device (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
serial_number VARCHAR(20) UNIQUE
);
CREATE TABLE error_log (
id SERIAL PRIMARY KEY,
device_id INT NOT NULL REFERENCES device(id),
);
I know want to change the schema to use the serial_number column of the device as the primary key, and drop the automatic key id. As part of the process I need to add a new column device_serial_number to the error_log table. My question is how to assign the values to this column using only SQL.
If a had an ORM mapper in action my pseudo code would look like this:
for log in error_log.all_objects():
log.device_serial_number = log.device.serial_number
Can I do this in pure SQL? (Postgresql if that matters)

You need to update the rows in the error_log table where the device.id = error.log match to device.serial_number.
So, first add a column to error_log. And then execute an UPDATE statement as following:
update error_log
set serial_number = d.serial_number
from device d
where error_log.id = d.id;
Afterwards you should add a UNIQUE constraint to the error_log.serial_number and then you can remove the error_log.device_id column if you don't need it anymore.

This is doable, just not in a single step.
add the new column to the error_log table. This must be nullable for now!
alter table error_log
add device_serial_number varchar(20);
Populate the new column with the correct values
update error_log
set device_serial_number = d.serial_number
from device d
where error_log.device_id = d.id;
Now the new column can be set to NOT NULL and we can remove the existing foreign key.
alter table error_log
alter column device_serial_number set not null,
drop constraint error_log_device_id_fkey;
Now drop the ID column, drop the old unique constraint and define a new primary key. Dropping the old unique constraint will remove the underlying unique index which is no longer needed as the primary key will create a new one.
alter table device
drop column id,
drop constraint device_serial_number_key,
add primary key (serial_number);
-- re-create the foreign key to the new primary key
alter table error_log
drop column device_id,
add foreign key (device_serial_number)
references device(serial_number);
Note that the drop constraint error_log_device_id_fkey and drop constraint device_serial_number_key assume the default naming convention that Postgres applies for "unnamed" constraints. You need to check if that really is the name of your foreign key.

Related

SQL Server Change Primary Key Data Type

I am working on SQL Server 2012:
I have a table with a primary key column as INT. I need to change this to a GUID.
Do I alter the table and remove int column as primary key?
Add the GUID column and set it as Primary and drop the old INT column?
Thank you.
You can't change primary key column,unless you drop it..Any operations to change its data type will lead to below error..
The object 'XXXX' is dependent on column 'XXXX'.
Only option is to
1.Drop primary key
2.change data type
3.recreate primary key
ALTER TABLE t1
DROP CONSTRAINT PK__t1__3213E83F88CF144D;
GO
alter table t1
alter column id varchar(10) not null
alter table t1 add primary key (id)
From 2012,there is a clause called (DROP_EXISTING = ON) which makes things simple ,by dropping the clustered index at final stage and also keeping old index available for all operations..But in your case,this clause won't work..
So i recommend
1.create new table with desired schema and indexes,with different name
2.insert data from old table to new table
3.finally at the time of switch ,insert data that got accumulated
4.Rename the table to old table name
This way you might have less downtime
You can change the date type of the primary key in three steps
Step 1 :- Drop the constraint associated with the Primary key
ALTER TABLE table_name
DROP CONSTRAINT constraint_name;
Step 2 :- Alter the Primay key column to a valid primary key data type
ALTER TABLE table_name
ALTER COLUMN pk_column_name target_data_type(size) not null;
Step 3 :- Make the altered column primary key again
ALTER TABLE table_name
ADD PRIMARY KEY (pk_column_name);
PS :-
You can get the Constraint name from the error message when you try to alter the
pk_column
If you already have data in the pk_column make sure the source and target data type of the column both can be used for the existing data. else another two steps would be needed to move the existing data to a temporary column and then perform the steps and bring back that data after vetting and dropping that temporary column.
Below is a script I wrote to help us deploy a change to primary key column data type.
This script assumes there aren't any non-primary key constraints (e.g. foreign keys) depending on this column.
It has a few safety checks as this was designed to be deployed to different servers (dev, uat, live) without creating side effects if the table was somehow different on a server.
I hope this helps someone. Please let me know if you find anything wrong before down-voting. I'm more than happy to update the script.
IF EXISTS(SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS C WITH (NOLOCK) WHERE C.TABLE_CATALOG = '<<DB>>' AND C.TABLE_SCHEMA = 'dbo' AND C.TABLE_NAME = '<<Table>>'
AND C.COLUMN_NAME = '<<COLUMN>>' AND C.DATA_TYPE = 'int') -- <- Additional test to check the current datatype so this won't make unnecessary or wrong updates
BEGIN
DECLARE #pkName VARCHAR(200);
SELECT #pkName = pkRef.CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS pkRef WITH (NOLOCK)
WHERE pkRef.TABLE_CATALOG = '<<DB>>' AND pkRef.TABLE_SCHEMA = 'dbo' AND TABLE_NAME = '<<Table>>'
IF(#pkName IS NOT NULL)
BEGIN
-- Make sure the primary key name is the one you are going to use in script beyond this point.
IF(#pkName != '<<PRIMARY KEY NAME>>')
BEGIN
RAISERROR ('Unexpected primary key name - The primary key found has a different name than expected. Please update the script.', 16, 1);
RETURN;
END
ALTER TABLE dbo.<<Table>>
DROP CONSTRAINT <<PRIMARY KEY NAME>>; -- Note: this is not a string or a variable (just type the PK name)
SELECT 'Dropped existing primary key';
END
ALTER TABLE dbo.<<Table>> ALTER COLUMN ID BIGINT
SELECT 'Updated column type to big int';
ALTER TABLE dbo.<<Table>>
ADD CONSTRAINT <<PRIMARY KEY NAME>> PRIMARY KEY CLUSTERED (<<COLUMN>>);
SELECT 'Created the primary key';
END
ELSE
BEGIN
SELECT 'No change required.';
END
In case other tables reference your PK with indexed FK's, these are the steps you must follow.
In this example, the main table's called Main, the single referencing table Reference. I'm changing the datatype to NVARCHAR(7). To use it:
Find/replace all these table names with your own;
Modify the data type;
You might also need to separately find/replace the dbo schema;
I'm using syntax which includes constraint names - if you want, also update these to your preferred naming conventions.
ALTER TABLE dbo.Main ADD IdNew NVARCHAR(7);
UPDATE dbo.Main SET IdNew = Id;
-- For all tables with FK's to this Main:
ALTER TABLE dbo.Reference ADD MainIdNew NVARCHAR(7);
UPDATE dbo.Reference SET MainIdNew = MainId;
ALTER TABLE dbo.Reference DROP CONSTRAINT FK_Reference_MainId_Main_Id;
DROP INDEX IX_Reference_MainId ON dbo.Reference;
ALTER TABLE dbo.Reference DROP COLUMN MainId;
-- Until here
ALTER TABLE dbo.Main DROP CONSTRAINT PK_Main;
ALTER TABLE dbo.Main DROP COLUMN Id;
EXEC sp_rename 'dbo.Main.IdNew', 'Id', 'COLUMN';
ALTER TABLE dbo.Main ALTER COLUMN Id NVARCHAR(7) NOT NULL;
ALTER TABLE dbo.Main ADD CONSTRAINT PK_Main PRIMARY KEY (Id);
-- Again for all tables with FK's to this Main:
EXEC sp_rename 'dbo.Reference.MainIdNew', 'MainId', 'COLUMN';
ALTER TABLE dbo.Reference ADD CONSTRAINT FK_Reference_MainId_Main_Id FOREIGN KEY (MainId) REFERENCES dbo.Main(Id);
CREATE INDEX IX_Reference_MainId ON dbo.Reference(MainId);
Right in the table you want to change the PK type >> Modify. Go in the column, change the type and save. If you want to see the code for such a change, before saving, you can right-click >> "Generate Change Script ..".
Using Microsoft SQL Server Management Studio do the following:
Open table Design
Change the primary key column type or any other change which is also possible with this way
Right click on the design area and select Generate Change Script
Accept Validation Warning
Preview changes or save them in file.
Profit :)
This works for any change to the table, just bare in mind that SSMS creates a temporary second table to do the difficult changes like primary column type change.
This works for me in version 18.9 of the app.

Why can't I add this foreign key?

I'll post only the main part. I have two tables, each one has to have the PK of the other as a FK.
CREATE TABLE apartment
(
cod_apartment INT NOT NULL PRIMARY KEY,
cod_offer INT NOT NULL
);
CREATE TABLE offer
(
cod_offer INT NOT NULL PRIMARY KEY,
cod_apartment INT NOT NULL
);
First I inserted the values on both tables and it was working, I could even search using "select * from...". But then I tried to add the foreign key:
This worked.
ALTER TABLE offer
ADD FOREIGN KEY (cod_apartment ) REFERENCES apartment;
And this not.
ALTER TABLE apartment
ADD FOREIGN KEY (cod_offer) REFERENCES offer;
This is the error message:
The ALTER TABLE statement conflicted with the FOREIGN KEY constraint "FK__apartment__cod_offer__6383C8BA". The conflict occurred in database "kleber_apartment", table "dbo.offer", column 'cod_offer'.
The problem is, every time I try to execute, the FK name changes. And this FK actually doesn't exist. I already dropped both tables and tried to insert the values again, but the same happens.
What could be?
That means you're trying to add a foreign key when existing data doesn't obey that constraint. So you have a record in your apartment table where the cod_offer column does not match any value in the cod_apartment table.
Adding a foreign key not only constrains future data, but it requires that any existing data must also follow the rule.
And regarding the 6383C8BA, whenever you add a constraint without giving it a name, SQL Server picks one for you. Personally, I'd recommend something like:
alter table dbo.apartment
add constraint FK_apartment__cod_offer
foreign key (cod_offer) references dbo.offer (cod_offer);
This lets you define names the way you want, and is a little more clear about what you're actually building.

The ALTER TABLE statement conflicted with the FOREIGN KEY constraint

Why does add a foreign key to the tblDomare table result in this error?
The ALTER TABLE statement conflicted with the FOREIGN KEY constraint "FK__tblDomare__PersN__5F7E2DAC". The conflict occurred in database "almu0004", table "dbo.tblBana", column 'BanNR'.
Code
CREATE TABLE tblDomare
(PersNR VARCHAR (15) NOT NULL,
fNamn VARCHAR (15) NOT NULL,
eNamn VARCHAR (20) NOT NULL,
Erfarenhet VARCHAR (5),
PRIMARY KEY (PersNR));
INSERT INTO tblDomare (PersNR,fNamn,eNamn,Erfarenhet)
Values (6811034679,'Bengt','Carlberg',10);
INSERT INTO tblDomare (PersNR,fNamn,eNamn,Erfarenhet)
Values (7606091347,'Josefin','Backman',4);
INSERT INTO tblDomare (PersNR,fNamn,eNamn,Erfarenhet)
Values (8508284163,'Johanna','Backman',1);
CREATE TABLE tblBana
(BanNR VARCHAR (15) NOT NULL,
PRIMARY KEY (BanNR));
INSERT INTO tblBana (BanNR)
Values (1);
INSERT INTO tblBana (BanNR)
Values (2);
INSERT INTO tblBana (BanNR)
Values (3);
ALTER TABLE tblDomare
ADD FOREIGN KEY (PersNR)
REFERENCES tblBana(BanNR);
It occurred because you tried to create a foreign key from tblDomare.PersNR to tblBana.BanNR but/and the values in tblDomare.PersNR didn't match with any of the values in tblBana.BanNR. You cannot create a relation which violates referential integrity.
This query was very useful for me. It shows all values that don't have any matches
select FK_column from FK_table
WHERE FK_column NOT IN
(SELECT PK_column from PK_table)
Try this solution:
There is a data item in your table whose associated value doesn't exist in the table you want to use it as a primary key table.
Make your table empty or add the associated value to the second table.
It is possible to create the foreign key using ALTER TABLE tablename WITH NOCHECK ..., which will allow data that violates the foreign key.
"ALTER TABLE tablename WITH NOCHECK ..." option to add the FK -- This solution worked for me.
Remove all existing data from your tables and then make a relation between the tables.
Before You add Foreign key to the table, do the following
Make sure the table must empty or The column data should match.
Make sure it is not null.
If the table contains do not go to design and change, do it manually.
alter table Table 1 add foreign key (Column Name) references Table 2 (Column Name)
alter table Table 1 alter column Column Name attribute not null
I guess, a column value in a foreign key table should match with the column value of the primary key table. If we are trying to create a foreign key constraint between two tables where the value inside one column(going to be the foreign key) is different from the column value of the primary key table then it will throw the message.
So it is always recommended to insert only those values in the Foreign key column which are present in the Primary key table column.
For ex. If the Primary table column has values 1, 2, 3 and in Foreign key column the values inserted are different, then the query would not be executed as it expects the values to be between 1 & 3.
In very simple words your table already has data present in it and the table you are trying to create relationship with does have that Primary key set for the values that are already present.
Either delete all the values of the existing table.
Add all the values of foreign key reference in the new table.
Try DELETE the current datas from tblDomare.PersNR . Because the values in tblDomare.PersNR didn't match with any of the values in tblBana.BanNR.
When you define a Foreign Key in table B referencing the Primary Key of table A it means that when a value is in B, it must be in A. This is to prevent unconsistent modifications to the tables.
In your example, your tables contain:
tblDomare with PRIMARY KEY (PersNR):
PersNR |fNamn |eNamn |Erfarenhet
-----------|----------|-----------|----------
6811034679 |'Bengt' |'Carlberg' |10
7606091347 |'Josefin' |'Backman' |4
8508284163 |'Johanna' |'Backman' |1
---------------------------------------------
tblBana:
BanNR
-----
1
2
3
-----
This statement:
ALTER TABLE tblDomare
ADD FOREIGN KEY (PersNR)
REFERENCES tblBana(BanNR);
says that any line in tblDomare with key PersNR must have a correspondence in table tblBana on key BanNR. Your error is because you have lines inserted in tblDomare with no correspondence in tblBana.
2 solutions to fix your issue:
either add lines in tblBana with BanNR in (6811034679, 7606091347, 8508284163)
or remove all lines in tblDomare that have no correspondence in tblBana (but your table would be empty)
General advice: you should have the Foreign Key constraint before populating the tables. Foreign keys are here to prevent the user of the table from filling the tables with inconsistencies.
i had this error too
as Smutje reffered make sure that you have not a value in foreign key column of your base foreign key table that is not in your reference table i.e(every value in your base foreign key table(value of a column that is foreign key) must also be in your reference table column)
its good to empty your base foreign key table first then set foreign keys
the data you have entered a table(tbldomare) aren't match a data you have assigned primary key table. write between tbldomare and add this word (with nocheck) then execute your code.
for example you entered a table tbldomar this data
INSERT INTO tblDomare (PersNR,fNamn,eNamn,Erfarenhet)
Values (6811034679,'Bengt','Carlberg',10);
and you assigned a foreign key table to accept only 1,2,3.
you have two solutions one is delete the data you have entered a table then execute the code. another is write this word (with nocheck) put it between your table name and add
like this
ALTER TABLE tblDomare with nocheck
ADD FOREIGN KEY (PersNR)
REFERENCES tblBana(BanNR);
Smutje is correct and Chad HedgeCock offered a great layman's example.
Id like to build on Chad's example by offering a way to find/delete those records.
We will use Customer as the Parent and Order as the child. CustomerId is the common field.
select * from Order Child
left join Customer Parent on Child.CustomerId = Parent.CustomerId
where Parent.CustomerId is null
if you are reading this thread... you will get results. These are orphaned children. select * from Order Child
left join Customer Parent on Child.CustomerId = Parent.CustomerId
where Parent.CustomerId is null Note the row count in the bottom right.
Go verify w/ whomever you need to that you are going to delete these rows!
begin tran
delete Order
from Order Child
left join Customer Parent on Child.CustomerId = Parent.CustomerId
where Parent.CustomerId is null
Run the first bit.
Check that row count = what you expected
commit the tran
commit tran
Be careful. Someone's sloppy programming got you into this mess. Make sure you understand the why before you delete the orphans. Maybe the parent needs to be restored.
From our end, this is the scenario:
We have an existing table in the database with records.
Then I introduces a NOT nullable foreign key
After executing the update i got this error.
How did i solve you ask?
SOLUTION: I just removed all the records of the table, then tried to update the database and it was successful.
This happens to me, since I am designing my database, I notice that I change my seed on my main table, now the relational table has no foreign key on the main table.
So I need to truncate both tables, and it now works!
You should see if your tables has any data on the rows. If "yes" then you should truncate the table(s) or else you can make them to have the same number of data at tblDomare.PersNR to tblBana.BanNR and vise-verse.
In my scenario, using EF, upon trying to create this new Foreign Key on existing data, I was wrongly trying to populate the data (make the links) AFTER creating the foreign key.
The fix is to populate your data before creating the foreign key since it checks all of them to see if the links are indeed valid. So it couldn't possibly work if you haven't populated it yet.
I encounter some issue in my project.
In child table, there isn't any record Id equals 1 and 11
I inserted DEAL_ITEM_THIRD_PARTY_PO table which Id equals 1 and 11 then I can create FK
Please first delete data from that table and then run the migration again. You will get success
I had the same problem.
My issue was having nullable: true in column (migration file):
AddColumn("dbo.table", "column", c => c.Int(nullable: true));
Possible Solutions:
Change nullable 'false' to 'true'. (Not Recommended)
Change property type from int to int? (Recommended)
And if required, change this later after adding column > then missing field data in previous records
If you've changed an existing property from nullable to non-nullable:
3) Fill the column data in database records
A foreign key constraint in a child table must have a parent table with a primary key. The primary key must be unique. The foreign key value must match a value in the patent table primary key
When you alter table column from nullable to not nullable column where this column is foreign key, you must :
Firstly, initialize this column with value (because it is foreign
key not nullable).
After that you can alter your table column normally.
Please try below query:
CREATE TABLE tblBana
(BanNR VARCHAR (15) NOT NULL PRIMARY KEY,
);
CREATE TABLE tblDomare
(PersNR VARCHAR (15) NOT NULL PRIMARY KEY,
fNamn VARCHAR (15) NOT NULL,
eNamn VARCHAR (20) NOT NULL,
Erfarenhet VARCHAR (5),
FK_tblBana_Id VARCHAR (15) references tblBana (BanNR)
);
INSERT INTO tblBana (BanNR)
Values (3);
INSERT INTO tblDomare (PersNR,fNamn,eNamn,Erfarenhet,FK_tblBana_Id)
Values (8508284173,'Johanna','Backman',1,3);
or you can use this
SELECT fk_id FROM dbo.tableA
Except
SELECT fk_id From dbo.tableB
and just FYI, in case you do all of your data reference checks and find no bad data...apparently it is not possible to create a foreign key constraint between two tables and fields where those fields are the primary key in both tables! Do not ask me how I know this.

Adding column with primary key in existing table

I am trying to add primary key to newly added column in existing table name Product_Details.
New Column added: Product_Detail_ID (int and not null)
I am trying add primary key to Product_Detail_ID (please note: there are no other primary or foreign key assigned to this table)
I am trying with this query but getting error.
ALTER TABLE Product_Details
ADD CONSTRAINT pk_Product_Detils_Product_Detail_ID PRIMARY KEY(Product_Detail_ID)
GO
Error:
The CREATE UNIQUE INDEX statement terminated because a duplicate key was found for the object name 'dbo.Product\_Details' and the index name 'pk\_Product\_Detils'. The duplicate key value is (0).
Am I missing something here? I am using SQL Server 2008 R2. I would appreciate any help.
If you want SQL Server to automatically provide values for the new column, make it an identity.
ALTER TABLE Product_Details DROP COLUMN Product_Detail_ID
GO
ALTER TABLE Product_Details ADD Product_Detail_ID int identity(1,1) not null
GO
ALTER TABLE Product_Details
add CONSTRAINT pk_Product_Detils_Product_Detail_ID primary key(Product_Detail_ID)
GO
In mysql, I was able to achieve with following query
ALTER TABLE table_name ADD new_column int NOT NULL AUTO_INCREMENT primary key
Add Primary Key to First Position
ALTER TABLE table_name
ADD column_name INT PRIMARY KEY AUTO_INCREMENT FIRST;
Reference: Stack Overflow | Tech On The Net
You are getting the error because you have existing data that does not fullfill the constraint.
There are 2 ways to fix it:
clean up the existing data before adding the constraint
add the constraint with the "WITH NOCHECK" option, this will stop sql server checking existing data, only new data will be checked
ALTER TABLE Jaya
ADD CONSTRAINT no primary key(No);
here Jaya is table name,
no is column name,
ADD CONSTRAINT is we giving the primary key keyword
If you want to add a new column say deptId to the existing table say department then you can do it using the below code.
ALTER TABLE department ADD COLUMN deptID INT;
it will create your new column named deptID.
now if you want to add constraint also along with new column while creating it then you can do it using the below code. In the below code I am adding primary key as a constraint. you can add another constraint also instead of primary key like foreign key, default etc.
ALTER TABLE department ADD COLUMN deptID INT NOT NULL ADD CONSTRAINT PRIMARY KEY(deptID);
k. friend
command:
sql> alter table tablename add primary key(col_name);
ex: alter table pk_Product_Detils add primary key(Product_Detail_ID);

Alter table & Add UNIQUE key results in an error

I have a table called Animal. AnimalId is the primary key & I wanted to set the column AnimalType_id as UNIQUE (I have an AnimalType table and need to set a foreign key here)
ALTER TABLE Animal
ADD UNIQUE Animal.AnimalType_id int
There already is data in both tables, because of that I can't drop the table.
This however results in an error:
Msg 102, Level 15, State 1, Line 2
Incorrect syntax near '.'.
See the documentation for how to add a table constraint.
ALTER TABLE Animal ADD CONSTRAINT UQ_Animal_AnimalTypeId UNIQUE (AnimalType_id)
It sounds like AnimalType_id is a foreign key so I just wanted to check you understood that by making this column unique, you're making the relationship one-one - you'll only be able to have one animal of each type.
Since you're getting an error adding the unique constraint, I'm going to suggest that you actually want a foreign key instead of a unique constraint:
ALTER TABLE Animal
ADD CONSTRAINT FK_Animal_AnimalType
FOREIGN KEY
(
AnimalType_id
)
REFERENCES AnimalType
(
id
)
I've had to guess at the name of the AnimalType table name and it's primary key column name - please change these if they are incorrect.
If you get into the habit of giving names to all objects (even constraints) that you create, you will have easier time later when you need to disable, drop, or alter the constraint:
ALTER TABLE Animal ADD CONSTRAINT UQ_Animal_Type UNIQUE (AnimalType_id)
It is also possible to get a more flexible constraint-like effect from creating a unique index.
I think you are trying to do this:
ALTER TABLE Animal
ADD COLUMN AnimalType_id int;
It seems the data in the column is not unique.
when you create a unique constraint on a column, you can not have any duplicate entries (you can have at most one null)
try this
ALTER TABLE table_name ADD CONSTRAINT UNIQUE (column_name)