Changing datatype of a column, which is referenced by other tables - sql

I want to change the datatype of a primary key column in Table A, which is referenced by Table B. The schema is something like-
Table A: (col1A number, col2A...)
Table B: (col1B number, col2B...)
col2B -> col1A
I want to change datatype of col1A from number to varchar. I want that to reflect in Table B also. Is there any simple way to do that?
--
Thanks.

No, there is no simple way to do this. Assuming that both tables have data in them, you'd need to
Add a new VARCHAR2 column to table A
Update A to set the new column equal to TO_CHAR( col1A )
Add a new 'VARCHAR2` column to table B
Update B to set the column equal to TO_CHAR( col2B )
Drop the existing foreign key constraint
Drop the existing primary key constraint
Drop col1A from A
Drop col2B from B
Rename the new columns (if desired) in A & B to col1A and col2B
Create the new primary key constraint
Create the new foreign key constraint
Obviously, that's going to be a rather expensive operation.

This is a proof of concept in T-SQL (SQL Server). Basically, we're changing the primary key's data type from INT to UNIQUEIDENTIFIER (GUID) in table A, which is referenced by a foreign key in table B.
-- ARRANGE
CREATE TABLE A(
[Id] [int] IDENTITY(1,1) NOT NULL,
CONSTRAINT PK_A PRIMARY KEY (Id)
)
CREATE TABLE B(
[Id] INT IDENTITY(1,1) NOT NULL,
[A_Id] INT NOT NULL,
CONSTRAINT PK_B PRIMARY KEY (Id),
CONSTRAINT FK_B_A FOREIGN KEY (A_Id) REFERENCES A(Id)
)
INSERT A DEFAULT VALUES
DECLARE #A_Id INT
SELECT #A_Id = SCOPE_IDENTITY()
INSERT INTO B VALUES (#A_Id)
INSERT INTO B VALUES (#A_Id)
INSERT INTO B VALUES (#A_Id)
INSERT INTO B VALUES (#A_Id)
INSERT INTO B VALUES (#A_Id)
-- ACT
ALTER TABLE A ADD New_Id UNIQUEIDENTIFIER NOT NULL DEFAULT NEWID()
ALTER TABLE B ADD New_A_Id UNIQUEIDENTIFIER NULL
UPDATE B SET B.New_A_Id = (SELECT A.New_Id FROM A WHERE A.Id = B.A_Id)
ALTER TABLE B DROP FK_B_A
ALTER TABLE B DROP COLUMN A_Id
EXEC sp_RENAME 'B.New_A_Id', 'A_Id', 'COLUMN'
ALTER TABLE A DROP PK_A
ALTER TABLE A DROP COLUMN Id
ALTER TABLE A ADD CONSTRAINT PK_A PRIMARY KEY (New_Id)
EXEC sp_RENAME 'A.New_Id', 'Id', 'COLUMN'
ALTER TABLE B ADD CONSTRAINT FK_B_A FOREIGN KEY (A_Id) REFERENCES A(Id)
-- ASSERT
SELECT * FROM A
SELECt * FROM B

Related

How to set a primary key?

SELECT TOP 1000 [LicensePlate]
,[Manufacturer]
,[Model]
,[Colour]
,[Year]
,[EngineSize]
,[Value]
FROM [Cars2].[dbo].[Cartable1]
Above is my layout for a cars table. I am completely new to SQL and was wondering how I would set 'Licence Plate' as the primary key?
First find any duplicates by that column.
SELECT
C.LicensePlate,
AmountDuplicates = COUNT(*)
FROM
Cars2.dbo.Cartable1 AS C
GROUP BY
C.LicensePlate
HAVING
COUNT(*) > 1
If any record shows up, you need to either delete all the duplicates or update their license plates so they don't repeat anymore.
You will also need to check for NULL values and update or delete them (primary key can't be null).
SELECT
C.*
FROM
Cars2.dbo.Cartable1 AS C
WHERE
C.LicensePlate IS NULL
Then you can add the PRIMARY KEY constraint with:
ALTER TABLE Cars2.dbo.Cartable1
ADD CONSTRAINT PK_Cartable1 -- Name of the constraint
PRIMARY KEY (LicensePlate)
You might get an error if LicensePlate can hold NULL values. You can change it with an ALTER TABLE:
ALTER TABLE Cars2.dbo.Cartable1 ALTER COLUMN LicensePlate VARCHAR(20) NOT NULL -- The proper data type
If you already have a primary key defined on that table you will have to drop it and then create your new one (one table can only have 1 primary key constraint at a time). You can check which one is it with the following query:
USE Cars2; -- The database name here
DECLARE #TableName VARCHAR(100) = 'Cartable1'
DECLARE #SchemaName VARCHAR(100) = 'dbo'
SELECT
ColumnName = Col.Column_Name,
ConstraintName = tab.CONSTRAINT_NAME
FROM
INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS Tab
INNER JOIN INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE AS Col ON
Col.Constraint_Name = Tab.Constraint_Name AND
Col.Table_Name = Tab.Table_Name
WHERE
Constraint_Type = 'PRIMARY KEY' AND
Col.Table_Name = #TableName AND
Col.TABLE_SCHEMA = #SchemaName
Once you see the result, you can drop the current primary key with another ALTER TABLE:
ALTER TABLE Cars2.dbo.Cartable1 DROP CONSTRAINT ConstraintNameFromThePreviousQuery
CREATE TABLE Cartable1(
LicensePlate int NOT NULL PRIMARY KEY,
Manufacturer varchar(255) NOT NULL,
Model varchar(255),
Colour varchar(255),
Model varchar(255),
Year int,
EngineSize int,
Value float
);
You do this when you create a table or through an alter table statement:
create table [Cars2].[dbo].[Cartable1] (
LicensePlace varchar(?) primary key,
. . .
);
I recommend adding the primary key when you create the table and before you add any data into it.
If You already have table with data try this ,
ALTER TABLE [Cars2].[dbo].[Cartable1]
ADD CONSTRAINT PRIMARY_KEY_LicensePlate PRIMARY KEY(LicensePlate)

'Disabled' constraints preventing MERGE OUTPUT from working

CREATE TABLE a(
id INT PRIMARY KEY IDENTITY(1,1),
val INT
);
CREATE TABLE b(
id INT PRIMARY KEY IDENTITY(1,1),
val INT
);
CREATE TABLE a_b(
fk_a INT,
fk_b INT
);
CREATE TABLE c(
id INT PRIMARY KEY IDENTITY(1,1),
a_val INT,
b_val INT
);
ALTER TABLE a_b ADD CONSTRAINT fk_a_b_fk_a FOREIGN KEY (fk_a) REFERENCES a(id);
ALTER TABLE a_b ADD CONSTRAINT fk_a_b_fk_b FOREIGN KEY (fk_b) REFERENCES b(id);
INSERT INTO a(val) VALUES (1), (2), (3);
INSERT INTO b(val) VALUES (2), (4), (6);
ALTER TABLE a_b NOCHECK CONSTRAINT ALL;
WITH cte AS (
SELECT
a.id AS 'a_id',
a.val AS 'a_val',
b.id AS 'b_id',
b.val AS 'b_val'
FROM a
INNER JOIN b
ON a.id = b.id
)
MERGE INTO c
USING cte ON 1 = 0
WHEN NOT MATCHED THEN
INSERT (a_val, b_val)
VALUES (cte.a_val, cte.b_val)
OUTPUT cte.a_id, cte.b_id INTO a_b(fk_a, fk_b);
ALTER TABLE a_b WITH CHECK CHECK CONSTRAINT ALL;
SELECT * FROM c;
The error I'm getting is:
The target table 'a_b' of the OUTPUT INTO clause cannot be on either side of a (primary key, foreign key) relationship. Found reference constraint 'fk_a_b_fk_a'.
However, I'm very clearly disabling the constraints surrounding the merge statement. Is there any way to fix this?
The same thing goes for/is happening with triggers and disabling them surrounding the merge statement doesn't work.
try this
CREATE TABLE #a(
id INT PRIMARY KEY IDENTITY(1,1),
val INT
);
CREATE TABLE #b(
id INT PRIMARY KEY IDENTITY(1,1),
val INT
);
CREATE TABLE #a_b(
fk_a INT,
fk_b INT
);
CREATE TABLE #c(
id INT PRIMARY KEY IDENTITY(1,1),
a_val INT,
b_val INT
);
ALTER TABLE #a_b ADD CONSTRAINT fk_a_b_fk_a FOREIGN KEY (fk_a) REFERENCES a(id);
ALTER TABLE #a_b ADD CONSTRAINT fk_a_b_fk_b FOREIGN KEY (fk_b) REFERENCES b(id);
INSERT INTO #a(val) VALUES (1), (2), (3);
INSERT INTO #b(val) VALUES (2), (4), (6);
WITH cte AS (
SELECT
a.id AS 'a_id',
a.val AS 'a_val',
b.id AS 'b_id',
b.val AS 'b_val'
FROM #a a
INNER JOIN #b b
ON a.id = b.id
)
MERGE INTO #c
USING cte ON 1 = 0
WHEN NOT MATCHED THEN
INSERT (a_val, b_val)
VALUES (cte.a_val, cte.b_val)
OUTPUT cte.a_id, cte.b_id INTO #a_b(fk_a, fk_b);
SELECT * FROM #c;

Reference from one table to another entire table and specified row

I've got three tables: A, B and C. I want to create table A, and it should have 3 columns, first one should be a PRIMARY KEY.
1) How to create second column, with is a refference to table B,
2) and third with is a refference to 'C.id' row from table C. A.id = C.id
CREATE TABLE A
(
id SERIAL PRIMARY KEY,
// ? - reference to table B
// ? - reference to C.id row. A.id = C.id
)
Database: postgresql
I assume you use mysql database.
CREATE TABLE A
(
id INT NOT NULL PRIMARY KEY,
b_id INT NOT NULL,
c_id INT NOT NULL,
FOREIGN KEY (b_id) REFERENCES B (id),
FOREIGN KEY (c_id) REFERENCES C (id)
) TYPE = INNODB;
Update for using postgresql:
CREATE TABLE "A"
(
id integer NOT NULL,
b_id integer NOT NULL,
c_id integer NOT NULL,
CONSTRAINT id PRIMARY KEY (id),
CONSTRAINT b_id FOREIGN KEY (b_id) REFERENCES "B" (id)
ON UPDATE NO ACTION ON DELETE NO ACTION, --with no action restriction
CONSTRAINT c_id FOREIGN KEY (c_id) REFERENCES "C" (id)
ON UPDATE CASCADE ON DELETE CASCADE --with cascade restriction
)
WITH (
OIDS = FALSE
)
;
ALTER TABLE "C" OWNER TO postgres;

How to insert row in table with foreign key to itself?

I have table that has foreign key for itself. Column parentid is foreign key and it cannot be NULL.
if I doINSERT INTO mytable(name) VALUES('name'), so it says that can't insert NULL to parentid. BUT, what value I can set to it if no row was inserted yet?!
How I can write script that will add row to this table?
Thank you
Remove the NOT NULL constraint, as it is an inappropriate constraint. If you do not have a ParentId then the value is NULL and should be allowed. Creating a dummy row just to have a dummy parentid creates unnecessary dependencies.
A trick: Have a dummy row with a dummy key, say 99999. Insert with this as the FK, and then change the FK to its real value. And do it in a transaction.
Disable the FK in charge.
Then do the insert
Then do an update with the new (generated?) PK-ID into the Self-FK-Field
Then Enable the FK back.
LIke so:
ALTER TABLE [Client] NOCHECK CONSTRAINT [FK_Client_MainClient]
INSERT INTO Client VALUES ...
#ClientID = SCOPE_IDENTITY()
IF #IsMainClient=1
BEGIN
UPDATE [Client]
SET MainClientID = #ClientID
WHERE ClientID = #ClientID
END
ALTER TABLE [Relatie] WITH CHECK CHECK CONSTRAINT [FK_Relatie_Relatie]
How to make a dummy row with both id and parentid equal to -1:
CREATE TABLE mytable(
id int NOT NULL IDENTITY,
parentid int NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (parentid) REFERENCES mytable(id)
) ;
SET IDENTITY_INSERT mytable ON ; <-- this allows you to insert the
INSERT INTO mytable(id, parentid) <-- auto incremented identity field
VALUES (-1, -1);
SET IDENTITY_INSERT mytable OFF ;
SELECT * FROM mytable ;
| id | parentid |
| -1 | -1 |
If you have many data from other tables that you want to transfer into this table, you can set the IDENTITY_INSERT variable to ON, insert the data and then set it to OFF again.
But as others said, it might be better to just remove the NOT NULL constraint from the parentid field.
You can alter the column to allow null then set the fk to the new identity and enable not null again.
This should work, though maybe there is a better way
CREATE TABLE mytable
(
id int IDENTITY(1,1) primary key,
name varchar(50) not null,
parentid int not null
)
go
alter table mytable
add constraint FK_mytable_parentid FOREIGN KEY ( parentid ) references mytable(id)
ALTER TABLE mytable alter column parentid int null;
insert into mytable(name)
select 'test'
update mytable
set parentid = SCOPE_IDENTITY()
where id = SCOPE_IDENTITY()
ALTER TABLE mytable alter column parentid int not null;
select * from mytable
drop table mytable
From what I understood you already have id before inserting and you can't insert it because identity field isn't letting you to.
Like you mentioned in your comment:
in 1 table I have the rows 34 'name1'
34, 35 'name2' 34 (id,name,parentid)
and I want to copy them to other table
First table
create table Table1
(
id int not null primary key,
name varchar(20) not null,
parentId int not null
)
insert Table1
values
(34, 'name1', 34),
(35, 'name2', 34)
Second table:
create table Table2
(
id int identity(1, 1) primary key,
name varchar(20) not null,
parentId int not null foreign key references Table2(id)
)
Copying data from the first table to the second one:
set identity_insert Table2 on
insert Table2(id, name, parentId)
select *
from Table1
set identity_insert Table2 on
[Update]
If the second table already has values then:
alter table Table2
add oldId int null
alter table Table2
alter column parentId int null
go
insert Table2(name, oldId)
select name, id
from Table1
update tt3
set parentId = tt2.id
from Table2 tt3
join Table1 tt1 on
tt1.id = tt3.oldId
join Table2 tt2 on
tt1.parentId = tt2.oldId
alter table Table2
drop column oldId
alter table Table2
alter column parentId int not null
Don't reference an IDENTITY column as a self-referencing foreign key. Use an alternative key of the table instead. I guess you are using IDENTITY as a surrogate key but every table ought to have a natural key as well, so the IDENTITY column shouldn't be the only key of your table.

Business-Logic Relationships in SQL

Can anyone please tell me how can one estabilish 1 to 0..1 and 1 to 1..* relationships between tables in SQL (Server)?
Thank you very much.
1 to 1..*
Create a Foreign key from a parent table to the primary key of the child (lookup table).
CREATE TABLE A
(
id int NOT NULL IDENTITY(1,1) PRIMARY KEY,
Somecolumn int,
SomeOtherColumn Varchar(50),
B_id int CONSTRAINT FOREIGN KEY REFERENCES B(id),
-- ...other columns
)
CREATE TABLE B
(
id int NOT NULL IDENTITY(1,1) PRIMARY KEY,
Name Varchar(50)
)
1 to 0..1
Create a table with the primary key also defined as a Foreign key to the parent table
CREATE TABLE [Master]
(
id int NOT NULL IDENTITY(1,1) PRIMARY KEY,
Somecolumn int,
SomeOtherColumn Varchar(50),
-- ...other columns
)
CREATE TABLE [Child]
(
id int NOT NULL PRIMARY KEY,
OtherColumn Varchar(50),
)
ALTER TABLE Child
ADD CONSTRAINT FK_Master FOREIGN KEY (id) REFERENCES Master(id)
One to many
Define two tables (example A and B), with their own primary key
Define a column in Table A as having a Foreign key relationship based on the primary key of Table B
This means that Table A can have one or more records relating to a single record in Table B.
If you already have the tables in place, use the ALTER TABLE statement to create the foreign key constraint:
ALTER TABLE A ADD CONSTRAINT FOREIGN KEY fk_b ( b_id ) references b(id)
* fk_b: Name of the foreign key constraint, must be unique to the database
* b_id: Name of column in Table A you are creating the foreign key relationship on
* b: Name of table, in this case b
* id: Name of column in Table B