primary key of new row automatically goes to 2nd table where it's a foreign key in a new row - sql

This is my 1st table
CREATE TABLE [dbo].[Booking_Date]
(
[Book_ID] INT IDENTITY (1, 1) NOT NULL,
[Book_Checkin_Date] DATETIME NULL,
[Book_Checkout_date] DATETIME NULL,
[Adults] INT NULL,
[Children] INT NULL,
CONSTRAINT [PK_Booking_Date]
PRIMARY KEY CLUSTERED ([Book_ID] ASC)
);
And this is my 2nd table
CREATE TABLE [dbo].[Room_Detail]
(
[R_D_ID] INT IDENTITY (1, 1) NOT NULL,
[Cust_ID] INT NULL,
[Book_ID] INT NULL,
[Room_ID] INT NULL,
[Room_Price] MONEY NULL,
PRIMARY KEY CLUSTERED ([R_D_ID] ASC),
CONSTRAINT [FK_Room_Detail_Customer]
FOREIGN KEY ([Cust_ID]) REFERENCES [dbo].[Customer] ([Cust_ID]),
CONSTRAINT [FK_Room_Detail_Booking_Date]
FOREIGN KEY ([Book_ID]) REFERENCES [dbo].[Booking_Date] ([Book_ID]),
CONSTRAINT [FK_Room_Detail_Room]
FOREIGN KEY ([Room_ID]) REFERENCES [dbo].[Room] ([Room_ID])
);
When I insert data into my 1st table a with booking date, then primary key of that data automatically is inserted into the 2nd table that is Room Detail in the Book_ID column

If you want to do this in T-SQL / SQL Server, you can use an AFTER INSERT trigger on the Booking_Date table - something like this:
CREATE TRIGGER trgInsertBookingDate
ON dbo.Booking_Date
AFTER INSERT
AS
-- for each row newly inserted into "Booking_Date",
-- insert a new (empty) row into "Room_Detail"
INSERT INTO dbo.Room_Detail(Book_ID)
SELECT i.Book_ID
FROM Inserted i
So every time you insert one or multiple row(s) into Booking_Date, a new (more or less empty) row will be inserted into Room_Detail for each of those new rows inserted. Since you don't have any other information available, you can only set the Book_ID column of Room_Detail in the trigger - the other columns will have to somehow be specified / filled later

Related

Speed up SQL Server AFTER INSERTED trigger

I have a SQL Express 2019 server with a table on which I created a trigger that updates a row of that table when another row is inserted. I have to INSERT about 100,000 rows a day. Unfortunately the insert takes about 1-2 hours with the trigger, which is okay but frustrating while testing.
This is my table structure:
[ID] BIGINT IDENTITY(1,1) NOT NULL PRIMARY KEY,
[REPORT_ID] BIGINT NOT NULL,
[CLUSTER_ID] BIGINT NOT NULL,
[ID_NEXT] BIGINT NULL,
[CREATED_AT] DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
[ID_SUCCESSOR] BIGINT NULL,
[CREATED_AT] DATETIME NOT NULL DEFAULT GETUTCDATE(),
CONSTRAINT [FK_CRASHREPORTS_REPORTS2CLUSTERS_CRASHREPORTS] FOREIGN KEY ([REPORT_ID]) REFERENCES [CRASHREPORTS]([ID]),
CONSTRAINT [FK_CRASHREPORTS_REPORTS2CLUSTERS_ID_PREV] FOREIGN KEY ([ID_NEXT]) REFERENCES [CRASHREPORTS_REPORTS2CLUSTERS]([ID]),
CONSTRAINT [FK_CRASHREPORTS_REPORTS2CLUSTERS_ID_PREV] FOREIGN KEY ([ID_SUCCESSOR]) REFERENCES [CRASHREPORTS_REPORTS2CLUSTERS]([ID]),
CONSTRAINT [FK_CRASHREPORTS_REPORTS2CLUSTERS_CRASHREPORTS_CLUSTERS_ID] FOREIGN KEY ([CLUSTER_ID]) REFERENCES [CRASHREPORTS_CLUSTERS]([ID])
And this is my trigger:
CREATE TRIGGER [dbo].[update_prev_after_insert]
ON [dbo].[CRASHREPORTS_REPORTS2CLUSTERS]
AFTER INSERT
AS
BEGIN
MERGE INTO [dbo].[CRASHREPORTS_REPORTS2CLUSTERS] T
USING inserted I
ON T.ID != I.ID AND T.ID_SUCCESSOR is NULL and T.REPORT_ID = I.REPORT_ID
WHEN MATCHED THEN
UPDATE SET ID_SUCCESSOR = I.ID;
END
Everything works as expected but really slow. Does anyone knows how to speed this up?

Composite Keys and Referential Integrity in T-SQL

Is it possible, in T-SQL, to have a relationship table with a composite key composed of 1 column defining Table Type and another column defining the Id of a row from a table referenced in the Table Type column?
For a shared-email address example:Three different user tables (UserA, UserB, UserC)One UserType Table (UserType)One Email Table (EmailAddress)One Email-User Relationship Table (EmailRelationship)The EmailRelationship Table contains three columns, EmailId, UserTypeId and UserId
Can I have a relationship from each User table to the EmailRelationship table (or some other way?) to maintain referential integrity?
I've tried making all three columns in the EmailRelationship table into primary keys, I've tried making only UserTypeId and UserId primary.
CREATE TABLE [dbo].[UserType](
[Id] [int] IDENTITY(1,1) NOT NULL ,
[Type] [varchar](50) NOT NULL)
insert into [dbo].[UserType]
([Type])
values
('A'),('B'),('C')
CREATE TABLE [dbo].[UserA](
[Id] [int] IDENTITY(1,1) NOT NULL,
[UserTypeId] [int] NOT NULL,
[Name] [varchar](50) NOT NULL)
insert into [dbo].[UserA]
(UserTypeId,Name)
values
(1,'UserA')
CREATE TABLE [dbo].[UserB](
[Id] [int] IDENTITY(1,1) NOT NULL,
[UserTypeId] [int] NOT NULL,
[Name] [varchar](50) NOT NULL)
insert into [dbo].[UserB]
(UserTypeId,Name)
values
(2,'UserB')
CREATE TABLE [dbo].[UserC](
[Id] [int] IDENTITY(1,1) NOT NULL,
[UserTypeId] [int] NOT NULL,
[Name] [varchar](50) NOT NULL)
insert into [dbo].[UserC]
(UserTypeId,Name)
values
(3,'UserC')
CREATE TABLE [dbo].[Email](
[Id] [int] IDENTITY(1,1) NOT NULL,
[EmailAddress] [varchar](50) NOT NULL)
insert into [dbo].[email]
(EmailAddress)
values
('SharedEmail#SharedEmail.com')
CREATE TABLE [dbo].[EmailRelationship](
[EmailId] [int] NOT NULL,
[UserTypeId] [int] NOT NULL,
[UserId] [int] NOT NULL)
insert into [dbo].[EmailRelationship]
(EmailId, UserTypeId, UserId)
values
(1,1,1),(1,2,1),(1,3,1)
No there isn't, a foreign key can refer to one table, and one table only, I can think of three ways you could approach this.
The first is to have 3 columns, one for each user table, each column with a foreign key, and a check constraint to check that at one, and only one of the values is not null
CREATE TABLE dbo.EmailRelationship
(
EmailId INT NOT NULL,
UserTypeId INT NOT NULL,
UserAId INT NULL,
UserBId INT NULL,
UserCId INT NULL,
CONSTRAINT FK_EmailRelationship__UserAID FOREIGN KEY (UserAId)
REFERENCES dbo.UserA (Id),
CONSTRAINT FK_EmailRelationship__UserBID FOREIGN KEY (UserBId)
REFERENCES dbo.UserB (Id),
CONSTRAINT FK_EmailRelationship__UserCID FOREIGN KEY (UserCId)
REFERENCES dbo.UserC (Id),
CONSTRAINT CK_EmailRelationship__ValidUserId CHECK
(CASE WHEN UserTypeID = 1 AND UserAId IS NOT NULL AND ISNULL(UserBId, UserCId) IS NULL THEN 1
WHEN UserTypeID = 2 AND UserBId IS NOT NULL AND ISNULL(UserAId, UserCId) IS NULL THEN 1
WHEN UserTypeID = 3 AND UserCId IS NOT NULL AND ISNULL(UserAId, UserBId) IS NULL THEN 1
ELSE 0
END = 1)
);
Then as a quick example trying to insert a UserAId with a user Type ID of 2 gives you an error:
INSERT EmailRelationship (EmailID, UserTypeID, UserAId)
VALUES (1, 1, 1);
The INSERT statement conflicted with the CHECK constraint "CK_EmailRelationship__ValidUserId".
The second approach is to just have a single user table, and store user type against it, along with any other common attributes
CREATE TABLE dbo.[User]
(
Id INT IDENTITY(1, 1) NOT NULL,
UserTypeID INT NOT NULL,
Name VARCHAR(50) NOT NULL,
CONSTRAINT PK_User__UserID PRIMARY KEY (Id),
CONSTRAINT FK_User__UserTypeID FOREIGN KEY (UserTypeID) REFERENCES dbo.UserType (UserTypeID),
CONSTRAINT UQ_User__Id_UserTypeID UNIQUE (Id, UserTypeID)
);
-- NOTE THE UNIQUE CONSTRAINT, THIS WILL BE USED LATER
Then you can just use a normal foreign key constraint on your email relationship table:
CREATE TABLE dbo.EmailRelationship
(
EmailId INT NOT NULL,
UserId INT NOT NULL,
CONSTRAINT PK_EmailRelationship PRIMARY KEY (EmailID),
CONSTRAINT FK_EmailRelationship__EmailId
FOREIGN KEY (EmailID) REFERENCES dbo.Email (Id),
CONSTRAINT FK_EmailRelationship__UserId
FOREIGN KEY (UserId) REFERENCES dbo.[User] (Id)
);
It is then no longer necessary to store UserTypeId against the email relationship because you can join back to User to get this.
Then, if for whatever reason you do need specific tables for different user types (this is not unheard of), you can create these tables, and enforce referential integrity to the user table:
CREATE TABLE dbo.UserA
(
UserID INT NOT NULL,
UserTypeID AS 1 PERSISTED,
SomeOtherCol VARCHAR(50),
CONSTRAINT PK_UserA__UserID PRIMARY KEY (UserID),
CONSTRAINT FK_UserA__UserID_UserTypeID FOREIGN KEY (UserID, UserTypeID)
REFERENCES dbo.[User] (Id, UserTypeID)
);
The foreign key from UserID and the computed column UserTypeID back to the User table, ensures that you can only enter users in this table where the UserTypeID is 1.
A third option is just to have a separate junction table for each User table:
CREATE TABLE dbo.UserAEmailRelationship
(
EmailId INT NOT NULL,
UserAId INT NOT NULL,
CONSTRAINT PK_UserAEmailRelationship PRIMARY KEY (EmailId, UserAId),
CONSTRAINT FK_UserAEmailRelationship__EmailId FOREIGN KEY (EmailId)
REFERENCES dbo.Email (Id),
CONSTRAINT FK_UserAEmailRelationship__UserAId FOREIGN KEY (UserAId)
REFERENCES dbo.UserA (Id)
);
CREATE TABLE dbo.UserBEmailRelationship
(
EmailId INT NOT NULL,
UserBId INT NOT NULL,
CONSTRAINT PK_UserBEmailRelationship PRIMARY KEY (EmailId, UserBId),
CONSTRAINT FK_UserBEmailRelationship__EmailId FOREIGN KEY (EmailId)
REFERENCES dbo.Email (Id),
CONSTRAINT FK_UserBEmailRelationship__UserBId FOREIGN KEY (UserBId)
REFERENCES dbo.UserB (Id)
);
Each approach has it's merits and drawbacks, so you would need to assess what is best for your scenario.
No it does not work that way. You cannot use a column value as a dynamic reference to different tables.
In general the data design is flawed.
Thanks to #GarethD I created a CHECK constraint that called a scalar-function that would enforce referential integrity (only upon insert, refer to caveat below):
Using my above example:
alter FUNCTION [dbo].[UserTableConstraint](#Id int, #UserTypeId int)
RETURNS int
AS
BEGIN
IF EXISTS (SELECT Id From [dbo].[UserA] WHERE Id = #Id and UserTypeId = #UserTypeId)
return 1
ELSE IF EXISTS (SELECT Id From [dbo].[UserB] WHERE Id = #Id and UserTypeId = #UserTypeId)
return 1
ELSE IF EXISTS (SELECT Id From [dbo].[UserC] WHERE Id = #Id and UserTypeId = #UserTypeId)
return 1
return 0
end;
alter table [dbo].[emailrelationship]
--drop constraint CK_UserType
with CHECK add constraint CK_UserType
CHECK([dbo].[UserTableConstraint](UserId,UserTypeId) = 1)
I am sure there is a not insignificant overhead to a Scalar-function call from within a CONSTRAINT. If the above becomes prohibitive I will report back here, though the tables in question will not have to deal with a large volume of INSERTs.
If there are any other reasons to not do the above, I would like to hear them. Thanks!
Update:
I've tested INSERT and UPDATE with 100k rows (SQL Server 2014, 2.1ghz quadcore w/ 8gb ram):
INSERT takes 2 seconds with out the CONSTRAINT
and 3 seconds with the CHECK CONSTRAINT
Turning on IO and TIME STATISTICS causes the INSERT tests to run in:
1.7 seconds with out the CONSTRAINT
and 10 seconds with the CHECK CONSTRAINT
I left the STATISTICS on for the UPDATE 100k rows test:
just over 1sec with out the CONSTRAINT
and 1.5sec with the CHECK CONSTRAINT
My referenced tables (UserA, UserB, UserC from my example) only contain around 10k rows each, so anybody else looking to implement the above may want to run some additional testing, especially if your referenced tables contain millions of rows.
Caveat:
The above solution may not be suitable for most uses, as the only time referential integrity is checked is during the CHECK CONSTRAINT upon INSERT. Any other operations or modifications of the data needs to take that into account. For example, using the above, if an Email is deleted any related EmailRelationship entries will be pointing to invalid data.

CONSTRAINT to foreign keys to one table - causes error

I am starting to build something like system up to the just cooperate with suggestions coming into the database called ForslagOpslag.
Every time I try to update table with likes, you will see it with this one error:
Update cannot proceed due to validation errors. Please correct the following errors and try again.
SQL71516 :: The referenced table '[dbo].[ForslagOpslag]' contains no
primary or candidate keys that match the referencing column list in
the foreign key. If the referenced column is a computed column, it
should be persisted.
Here is how I built my ForslagOpslagLikes table:
CREATE TABLE [dbo].[ForslagOpslagLikes]
(
[fk_brugerid] INT NOT NULL,
[opretdato] DATETIME NOT NULL,
[getid] INT NOT NULL,
CONSTRAINT [PK_ForslagOpslagLikes]
PRIMARY KEY CLUSTERED ([fk_brugerid], [getid]),
CONSTRAINT [FK_ForslagOpslagLikes_ToGetid]
FOREIGN KEY ([getid])
REFERENCES [dbo].[ForslagOpslag]([Id]),
CONSTRAINT [FK_ForslagOpslagLikes_ToForslagBrugerid]
FOREIGN KEY ([fk_brugerid])
REFERENCES [dbo].[ForslagOpslag]([fk_brugerid])
);
Reason I have both fk_brugerid and getid is for sure me that the user can not vote / like more once!
The way I have built my ForslagOpslag table:
CREATE TABLE [dbo].[ForslagOpslag]
(
[Id] INT IDENTITY (1, 1) NOT NULL,
[text] NVARCHAR (MAX) NOT NULL,
[fk_brugerid] INT NOT NULL,
[opretdato] DATETIME NOT NULL,
PRIMARY KEY CLUSTERED ([Id] ASC)
);
like this to be my like system do:
ForslagOpslagLikes -> fk_brugerid to ForslagOpslag -> fk_brugerid
ForslagOpslagLikes -> getid to ForslagOpslag -> id
Well - the error seems pretty clear: you're trying to estabslish a foreign key relationship to ForslagOpslag.fk_brugerid here:
CONSTRAINT [FK_ForslagOpslagLikes_ToForslagBrugerid]
FOREIGN KEY ([fk_brugerid])
REFERENCES [dbo].[ForslagOpslag]([fk_brugerid])
but that column is NOT the primary key of that other table - and it's not a UNIQUE constraint either - so you cannot reference that column in a foreign key relationship.
But the column(s) that a foreign key references must be the primary key of that other table - or in SQL Server, it's good enough if there's a UNIQUE constraint on that column. You must ensure that the values you reference in FroslagOpslag only match a single column in that table - otherwise, you cannot establish a foreign key relationship
Try to remove the foreing key in your first table like this
CREATE TABLE [dbo].[ForslagOpslagLikes]
(
[fk_brugerid] INT NOT NULL,
[opretdato] DATETIME NOT NULL,
[getid] INT NOT NULL,
CONSTRAINT [PK_ForslagOpslagLikes]
PRIMARY KEY CLUSTERED ([fk_brugerid], [getid]),
CONSTRAINT [FK_ForslagOpslagLikes_ToGetid]
FOREIGN KEY ([getid])
REFERENCES [dbo].[ForslagOpslag]([Id]),
);
Then you need to add the foreign key in the second table with the primary key with the first table
CREATE TABLE [dbo].[ForslagOpslag]
(
[Id] INT IDENTITY (1, 1) NOT NULL,
[text] NVARCHAR (MAX) NOT NULL,
[fk_brugerid] INT NOT NULL,
[opretdato] DATETIME NOT NULL,
PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_ForslagOpslagLikes_ToForslagBrugerid]
FOREIGN KEY ([fk_brugerid])
REFERENCES [dbo].[ForslagOpslagLikes]([fk_brugerid])
);
You sound scandinavian, and Bruger means User (for all the non-scandinavians here).
What you appear to want is a Bruger (User) table, where fk_brugerid in ForslagOpslag is the user who created the record with opretdato being the creation date, and ForslagOpslagLikes is an association table of users who likes the ForslagOpslag with opretdato being the date they clicked on "Like".
CREATE TABLE [dbo].[Bruger]
(
[brugerid] INT IDENTITY (1, 1) NOT NULL,
...,
CONSTRAINT [PK_Bruger]
PRIMARY KEY CLUSTERED ([brugerid])
);
CREATE TABLE [dbo].[ForslagOpslag]
(
[Id] INT IDENTITY (1, 1) NOT NULL,
[text] NVARCHAR(MAX) NOT NULL,
[fk_brugerid] INT NOT NULL,
[opretdato] DATETIME NOT NULL,
CONSTRAINT [PK_ForslagOpslag]
PRIMARY KEY CLUSTERED ([Id]),
CONSTRAINT [FK_ForslagOpslag_Bruger]
FOREIGN KEY ([fk_brugerid])
REFERENCES [dbo].[Bruger] ([brugerid])
);
CREATE TABLE [dbo].[ForslagOpslagLikes]
(
[fk_brugerid] INT NOT NULL,
[opretdato] DATETIME NOT NULL,
[getid] INT NOT NULL,
CONSTRAINT [PK_ForslagOpslagLikes]
PRIMARY KEY CLUSTERED ([fk_brugerid], [getid]),
CONSTRAINT [FK_ForslagOpslagLikes_Bruger]
FOREIGN KEY ([fk_brugerid])
REFERENCES [dbo].[Bruger] ([brugerid]),
CONSTRAINT [FK_ForslagOpslagLikes_ForslagOpslag]
FOREIGN KEY ([getid])
REFERENCES [dbo].[ForslagOpslag]([Id])
);

Oracle - How do I create a table that has an autoincrementing unique key for the ID

This is the first time that I've use oracle SQL and I'm having a problem creating tables with a unique key.
I don't understand why this auto-incrementing id is not working:
ID BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY
The next question I have is why I am getting an error in each of my statements:
ORA-00922: missing or invalid option
Here is my code:
--
-- Sequence for aout incrment
--
CREATE SEQUENCE IF NOT EXISTS AUTO_INC_SEQ
START WITH 1
INCREMENT BY 1;
--
-- Table Person
--
CREATE TABLE IF NOT EXISTS FITNESS_PERSON
(
ID NUMBER NOT NULL PRIMARY KEY,
FIRST_NAME VARCHAR NOT NULL,
LAST_NAME VARCHAR NOT NULL,
NICK_NAME VARCHAR NOT NULL,
DATE_BIRTH DATE NOT NULL,
PASSWORD VARCHAR NOT NULL,
CONSTRAINT UNIQUE(NICK_NAME)
);
--
-- Table BMR
--
CREATE TABLE IF NOT EXISTS FITNESS_BMR
(
ID NUMBER NOT NULL PRIMARY KEY,
VALUE FLOAT NOT NULL,
VALUE_DATE DATE NOT NULL
);
--
-- M:N for BMR and Person
--
CREATE TABLE IF NOT EXISTS FITNESS_BMR_PERSON
(
BMR_ID NUMBER NOT NULL,
PERSON_ID NUMBER NOT NULL,
FOREIGN KEY(BMR_ID) REFERENCES FITNESS_BMR(ID),
FOREIGN KEY(PERSON_ID) REFERENCES FITNESS_PERSON(ID),
CONSTRAINT BMR_PER PRIMARY KEY(BMR_ID, PERSON_ID)
);
What's the right way to do this (create a table and with an auto-incrementing key that is unique).
You can use a table, a sequence to generate unique ID values and a trigger.
For example:
Table:
CREATE Table FITNESS_BMR
(
ID NUMBER NOT NULL PRIMARY KEY,
VALUE FLOAT NOT NULL,
VALUE_DATE DATE NOT NULL
);
Sequence: create sequence t1_seq start with 1 increment by 1 nomaxvalue;
Trigger:
CREATE OR REPLACE TRIGGER test_trigger
BEFORE INSERT
ON FITNESS_BMR
REFERENCING NEW AS NEW
FOR EACH ROW
BEGIN
SELECT t1_seq.nextval INTO :NEW.ID FROM dual;
END;
/

Sybase constraint with foreign key and conditional check against foreign table column value

Is it possible on Sybase to define a constraint(s) which require a column to be a foreign key and also satisfy a condition based on the value of a foreign column, e.g. in the below example tables could a constraint be created on the "product" table such that "product.code is a foreign key of a brand.code which has valid=1"?
CREATE TABLE brand (
code char(8) NOT NULL,
valid int NOT NULL,
rowid numeric(10,0) IDENTITY,
CONSTRAINT brand_pk PRIMARY KEY (code),
CONSTRAINT valid_check CHECK (valid IN (0,1))
)
CREATE TABLE product (
code char(8) NOT NULL,
CONSTRAINT product_pk PRIMARY KEY (code)
)
I think it's best to change the structure just a little bit.
CREATE TABLE brand (
code char(8) NOT NULL,
valid int NOT NULL,
rowid numeric(10,0) IDENTITY,
CONSTRAINT brand_pk PRIMARY KEY (code),
-- The following UNIQUE constraint lets the pair of values be the target of
-- a foreign key reference.
CONSTRAINT brand_is_valid UNIQUE (code, valid),
CONSTRAINT valid_check CHECK (valid IN (0,1))
);
CREATE TABLE product (
code char(8) NOT NULL,
valid int NOT NULL,
-- The column "code" is a PK in the referenced table, so this still works. It's
-- a 1:0 or 1:1 relationship.
CONSTRAINT product_pk PRIMARY KEY (code),
-- The next constraint requires a unique constraint on the pair of
-- columns in the table "brand". By itself, it references every row
-- in "brand". That's too many rows.
CONSTRAINT product_fk FOREIGN KEY (code, valid)
REFERENCES brand (code, valid),
-- But this constraint restricts the foreign key references to only those
-- rows that have valid = 1 in the table "brand".
CHECK (valid = 1)
);
In order to bypass the creation of a foreign-key based on the "valid" condition, you will need to modify your table design and create a trigger to set the product.code = NULL. Pardon my syntax (I haven't coded Sybase for a while), but this is the general idea:
Add a new column to serve as the primary key since we will need to set product.code = NULL when valid=0:
CREATE TABLE product (
rowid int identity primary key,
code char(8) NULL,
CONSTRAINT brand_fk FOREIGN KEY (code) REFERENCES brand(code)
)
Then create a trigger similar to this one:
create trigger FK_WhenValid
on product
for insert
AS
IF (SELECT COUNT(*) FROM brand b inner join inserted i on b.code = i.code AND b.valid=0 ) > 0
BEGIN
UPDATE product SET code = NULL WHERE code in (SELECT i.code from brand b join inserted i on b.code = i.code and b.valid = 0)
END
Note: this trigger only supports product insertions. If "valid" can change, another approach is required.
You could also implement the foreign key as a trigger instead of a declarative constraint and only set product.code = inserted.code when valid = 1