I have a messaging system with three tables: Event_Types, Messages, and Event_Subscription. All messages have an event type, and each event type can have 0 or more subscriptions. In the Event Subscription table are a set of columns that can be used to further filter the messages coming in. So if i set Src to a non-null value in Event_Subscription, I am saying that I only want messages with that event types from that particular source.
Currently, our query is writer such that for each column, we check if it's null, and if it isn't we filter the message by that column. I am fairly certain that this isn't going to perform well, especially since our actual prod version will have 11 different columns to filter by.
My question is, is there a more performant way to filter the messages table by the event subscriptions table?
example
IF OBJECT_ID('tempdb..#event_types') IS NOT NULL
DROP TABLE #event_types
IF OBJECT_ID('tempdb..#messages') IS NOT NULL
DROP TABLE #messages
IF OBJECT_ID('tempdb..#event_subscription') IS NOT NULL
DROP TABLE #event_subscription
CREATE TABLE #event_types
(
PK_EventTypeID INT IDENTITY(1, 1) PRIMARY KEY,
EventTypeName VARCHAR(50)
)
CREATE TABLE #messages
(
PK_MessageID INT IDENTITY(1, 1) PRIMARY KEY,
FK_EventTypeID INT NOT NULL,
Src VARCHAR(50),
[Version] VARCHAR(50)
)
CREATE TABLE #event_subscription
(
PK_EventSubscriptionID INT IDENTITY(1, 1) PRIMARY KEY,
FK_EventTypeID INT NOT NULL,
SubScriberID INT NOT null,
Src VARCHAR(50) null,
[Version] VARCHAR(50) null
)
INSERT INTO #event_types ( EventTypeName )
VALUES ('Insert'), ('Update'), ('Delete')
INSERT INTO #event_subscription
(
FK_EventTypeID,
SubScriberID,
Src,
[Version]
)
VALUES
( 1, 1000, null, null ), /* all inserts */
( 2, 1001, 'System A', '1.0.0' ) /*All updates from System A with version 1.0.0*/
INSERT INTO #messages ( FK_EventTypeID, Src, Version )
VALUES
( 1, 'System A', '1.0.0' ),
( 1, 'System B', '2.0.0' ),
( 2, 'System A', '1.0.0' ),
( 2, 'System B', '2.0.0' )
SELECT *
FROM #messages m
INNER JOIN #event_types et
ON m.FK_EventTypeID = et.PK_EventTypeID
INNER JOIN #event_subscription es
ON m.FK_EventTypeID = es.FK_EventTypeID
WHERE (es.Src IS NULL OR m.Src = es.Src)
AND (es.[Version] IS NULL OR m.[Version] = es.[Version])
Related
I can't insert data in a table where 2 foreign keys refer to one primary key...
The code is as follows:
create table Currency
(
ID int primary key identity(1,1),
Code nvarchar(8) not null,
Name nvarchar(128) not null,
Is_Active bit not null default(0),
Is_Base_Currency bit not null default(0),
Country_id int foreign key(ID) references Country(ID) not null
)
Create table Currency_rate
(
ID int primary key identity(1,1),
Currency_id int foreign key(ID) references Currency(ID) not null,
Base_currency_id int foreign key(ID) references Currency(ID) not null,
Rate decimal(16,6) not null,
Ts datetime default getDate()
)
Insert into Currency_rate(Currency_id, Base_currency_id, Rate, Ts)
values (1, 1, 121212.212121, '2008-11-11 13:23:44.111'),
(2, 2, 232323.323232, '2009-11-11 13:23:44.222'),
(3, 3, 343434.434343, '2010-11-11 13:23:44.333')
This is the error I get:
Msg 547, Level 16, State 0, Line 1
The INSERT statement conflicted with the FOREIGN KEY constraint "FK__Currency___Curre__239E4DCF". The conflict occurred in database "CryptoCurrencyData", table "dbo.Currency", column 'ID'.
The statement has been terminated.
Please help me - I can't find any solution surfing the internet...
Thank you all,
Regards,
Elias.H
Constrainsts are created to prevent corrupting data by inserting or updating your tables.
In your case, you are trying to insert data which is not existing. You are trying to insert into Currency_rate values ID's of Currency which are not existing in Currency table. So this is a whole goal of constraints - prevent corruption of data.
Just for demonstration purposes I've created Country table:
Create table Country (
ID int primary key identity(1,1),
CountryName nvarchar(50)
)
Then your first step would be:
INSERT INTO dbo.Country
(
--ID - this column value is auto-generated
CountryName
)
VALUES
(
-- ID - int
N'India' -- CountryName - nvarchar
)
, (N'Canada')
, (N'South America')
The second step will be:
INSERT INTO dbo.Currency
(
--ID - this column value is auto-generated
Code,
Name,
Is_Active,
Is_Base_Currency,
Country_id
)
VALUES
(
-- ID - int
N'Code1', -- Code - nvarchar
N'India Currency', -- Name - nvarchar
0, -- Is_Active - bit
0, -- Is_Base_Currency - bit
1 -- Country_id - int
)
, (
N'Code2', -- Code - nvarchar
N'Canada Currency', -- Name - nvarchar
0, -- Is_Active - bit
0, -- Is_Base_Currency - bit
2 -- Country_id - int
)
, (
N'Code3', -- Code - nvarchar
N'South America Currency', -- Name - nvarchar
0, -- Is_Active - bit
0, -- Is_Base_Currency - bit
3 -- Country_id - int
)
And the final step is the following:
Insert into Currency_rate(Currency_id,Base_currency_id,Rate,Ts)
values(1,1,121212.212121,'2008-11-11 13:23:44.111'),
(2,2,232323.323232,'2009-11-11 13:23:44.222'),
(3,3,343434.434343,'2010-11-11 13:23:44.333')
I have a data table that contains a name and a social security number. I want to insert the name into a table with an identity field, then insert the ssn with that new identity field value into another table.
Below are the tables:
CREATE TABLE [data_table]
(
[name] [varchar](50) NOT NULL,
[ssn] [varchar](9) NOT NULL,
)
CREATE TABLE [entity_key_table]
(
[entity_key] [int] IDENTITY(1000000,1) NOT NULL,
[name] [varchar](50) NOT NULL,
)
CREATE TABLE [entity_identifier_table]
(
[entity_identifier_key] [int] IDENTITY(1000000,1) NOT NULL,
[entity_key] [int] NOT NULL,
[ssn] [int] NOT NULL,
)
This query works but doesn't link entity_key in [entity_key_table] TO ssn in [entity_identifier_table]:
INSERT INTO entity_key_table (name)
OUTPUT [INSERTED].[entity_key]
INTO [entity_identifier_table] (entity_key)
SELECT [name]
FROM [data_table]
This is what I want to do, but it doesn't work.
INSERT INTO entity (name)
OUTPUT [INSERTED].[entity_key], [data_table].[ssn]
INTO [entity_identifier] (entity_key,ssn)
SELECT [name]
FROM [data_table]
Rewriting my answer based on your requirements and the articles you linked. I think you can get that behavior doing something like this. I admit, I have never seen a merge on something like 1 != 1 like the article suggests, so I would be very cautious with this and test the bajeezes out out of it.
FWIW, it looks like during an INSERT, you can't access data that's not in the inserted virtual table, but updates (and apparently MERGE statements) can.
if object_id('tempdb.dbo.#data_table') is not null drop table #data_table
create table #data_table
(
[name] [varchar](50) NOT NULL,
[ssn] [varchar](9) NOT NULL,
)
if object_id('tempdb.dbo.#entity_key_table') is not null drop table #entity_key_table
create table #entity_key_table
(
[entity_key] [int] IDENTITY(1000000,1) NOT NULL,
name varchar(50)
)
if object_id('tempdb.dbo.#entity_identifier_table') is not null drop table #entity_identifier_table
create table #entity_identifier_table
(
[entity_identifier_key] [int] IDENTITY(2000000,1) NOT NULL,
[entity_key] [int] NOT NULL,
[ssn] varchar(9) NOT NULL,
)
insert into #Data_table (Name, SSN)
select 'John', '123456789' union all
select 'John', '001100110' union all
select 'Jill', '987654321'
merge into #entity_key_table t
using #data_table s
on 1 != 1
when not matched then insert
(
name
)
values
(
s.name
)
output inserted.entity_key, s.ssn
into #entity_identifier_table
(
entity_key,
ssn
);
select top 1000 *
from #data_table
select top 1000 *
from #entity_key_table
select top 1000 *
from #entity_identifier_table
The problem with your code is that you output data only from inserted or deleted.
Assuming your name column only relates to one SSN, the following would work:
DECLARE #output TABLE (entity_key INT,ssn VARCHAR (11))
INSERT INTO entity (entity_key, name)
OUTPUT [INSERTED].[entity_key], [inserted].[name]
INTO #output
SELECT D.Entity_key, d.name
FROM datatable
INSERT INTO entity_identifier (entity_key, ssn)
Select o.entity_key, d.snn
from #output o
join datatable d on o.name = d.name
However, the problem of multiple duplicated names having different Social Security Numbers is extremely high. In this case, your current structure simply does not work because there is no way to know which identity belongs to which name record. (The Merge solution in another post may also have this problem, before you put that to production be sure to test the scenario of duplicated names. The chances of duplicated names in a set of records is extremely high in any reasonable large data set of names and this should be one of your unit test for any potential solution.)
Here is a potential workaround. First, insert the SSN as the name in the first insert, then return output as shown but join on the #output Name column to the SSN column. After doing the other insert, then update the name in the orginal table to the correct name again joining on the SSN data.
DECLARE #output TABLE (entity_key INT,ssn VARCHAR (11))
INSERT INTO entity (entity_key, name)
OUTPUT [INSERTED].[entity_key], [inserted].[ssn]
INTO #output
SELECT D.Entity_key, d.name
FROM datatable
INSERT INTO entity_identifier (entity_key, ssn)
Select o.entity_key, d.output
from #output o
update e
set name = d.name
FROM entity e
join #output o on e.entity_key = o.entity_key
join datatable d on o.name = d.ssn
The project I am working on has 7 levels to their business hierarchy. None of them are of the same type. Meaning, this is not an organizational chart and all of the items are Employees of some level or other. They are things like Division, Region, Sales VP, Business Unit and such. Yes, some of them are perhaps Employees, but not all of them.
Currently, I have them each in their own table that follow a similar pattern to each other where each child has a foreign key to their parent. So starting with the smallest part of the hierarchy:
BusinessUnit (table)
ID
Name
AreaManagerID
AreaManager (table)
ID
Name
RegionalManagerID
RegionalManager (table)
ID
Name
DivisionID
Division (table)
ID
Name
There are 3 more tables intermixed, but this should show you the rather simple link between each level of the hierarchy. Every child must have a parent. There will not be any AreaManager that has no BusinessUnits.
Reading up a bit on the HierarchyID I am not totally sure it will help me.
I know the above works and it is fine. But I am more wondering if there is a better way and/or faster way when I am tasked with being given a Division and need to find all of the BU's within it. Or even being given a Region and needing to find all of the BU's within it.
If you're looking for "get me descendants", HierarchyID is pretty fast at finding descendants to an arbitrary depth. If you were to do that, I'd put all of the entities into one table with break out tables for the different types. It would look a little something like this:
CREATE TABLE [dbo].[BusinessEntity] (
[EntityID] INT IDENTITY NOT NULL PRIMARY KEY,
[ParentEntityID] INT
REFERENCES [dbo].[BusinessEntity] ([EntityID]),
[EntityType] TINYINT NOT NULL,
[Path] HIERARCHYID
);
CREATE TABLE [dbo].[BusinessUnit] (
[ID] INT NOT NULL PRIMARY KEY
REFERENCES [dbo].[BusinessEntity] ([EntityID]),
[Name] VARCHAR(255) NOT NULL,
[AreaManagerID] INT NOT NULL
);
CREATE TABLE [dbo].[AreaManager] (
[ID] INT NOT NULL PRIMARY KEY
REFERENCES [dbo].[BusinessEntity] ([EntityID]),
[Name] VARCHAR(255) NOT NULL,
[RegionalManagerID] INT NOT NULL
);
CREATE TABLE [dbo].[RegionalManager] (
[ID] INT NOT NULL PRIMARY KEY
REFERENCES [dbo].[BusinessEntity] ([EntityID]),
[Name] VARCHAR(255) NOT NULL,
[DivisionID] INT NOT NULL
);
CREATE TABLE [dbo].[Division] (
[ID] INT NOT NULL PRIMARY KEY
REFERENCES [dbo].[BusinessEntity] ([EntityID]),
[Name] VARCHAR(255)
);
When you go to insert into one of your actual tables (e.g. BusinessUnit, RegionalManager, etc), you'd first create a record in BusinessEntity and then use the generated identity value as the identifier for the insert. You'll also need to keep the Path column up to date with respect to its relationship in the hierarchy. That is, let's say that I have the following data in BusinessEntity:
SET IDENTITY_INSERT [dbo].[BusinessEntity] ON;
INSERT INTO [dbo].[BusinessEntity]
( [EntityID],
[ParentEntityID] ,
[EntityType]
)
VALUES
(1, NULL, 1),
(2, 1, 2),
(3, 1, 2),
(4, 2, 3),
(5, 3, 3),
(6, 4, 4),
(7, 6, 5);
Then I can use the following CTE to generate the Path values
WITH cte AS (
SELECT [be].[EntityID], [be].[ParentEntityID], CAST(CONCAT('/', [be].[EntityID], '/') AS VARCHAR(MAX)) AS [Path]
FROM [dbo].[BusinessEntity] AS [be]
WHERE [be].[ParentEntityID] IS null
UNION ALL
SELECT [child].[EntityID], [child].[ParentEntityID], CAST(CONCAT([parent].[Path], child.[EntityID], '/') AS VARCHAR(MAX))
FROM [dbo].[BusinessEntity] AS [child]
JOIN [cte] AS [parent]
ON [child].[ParentEntityID] = [parent].[EntityID]
)
UPDATE [be]
SET [be].[Path] = cte.[Path]
FROM [dbo].[BusinessEntity] AS be
JOIN cte
ON [be].[EntityID] = [cte].[EntityID]
WHERE [Path] IS NULL;
Of course, keeping them up to date is a lot easier. When you insert a new row, grab the Path from the parent row, tack your ID onto it, and that's your Path. Updating a row's parent is a little trickier, but not terrible. I'll leave it as an exercise for the reader. But as a hint, it involves the GetReparentedValue() method of the HierarchyID data type. Finally, if ever you don't trust the value in Path (as it is a derived value), you can just set whatever values you don't trust to NULL and re-run the above cte update.
Hopefully someone can help. I have created two tables Customer and Order as follows;
CREATE TABLE Customer
CustomerID int NOT NULL PRIMARY KEY
CustomerName varchar(25)
The other columns in Customer table are not relevant to my question, so I will not include them here. My CustomerID numbers are from 1 through to 15, all unique.
The second table I created is Orders as follows
CREATE TABLE Orders
OrderID smallint NOT NULL PRIMARY KEY
OrderDate date NOT NULL
CustomerID int FOREIGN KEY REFERENCES Customer (CustomerID);
My insert values is as follows
INSERT INTO Orders (OrderID, OrderDate, CustomerID)
VALUES
(1001, '2008-10-21', 1),
(1002, '2008-10-21', 8),
(1003, '2008-10-22', 15),
(1004, '2008-10-22', 5),
(1005, '2008-10-24', 3),
(1006, '2008-10-24', 2),
(1007, '2008-10-27', 11),
(1008, '2008-10-30', 12),
(1009, '2008-11-05', 4),
(1010, '2008-11-05', 1);
When I try to insert my values into the Order table, I get the following error message....
Msg 547, Level 16, State 0, Line 1.....The INSERT statement conflicted
with the FOREIGN KEY constraint "FK__OrderT__Customer__2D27B809". The
conflict occurred table "dbo.Customer", column 'CustomerID'. The
statement has been terminated.
The numbers for CustomerID in my Order table, are (1; 1; 2; 3; 4; 5; 8; 11; 12 and 15). Therefore I have checked that all my CustomerID numbers in Order table are also in the Customer table.
So my questions are
1) Has the insert values failed because my CustomerID column in Customer table in NOT NULL and I in error made CustomerID column NULL in Order.
2) If the answer to the above question is yes, then is it possible for me to (a) drop the foreign key on the CustomerID column in Order (b) change the column to NOT NULL and (c) then add the foreign key constraint again to this column and then insert the values again?
It might be easier to drop and re-create the table Order. But I am curious if option 2 would work, re dropping and adding a foreign key on the same column.
Hopefully I am on the right track with why I think the error occurred, feel
to correct me if I am wrong.
Thanks everyone
Josie
1) It should be NOT NULL in both. However error is because you attempted to insert a CustomerId that is not in Customer table.
2) You can simply alter the table and make it NOT NULL (error was not that).
Sample:
CREATE TABLE Customer
(
CustomerID INT NOT NULL
PRIMARY KEY ,
CustomerName VARCHAR(25)
);
CREATE TABLE Orders
(
OrderID INT NOT NULL
PRIMARY KEY ,
OrderDate DATE NOT NULL ,
CustomerID INT FOREIGN KEY REFERENCES Customer ( CustomerID )
);
INSERT [Customer] ( [CustomerID], [CustomerName] )
VALUES ( 1, 'Customer 1' ),
( 2, 'Customer 2' ),
( 3, 'Customer 3' ),
( 4, 'Customer 4' ),
( 5, 'Customer 5' ),
( 6, 'Customer 6' );
INSERT [Orders] ( [OrderID], [OrderDate], [CustomerID] )
VALUES
( 1, GETDATE(), 1 ),
( 2, GETDATE(), 2 ),
( 3, GETDATE(), 3 ),
( 4, GETDATE(), 4 ),
( 5, GETDATE(), 5 ),
( 6, GETDATE(), 6 );
INSERT [Orders] ( [OrderID], [OrderDate], [CustomerID] )
VALUES ( 7, GETDATE(), 7 );
Last one would error, because Customer with CustomerID 7 doesn't exist.
Update: I later saw your sample insert. You can find the offending ID like this:
DECLARE #ids TABLE ( id INT );
INSERT #ids ( [id] )
VALUES ( 1 ),
( 8 ),
( 15 ),
( 5 ),
( 3 ),
( 2 ),
( 11 ),
( 12 ),
( 4 ),
( 1 );
SELECT *
FROM #ids AS [i]
WHERE id NOT IN ( SELECT CustomerID
FROM [Customer] AS [c] );
1) Has the insert values failed because my CustomerID column in
Customer table in NOT NULL and I in error made CustomerID column NULL
in Order.
No. The error is not related to allowing NULL in the Order table. A NULL value will be allowed and not checked for referential integrity.
The foreign key violation error means you are attempting to insert a non-NULL CustomerID value into the Order table that does not exist in Customer. If you are certain the CustomerID values exist, perhaps the column mapping is wrong. Try specifying an explicit column list on the INSERT statement.
This error happens whern you are trying to insert a value in foreign key column, which this value does not exists in it's parent table. for example you are trying to insert value X to CustomerId in Order table, which this value does not exists in Customer table. This error occurred because we need to have a good strategy for Referential Integrity. So the only you need to do, is to check your new values(which you are going to insert them into table) to find out that is there any value compromising this rule or not.
However if you want to get an answer for your second question, you can try the below script:
create table t1
(
Id int primary key,
Name varchar(50) null
)
create table t2
(
Id int,
FK int null foreign key references t1(id)
)
go
alter table t2
alter column FK int not null
The identity column is set up once when you create the table.
The id assigned by an identity column start by the seed and are never reused.
So if you find that your customer ids starts from 6, it means that in the past you have added 5 customers and removed it.
If, for any reason, you want to use fixed Id don't use identity. In that case you take full responsability to set a unique value.
I suggest to never rely on fixed ids, if you must add orders from a script use the CustomerName (if unique), or any natural unique key.
You could use a script like this
DECLARE #newOrders TABLE (OrderID INT, CustomerName VARCHAR(25), OrderDate DATE);
INSERT INTO #newOrders (OrderID, CustomerName, OrderDate) VALUES
(1001, 'some-username', '2008-10-21'),
(1002, 'another-username', '2008-10-21');
INSERT INTO Orders(OrderID, CustomerId, OrderDate)
SELECT
o.OrderID,
c.CustomerID,
o.OrderDate
FROM #newOrders o
JOIN Customer c
ON c.CustomerName = o.CustomerName;
In this way you insert the correct CustomerID.
Note that in very rare cases (think twice to use it) you could insert values in identity colums using SET IDENTITY_INSERT statement.
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