Inserting 2 Foreign Keys in a table in SQL Server - sql-server

I have the following 3 tables. They are related:
CREATE TABLE [MemberDetails]
(
[MemberID] int identity (1000,1) NOT NULL UNIQUE,
[MName] varchar(100) NOT NULL,
[MSurname] varchar(100) NOT NULL,
[MPhone] varchar(20) NOT NULL UNIQUE,
[MEmail] varchar(200) NOT NULL UNIQUE,
[MAddress] varchar(250) NOT NULL,
[MActive] char (1) NOT NULL CHECK (MActive IN ('Y','N')) DEFAULT 'Y',
[MUpdateDate] Date NOT NULL DEFAULT GETDATE(),
[MPhoto] Image NOT NULL,
[MDid] int NULL UNIQUE,
[MTid] int NULL UNIQUE,
PRIMARY KEY ([MemberID]),
FOREIGN KEY (MDid) REFERENCES [MembershipDetails] ([MDid])
ON DELETE SET NULL
ON UPDATE CASCADE,
FOREIGN KEY (MTid) REFERENCES [MarketingTarget] ([MTid])
ON DELETE SET NULL
ON UPDATE CASCADE
);
CREATE TABLE [MarketingTarget]
(
[MTid] int identity (5000,1) NOT NULL UNIQUE,
[MDOB] date NOT NULL,
[MSex] char NOT NULL CHECK (MSex IN ('M','F')) DEFAULT 'M',
PRIMARY KEY ([MTid]),
);
CREATE TABLE [MembershipDetails]
(
[MDid] int identity (2000,1) NOT NULL UNIQUE,
[MType] varchar(10) NOT NULL CHECK (MType IN ('Monthly', 'Quaterly', 'Yearly')) DEFAULT 'Monthly',
[JoinDate] Date NOT NULL DEFAULT GETDATE(),
[ExpiryDate] Date NULL,
[MsUpdateDate] Date DEFAULT GETDATE(),
PRIMARY KEY ([MDid])
);
I would like to know if it's possible to insert the FKs automatically into the MemberDetails table from the other two tables? I am trying to write the Stored Procedures.
I was checking the Scope_Identity which can get the last identity generated but I am not sure how to use it properly.
Any suggestions would be much appreciate it.

Here is an example (table has been shortened): Since both IDENTITY values are required, I'd save them into a variable and then insert together into MemberDetail.
I would recommend to have a transaction for this operation. And have a look at TRY..CATCH. Depending on your situation you might want to check XACT_STATE().
https://learn.microsoft.com/en-us/sql/t-sql/language-elements/try-catch-transact-sql?view=sql-server-ver15
and there are examples using a trigger
https://learn.microsoft.com/en-us/sql/t-sql/functions/scope-identity-transact-sql?view=sql-server-ver15
CREATE TABLE [_TEST_MemberDetails] (
[MemberID] int identity (1000,1) NOT NULL UNIQUE,
[MDid] int NULL UNIQUE,
[MTid] int NULL UNIQUE,
PRIMARY KEY ([MemberID]),
FOREIGN KEY (MDid) REFERENCES [_TEST_MembershipDetails] ([MDid]) ON DELETE SET NULL
ON UPDATE CASCADE,
FOREIGN KEY (MTid) REFERENCES [_TEST_MarketingTarget] ([MTid]) ON DELETE SET NULL
ON UPDATE CASCADE
);
CREATE TABLE [_TEST_MarketingTarget] (
[MTid] int identity (5000,1) NOT NULL UNIQUE,
[MDOB] date NOT NULL,
[MSex] char NOT NULL CHECK (MSex IN ('M','F')) DEFAULT 'M',
PRIMARY KEY ([MTid]),
);
CREATE TABLE [_TEST_MembershipDetails] (
[MDid] int identity (2000,1) NOT NULL UNIQUE,
[MType] varchar(10) NOT NULL CHECK (MType IN ('Monthly','Quaterly','Yearly')) DEFAULT 'Monthly',
[JoinDate] Date NOT NULL DEFAULT GETDATE(),
[ExpiryDate] Date NULL,
[MsUpdateDate] Date DEFAULT GETDATE(),
PRIMARY KEY ([MDid])
);
-- SP CODE START HERE
SET XACT_ABORT ON;
BEGIN TRY
BEGIN TRANSACTION
DECLARE #MTID int = 0, #MDID int = 0
INSERT INTO _TEST_MarketingTarget ([MDOB], [MSex])
VALUES ('Jan 1, 2020', 'M')
SELECT #MTID = SCOPE_IDENTITY()
INSERT INTO [_TEST_MembershipDetails] ([MType], [JoinDate], [ExpiryDate], [MsUpdateDate])
VALUES (DEFAULT, DEFAULT, 'Jan 1, 2020', DEFAULT)
SELECT #MDID = SCOPE_IDENTITY()
INSERT INTO [_TEST_MemberDetails] (Mdid, MTid)
SELECT #MDID, #MTID
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF ##TRANCOUNT <> 0 ROLLBACK TRANSACTION
END CATCH
go

Related

Creting FOREIGN KEY constraint on multiple columns with one of them being constant value

When I have table with PRIMARY KEY from 2 columns:
CREATE TABLE SizeTypes
(
TypeID tinyint NOT NULL,
SizeID tinyint NOT NULL,
Name varchar(100) NOT NULL,
CONSTRAINT PK_SizeType
PRIMARY KEY (TypeID, SizeID)
)
How can I create second table with a foreign key that have 1st constant value and 2nd from column like below:
CREATE TABLE Something
(
ID INT IDENTITY(1,1) PRIMARY KEY,
SizeTypeID_1 TINYINT,
SizeTypeID_2 TINYINT,
SizeTypeID_3 TINYINT,
CONSTRAINT FK_Something_SizeTypes_1
FOREIGN KEY (1, SizeTypeID_1)
REFERENCES SizeTypes(TypeID, SizeID),
CONSTRAINT FK_Something_SizeTypes_2
FOREIGN KEY (2, SizeTypeID_2)
REFERENCES SizeTypes(TypeID, SizeID),
CONSTRAINT FK_Something_SizeTypes_3
FOREIGN KEY (3, SizeTypeID_3)
REFERENCES SizeTypes(TypeID, SizeID)
)
This can be done using FOREIGN KEY, if yes then how?
If no then what other ways to do this I have? Triggers on INSERT and UPDATE for table something and on DELETE for table SizeTypes? Any other choices I have?
It looks like the following code will let you create suitable check constraints with the check implemented by a separate function:
-- Create the first table.
create table SizeTypes(
TypeId TinyInt not NULL,
SizeId TinyInt not NULL,
Name VarChar(100) not NULL,
constraint PK_SizeType primary key ( TypeId, SizeId ) );
go
-- Create a function to implement the logic for the check constraint.
create function CheckSizeTypeId(
#TypeId TinyInt, #SizeId TinyInt )
returns Int
as begin
-- Replace the following statement with the logic for your check.
if #SizeId >= 0 and #SizeId <= ( select SizeId from SizeTypes where TypeID = #TypeID )
return 1;
return 0;
end;
go
-- Create the second table with the check constraints.
create table Something(
Id Int identity(1,1) primary key,
SizeTypeId_1 TinyInt,
SizeTypeId_2 TinyInt,
SizeTypeId_3 TinyInt,
constraint Check_SizeTypeId_1 check ( dbo.CheckSizeTypeId( 1, SizeTypeId_1 ) = 1 ),
constraint Check_SizeTypeId_2 check ( dbo.CheckSizeTypeId( 2, SizeTypeId_2 ) = 1 ),
constraint Check_SizeTypeId_3 check ( dbo.CheckSizeTypeId( 3, SizeTypeId_3 ) = 1 ) );
go
-- Houseclean.
drop table SizeTypes;
drop table Something;
drop function CheckSizeTypeId;
Note that the constraints restrict what you can do with values in Something. Changes in SizeTypes will not revalidate data in Something, though that could be implemented in a trigger on SizeTypes.

Why does SCOPE_IDENTITY() return NULL after insert on one table and not on the other?

Why does SCOPE_IDENTITY() return NULL after inserting a row in the ComponentAssociation table (as ##IDENTITY returns the right Id)
while SCOPE_IDENTITY() returns the right Id after inserting a row in the CustomerProjectAssociation table ?
The two association tables are created the same way.
Here is an extract of the table creation script:
-- Creating table 'CustomerProjectAssociation'
CREATE TABLE [dbo].[CustomerProjectAssociation]
(
[Id] int IDENTITY(1,1) NOT NULL,
[CustomerId] int NOT NULL,
[ProjectId] int NOT NULL,
[CreationDate] datetime NOT NULL CONSTRAINT DF_CustomerProjectAssociation_CreationDate DEFAULT (SYSUTCDATETIME()),
[LastModificationDate] datetime NOT NULL CONSTRAINT DF_CustomerProjectAssociation_ModificationDate DEFAULT (SYSUTCDATETIME())
);
GO
-- Creating table 'ComponentAssociation'
CREATE TABLE [dbo].[ComponentAssociation]
(
[Id] int IDENTITY(1,1) NOT NULL,
[EcuId] int NOT NULL,
[CreationDate] datetime NOT NULL CONSTRAINT DF_ComponentAssociation_CreationDate DEFAULT (SYSUTCDATETIME()),
[LastModificationDate] datetime NOT NULL CONSTRAINT DF_ComponentAssociation_ModificationDate DEFAULT (SYSUTCDATETIME()),
[ComponentId] int NOT NULL
);
GO
-- Creating primary key on [Id] in table 'CustomerProjectAssociation'
ALTER TABLE [dbo].[CustomerProjectAssociation]
ADD CONSTRAINT [PK_CustomerProjectAssociation]
PRIMARY KEY CLUSTERED ([Id] ASC);
GO
-- Creating primary key on [Id] in table 'ComponentAssociation'
ALTER TABLE [dbo].[ComponentAssociation]
ADD CONSTRAINT [PK_ComponentAssociation]
PRIMARY KEY CLUSTERED ([Id] ASC);
GO
And here are two queries executed on the database from SQL Server Management Studio:
INSERT [dbo].[CustomerProjectAssociation]([CustomerId], [ProjectId])
VALUES (1, 2)
SELECT
[RowCount] = ##RowCount,
[##IDENTITY] = ##IDENTITY,
[SCOPE_IDENTITY] = SCOPE_IDENTITY()
Result:
RowCount ##IDENTITY SCOPE_IDENTITY
1 24 24
INSERT [dbo].[ComponentAssociation]([EcuId], [ComponentId])
VALUES(1, 2)
SELECT
[RowCount] = ##RowCount,
[##IDENTITY] = ##IDENTITY,
[SCOPE_IDENTITY] = SCOPE_IDENTITY()
Result:
RowCount ##IDENTITY SCOPE_IDENTITY
1 613 NULL
OK, the issue is solved.
Found the solution here: error when inserting into table having instead of trigger from entity data framework
I added the following select statement at the end of the instead of insert,update trigger returning all the computed columns:
select [Id], [CreationDate], [LastModificationDate] from {0}.[dbo].[ComponentAssociation] where ##ROWCOUNT > 0 and Id = scope_identity()

Inserting into a table to by pass constraints

maybe I can get some feedback from some folks on this. I created two tables and inserted data into one table and i put a constraint (Foreign key) on the table std_individual_address.
I get the following error message when I try to execute the insert now:
Msg 515, Level 16, State 2, Line 43 Cannot insert the value NULL into
column 'individual_GUID', table 'ABLE.dbo.std_individual_address';
column does not allow nulls. INSERT fails. The statement has been
terminated.
Here is all my code:
--Create the std_individual table
CREATE TABLE std_individual(
individual_GUID INT NOT NULL IDENTITY,
individual_First_Name VARCHAR(50) NULL,
individual_Last_Name VARCHAR(50) NULL,
individual_email VARCHAR(40) NULL,
PRIMARY KEY (individual_GUID));
--Create the std_individual_address table
CREATE TABLE std_individual_address
(
individual_address_GUID INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
individual_address_line1 VARCHAR(100) NULL,
individual_address_line2 VARCHAR(100) NULL,
individual_address_line3 VARCHAR(100) NULL,
individual_address_city VARCHAR(50) NULL,
individual_address_state VARCHAR(30) NULL,
individual_address_zipcode VARCHAR(30) NULL,
individual_GUID INT NOT NULL,
CONSTRAINT fk_std_individual_address_std_individual FOREIGN KEY (individual_GUID) REFERENCES std_individual (individual_GUID)
)
--Insert Individual Data
INSERT INTO std_individual
(individual_First_Name,individual_Last_Name,individual_email)
VALUES
('Terry','Smith','tsmith#example.net'),
('Ronald','Smegan','ronald#example.net'),
('Arnold','Aggassi','aaggassi#example.edu'),
('Jerry','Brukheimer','bbrukheimer#example.edu');
--Mind the Constraint
INSERT INTO std_individual_address(individual_GUID) SELECT individual_GUID from std_individual
--Attempt to insert rest of the data
INSERT INTO std_individual_address
(individual_address_line1,individual_address_line2,individual_address_city,individual_address_state,
individual_address_zipcode )
VALUES
('8200 Greensboro Drive','Ste 1500','Mclean','Virgina','22102'),
('1121 14th Street, NW','Ste 1000','Washington' ,'District of Columbia','20005'),
('1700 Connecticut Ave,NW','Ste 300','Washington' ,'District of Columbia','20009'),
('205 Pennsylvania Ave,SE','','Washington','District of Columbia','20003');
Then I get the error message above. Any ideas on how to combat that issue?

How to set default value in SQL server 2008?

I have bulk amount of data stored in SQL Server 2008. Now I want to add new field [Book_ID] with default value 1 to existing table but its not working.
code
CREATE TABLE [dbo].[Ayyat_Translation_Language_old_20131209] (
[Ayat_Translation_Language_ID] INT IDENTITY (1, 1) NOT NULL,
[Translation_Laanguage_ID] INT NULL,
[Juz_ID] INT NULL,
[Surah_ID] INT NOT NULL,
[Ayat_Description] NVARCHAR (MAX) COLLATE Arabic_CI_AI_KS_WS NOT NULL,
[Ayat_No] INT NULL,
[Book_ID] INT NULL DEFAULT 1,
PRIMARY KEY CLUSTERED ([Ayat_Translation_Language_ID] ASC),
CONSTRAINT [fkey2] FOREIGN KEY ([Translation_Laanguage_ID]) REFERENCES [dbo].[Translation_Language] ([TransLation_Language_ID]) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT [fkey1] FOREIGN KEY ([Surah_ID]) REFERENCES [dbo].[Surah] ([Surah_ID]) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT [fkey0] FOREIGN KEY ([Juz_ID]) REFERENCES [dbo].[Juz] ([Juz_ID])
);
New field is added to table but it contains Null. any help!
This behavior depends on the way you insert data. If you explicitly insert NULL into the column, it will take NULL, not the default. Omit the Book_ID column out totally during an insert if you want it to take on the default (or, you can also use the keyword DEFAULT as a placeholder).
e.g. This will still insert NULL:
INSERT INTO [dbo].[Ayyat_Translation_Language_old_20131209]
(
[Ayat_Translation_Language_ID], ,
[Translation_Laanguage_ID] ,
[Juz_ID] ,
[Surah_ID] ,
[Ayat_Description] ,
[Ayat_No] ,
[Book_ID]
)
VALUES (1, 2, 3, 4, 'Foo', 5, NULL);
Whereas this will assume the default:
INSERT INTO [dbo].[Ayyat_Translation_Language_old_20131209]
(
[Ayat_Translation_Language_ID], ,
[Translation_Laanguage_ID] ,
[Juz_ID] ,
[Surah_ID] ,
[Ayat_Description] ,
[Ayat_No]
-- BOOK_ID is omitted, or use DEFAULT
)
VALUES (1, 2, 3, 4, 'Foo', 5);
You need to now alter the table to not accept NULL for Book_ID
ALTER TABLE [dbo].[Ayyat_Translation_Language_old_20131209]
ALTER COLUMN [Book_ID] INT NOT NULL DEFAULT 1
If that gives errors, you may need to update the existing records first:
UPDATE [dbo].[Ayyat_Translation_Language_old_20131209]
SET [Book_ID] = 1

SQL - Trigger for auto-incrementing number of signed in people

I am having a little bit of trouble with making a trigger in my SQL. I have two tables:
This one
Create table [user]
(
[id_user] Integer Identity(1,1) NOT NULL,
[id_event] Integer NULL,
[name] Nvarchar(15) NOT NULL,
[lastname] Nvarchar(25) NOT NULL,
[email] Nvarchar(50) NOT NULL, UNIQUE ([email]),
[phone] Integer NULL, UNIQUE ([phone]),
[pass] Nvarchar(50) NOT NULL,
[nick] Nvarchar(20) NOT NULL, UNIQUE ([nick]),
Primary Key ([id_user])
)
go
and this one
Create table [event]
(
[id_event] Integer Identity(1,1) NOT NULL,
[id_creator] Integer NOT NULL,
[name] Nvarchar(50) NOT NULL,
[date] Datetime NOT NULL, UNIQUE ([date]),
[city] Nvarchar(50) NOT NULL,
[street] Nvarchar(50) NOT NULL,
[zip] Integer NOT NULL,
[building_number] Integer NOT NULL,
[n_signed_people] Integer Default 0 NOT NULL Constraint [n_signed_people] Check (n_signed_people <= 20),
Primary Key ([id_akce])
)
Now I need a trigger for when I insert a new user with and id_event, or update existing one with one, to take the id_event I inserted, look in the table of events and increment the n_signed_people in a line with a coresponding id_event, until it is 20. When it is 20, it should say that the event is full. I made something like this, it is working when I add a new user with id, but now I need it to stop at 20 and say its full and also I am not sure if it will work, when I'll try to update existing user, by adding an id_event (I assume it was NULL before update).
CREATE TRIGGER TR_userSigning
ON user
AFTER INSERT
AS
BEGIN
DECLARE #idevent int;
IF (SELECT id_event FROM Inserted) IS NOT NULL --if the id_event is not empty
BEGIN
SELECT #idevent=id_event FROM Inserted; --the inserted id_event will be save in a local variable
UPDATE event SET n_signed_people = n_signed_people+1 WHERE #idevent = id_event;
END
END
go
Good evening,
I did notice some issues with your schema. I want to list the fixes I made in order.
1 - Do not use reserved words. Both user and event are reserved.
2 - Name your constraints. You will be glad they are not some random word when you want to drop one.
3 - I added a foreign key to make sure there is integrity in the relationship.
All this work was done in tempdb. Now, lets get to the fun stuff, the trigger.
-- Just playing around
use tempdb;
go
-- attendee table
if object_id('attendees') > 0
drop table attendees
go
create table attendees
(
id int identity (1,1) NOT NULL constraint pk_attendees primary key,
firstname nvarchar(15) NOT NULL,
lastname nvarchar(25) NOT NULL,
email nvarchar(50) NOT NULL constraint uc_email unique,
phone int NULL constraint uc_phone unique,
pass nvarchar(50) NOT NULL,
nick nvarchar(20) NOT NULL constraint uc_nick unique,
event_id int NOT NULL
)
go
-- events table
if object_id('events') > 0
drop table events
go
create table events
(
id int identity (1,1) NOT NULL constraint pk_events primary key,
creator int NOT NULL,
name nvarchar(50) NOT NULL,
planed_date datetime NOT NULL constraint uc_planed_date unique,
street nvarchar(50) NOT NULL,
city nvarchar(50) NOT NULL,
zip nvarchar(9) NOT NULL,
building_num int NOT NULL,
registered int
constraint df_registered default (0) NOT NULL
constraint chk_registered check (registered <= 20),
);
go
-- add some data
insert into events (creator, name, planed_date, street, city, zip, building_num)
values (1, 'new years eve', '20131231 20:00:00', 'Promenade Street', 'Providence', '02908', 99);
-- make sure their is integrity
alter table attendees add constraint [fk_event_id]
foreign key (event_id) references events (id);
I usually add all three options (insert, update, & delete). You coded for insert in the example above. But you did not code for delete.
Also, both the inserted and deleted tables can contain multiple rows. For instance, if two attendees decide to drop out, you want to minus 2 from the table.
-- create the new trigger.
CREATE TRIGGER [dbo].[trg_attendees_cnt] on [dbo].[attendees]
FOR INSERT, UPDATE, DELETE
AS
BEGIN
-- declare local variable
DECLARE #MYMSG VARCHAR(250);
-- nothing to do?
IF (##rowcount = 0) RETURN;
-- do not count rows
SET NOCOUNT ON;
-- deleted data
IF NOT EXISTS (SELECT * FROM inserted)
BEGIN
UPDATE e
SET e.registered = e.registered - c.total
FROM
[dbo].[events] e
INNER JOIN
(SELECT [event_id], count(*) as total
FROM deleted group by [event_id]) c
ON e.id = c.event_id;
RETURN;
END
-- inserted data
ELSE IF NOT EXISTS (SELECT * FROM deleted)
BEGIN
UPDATE e
SET e.registered = e.registered + c.total
FROM
[dbo].[events] e
INNER JOIN
(SELECT [event_id], count(*) as total
FROM inserted group by [event_id]) c
ON e.id = c.event_id;
RETURN;
END;
-- updated data (no counting involved)
END
GO
Like any good programmer, I need to test my work to make sure it is sound.
Lets add 21 new attendees. The check constraint should fire. This only works since the error generated by the UPDATE rollback the insert.
-- Add 21 attendees
declare #var_cnt int = 0;
declare #var_num char(2);
while (#var_cnt < 22)
begin
set #var_num = str(#var_cnt, 2, 0);
insert into attendees (firstname, lastname, email, phone, pass, nick, event_id)
values ('first-' + #var_num,
'last-' + #var_num,
'email-'+ #var_num,
5554400 + (#var_cnt),
'pass-' + #var_num,
'nick-' + #var_num, 1);
set #var_cnt = #var_cnt + 1
end
go
Last but not least, we need to test a DELETE action.
-- Delete the last row
delete from [dbo].[attendees] where id = 20;
go

Resources