This is a question for a SQL expert. I’m using SQL Server 2008 R2
I have two relevant tables: Labs and LabUsers.
Users are assigned to Labs with no repetitions of an entire groups of any order.
The goal is to insert a @userName (for the example @user = "Paul") into LabUsers fulfilling all the following limitations:
-
No more than
@maxUsersin a group (for the example@maxUsers=4) -
No duplicates of complete groups (full labs). The order of the users in a group is not significant. [edited]
-
If no existing Lab is allowed, create (
INSERT) a new lab, then insert the row for@user, given no exceeding@maxLabs(for the example@maxLabs=5). -
Very important: There are many concurrent same requests from the server in a split of a second, which may interfere one to the other. Therefore, as soon as the command begins to execute, no other queries are allowed to execute until the end of this command.
-
The query should return 0 in cases it cannot meet the above restrictions, and return the
LabIDof the inserted row. -
[EDITED] There are several Labs’ zones. The zones are independent. Each zone #labCount is bounded by the
@maxLabs. The@maxLabsis equal for all zones, therefore theTotal_maxLabs=@maxLabsx#zonesCount. For the example@zone=51(later on@zone=52, 53 etc.). (The same LabUsers can use zones with no limitations. Zones do not ‘know’ about each other) -
LabIDinLabUsersis a foreign key fromLabs.
The example:
Here is the Labs table:
LabID LabName LabZone
----- ------- -------
1 North 51
2 North East 51
3 South West 51
And the LabUsers is:
LabUserID LabUserName LabID
--------- ----------- -----
1 Diana 3
2 Julia 2
3 Paula 2
4 Romeo 1
5 Julia 3
6 Rose 2
7 Diana 1
8 Diana 2
9 Julia 1
10 Romeo 3
11 Paul 1
In the example the users are assigned like this:
LabID LabName LabZone LabUsers (ordered LTR a>z)
----- ------- ------- --------
1 North 51 Diana•Julia•Paul•Romeo
2 North East 51 Diana•Julia•Paula•Rose
3 South West 51 Diana•Julia•Romeo
- The insert should not be into
LabID=1or 2 because there are already 4 users in these labs. - The insert should not take place into
LabID=3due to creating duplicate withLabID=1.
Therefore, because the @maxLabs is not 3 (existing labs), it is necessary to insert a new row into Labs with the value LabZone=@zone=51.
The IDENTITY will set the LabID to 4 for the new row.
Now is the time to insert Paul into LabUsers with LabID just returned from inserting a new lab.
How to solve this problem?
What is the method to use in order to ensure that the command is executed as a whole with no interference?
The script to create the database is:
CREATE DATABASE [Allocation]
GO
USE [Allocation]
GO
CREATE TABLE [dbo].[LabUsers](
[LabUserID] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED ,
[LabUserName] [nvarchar](50) NOT NULL,
[LabID] [int] NOT NULL)
GO
SET IDENTITY_INSERT [dbo].[LabUsers] ON
INSERT [dbo].[LabUsers] ([LabUserID], [LabUserName], [LabID]) VALUES (1, N'Diana', 3)
INSERT [dbo].[LabUsers] ([LabUserID], [LabUserName], [LabID]) VALUES (2, N'Julia', 2)
INSERT [dbo].[LabUsers] ([LabUserID], [LabUserName], [LabID]) VALUES (3, N'Paula', 2)
INSERT [dbo].[LabUsers] ([LabUserID], [LabUserName], [LabID]) VALUES (4, N'Romeo', 1)
INSERT [dbo].[LabUsers] ([LabUserID], [LabUserName], [LabID]) VALUES (5, N'Julia', 3)
INSERT [dbo].[LabUsers] ([LabUserID], [LabUserName], [LabID]) VALUES (6, N'Rose', 2)
INSERT [dbo].[LabUsers] ([LabUserID], [LabUserName], [LabID]) VALUES (7, N'Diana', 1)
INSERT [dbo].[LabUsers] ([LabUserID], [LabUserName], [LabID]) VALUES (8, N'Diana', 2)
INSERT [dbo].[LabUsers] ([LabUserID], [LabUserName], [LabID]) VALUES (9, N'Julia', 1)
INSERT [dbo].[LabUsers] ([LabUserID], [LabUserName], [LabID]) VALUES (10, N'Romeo', 3)
INSERT [dbo].[LabUsers] ([LabUserID], [LabUserName], [LabID]) VALUES (11, N'Paul', 1)
SET IDENTITY_INSERT [dbo].[LabUsers] OFF
CREATE TABLE [dbo].[Labs](
[LabID] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED ,
[LabName] [nvarchar](50) NULL,
[LabZone] [int] NOT NULL)
GO
SET IDENTITY_INSERT [dbo].[Labs] ON
INSERT [dbo].[Labs] ([LabID], [LabName], [LabZone]) VALUES (1, N'North', 51)
INSERT [dbo].[Labs] ([LabID], [LabName], [LabZone]) VALUES (2, N'North East', 51)
INSERT [dbo].[Labs] ([LabID], [LabName], [LabZone]) VALUES (3, N'South West', 51)
SET IDENTITY_INSERT [dbo].[Labs] OFF
I piggy backed off dradu’s variables and implemented a similar but different solution. It does make the assumption that a new lab will be 1 more than the maximum available current lab. I also make the assumption a lab does NOT remove users.
The goal of this solution is to see what the end result of insertion of the user would look like and run checks on it to see which end result is valid. The logic is as follows:
Given the starting data from the original question and execution in the order below:
A tablockx within the transaction on LabUsers should prevent concurrent transactions from causing havoc.
Also, when debugging common table expressions, it helps to replace them with a temporary table so you can look at the results of each step along the way.