'Disabled' constraints preventing MERGE OUTPUT from working - sql

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;

Related

sql error with foreign key

I have the error here:
The INSERT statement conflicted with the FOREIGN KEY constraint "FK_CHITIETDH__MaDH__4316F928". The conflict occurred in database [...]
But with the code here :
create database abc_1quanly111
use abc_1quanly111
create table abc_1nhacc
(
MaNhaCC varchar(10) primary key,
TenNhaCC varchar(50),
DiaChiCC varchar(50),
Telephone varchar(50),
)
create table abc_1mamathang
(
MaMH varchar(10) primary key,
TenMH varchar(50) not null,
DonGia int check (DonGia>0),
SoLuong int ,
MaNhaCC varchar(10) foreign key references abc_1nhacc(MaNhaCC)
)
--Insert Nha CC --- Values -----
INSERT INTO abc_1nhacc VALUES('NC1','Phong Vu','123 NTMK ','0283232677')
INSERT INTO abc_1nhacc VALUES('NC2','Phong Hoang','123 NTD ','0290120217'),('NC3','THE GIOI DI DONG','12 LE VAN VIET ','028382901'),('NC4','NGUYEN KIM','23 LE DUAN ','01283232677'),('NC5','Phong KIM','13 LE LOI ','0288292677')
select * from abc_1nhacc
--Insert Mat Hang ---- Values ----
insert into abc_1mamathang values('M1','Laptop Sony',1000000,100,'NC1')
,
('M2','Laptop MSI GAMING',3200000,1,'NC2'),
('M3','Laptop ASUS',900000,10,'NC3'),
('M4','Laptop ACER',1340000,20,'NC4'),
('M5','Xperia A',10000,10,'NC5')
select * from abc_1nhacc
select * from abc_1mamathang
create table abc_1khachhang
(
MaKhachHang varchar(10) primary key,
TenKhachHang varchar(50),
DiaChiKhachHang varchar(50),
PhoneKhachHang varchar(10),
)
--Insert Khach Hang ---- Values ----
INSERT INTO abc_1khachhang values('KH1','Anh A','121 Le Duan Q1','09878721'),('KH2','Anh B','12 Quang Trung Q12','01878221'),('KH3','Anh C','331 Nguyen Thi Minh Khai','028787221'),('KH4','Anh D','23 Le Quang Dinh','09872321'),('KH5','Bac AD','','098787211')
create table abc_1dondathang
(
MaSoDonDatHang varchar(10) primary key,
NgayDatHang DATE Default GetDate(),
MaKhachHang varchar(10) foreign key references abc_1khachhang(MaKhachHang)
)
CREATE TABLE CHITIETDH
(
MaDH varchar(10) foreign key references abc_1dondathang,
MaMH varchar(10) foreign key references abc_1mamathang,
Soluong int check (soluong>0)
constraint pk_DHMH primary key(MaDH ,MaMH)
)
select * from abc_1dondathang
select * from abc_1mamathang
select * from CHITIETDH
insert into CHITIETDH values('DH1','MH1',12) // errors
select * from CHITIETDH
I don't know solution how to fix that, can anyone help me thanks.
Your table CHITIETDH has a foreign key in table abc_1dondathang.
You try to insert values to CHITIETDH while abc_1dondathang is empty. You must all the values under this column MaDH varchar(10) foreign key references abc_1dondathang will exist in abc_1dondathang before you insert something in CHITIETDH.
You can read here more.

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

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

Adding a nullable foreign key

I have two tables built like this (this is just a simplified and non-proprietary example):
Person Table
-----------
p_Id, f_name, l_name
Job Table
----------
job_Id, job_desc
I want to add a foreign key column, Persons.job_Id, that can be nullable that references Job.job_Id (the PK) The reason is, the job may not be known in advance, so it could be null. Having an "Other" is not an option.
I had this so far but I'm getting "could not create constraint".
ALTER TABLE dbo.Person
ADD job_Id INT FOREIGN KEY (job_Id) REFERENCES dbo.Job(job_Id)
Try it in two steps:
ALTER TABLE dbo.Person ADD job_Id INT NULL;
ALTER TABLE dbo.Person ADD CONSTRAINT FL_JOB
FOREIGN KEY (job_Id) REFERENCES dbo.Job(job_Id);
Try it like this, WITH NOCHECK:
ALTER TABLE dbo.Person ADD job_Id INT NULL;
ALTER TABLE dbo.Person WITH NOCHECK ADD CONSTRAINT FL_JOB
FOREIGN KEY (job_Id) REFERENCES dbo.Job(job_Id);
Below is my solution with creating foreign key programmatically.
TestTable1 has substitute of FK that is either NULL or matches record in TestTable2.
TestTable2 has standard FK in TestTable1.
CREATE Table TestTable1 (ID1 int IDENTITY UNIQUE, ID2 int NULL);
GO
CREATE Table TestTable2 (ID2 int IDENTITY UNIQUE, ID1 int NOT NULL foreign key references TestTable1(ID1));
GO
CREATE procedure CreateTestRecord1 #ID2 int null AS
begin
if #iD2 IS NOT NULL AND NOT EXISTS(SELECT * from TestTable2 where ID2 = #ID2)
begin
RAISERROR('Cannot insert TestTable1 record. TestTable2 record with ID %d doesnt exist', 16, 1, #ID2);
return;
end
Insert into TestTable1(ID2) OUTPUT Inserted.ID1 Values(#ID2);
end
GO
CREATE procedure LinkTable1toTable2 #ID1 int, #ID2 int NULL as
begin
if #iD2 IS NOT NULL AND NOT EXISTS(SELECT * from TestTable2 where ID2 = #ID2)
begin
RAISERROR('Cannot update ID2 in TestTable1 record. TestTable2 record with ID %d doesnt exist', 16, 1, #ID2);
return;
end
update TestTable1 Set ID2=#ID2 where ID1=#ID1;
select ##ROWCOUNT;
endGO

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.

with 2 field that allow null how to make a constrain that at only one must be filled?

to simplify this let take that table:
table1
-------------
id unique primary int
myVal1 int null (fk)
myVal2 int null (fk)
myData int not null
what would be the best way to create a constrain on this table so only one value can be filled?
these would work:
insert into table1 (myval1,myData) values (1,234)
insert into table1 (myval2,myData) values (1,123)
these would not work:
insert into table1 (myData) values (234)
insert into table1 (myVal1,myval2,myData) values (1,2,123)
try using a check constraint:
CREATE TABLE dbo.Table1
(
rowID int NOT NULL primary key identity(1,1),
myVal1 int NULL,
myVal2 int NULL,
myData int NOT NULL
) ON [PRIMARY]
GO
ALTER TABLE dbo.Table1 ADD CONSTRAINT
CK_Table1_myVal1_or_myVal2 CHECK ((myVal2 IS NOT NULL AND myVal1 IS NULL) OR (myVal2 IS NULL AND myVal1 IS NOT NULL))
GO