I have a table called 'Workspaces' where the columns 'AreaID' and 'SurfaceID' work as a composite primary key. The AreaID references to another table called 'Areas' which only has AreaID as the primary key. What I want to do now is to make the surfaceID recound from 1 on every new AreaID. Right now I'm using the following code for the tables 'Areas' and 'Workspaces':
--Table 'Areas'
CREATE TABLE Areas (
AreaID INT IDENTITY(1,1) PRIMARY KEY,
Areaname VARCHAR(60) UNIQUE NOT NULL
)
--Table 'Workspaces'
CREATE TABLE Workspaces (
AreaID INT
CONSTRAINT ck_a_areaid REFERENCES Areas(AreaID)
ON DELETE CASCADE
ON UPDATE NO ACTION,
SurfaceID INT IDENTITY(1,1)
CONSTRAINT ck_surfaceid CHECK (surfaceid > 0 AND surfaceid < 1001),
Description VARCHAR(300) NOT NULL,
CONSTRAINT ck_workspaces PRIMARY KEY (AreaID, SurfaceID)
)
When I use the code above I get a result like this when creating new workspaces in different areas:
AreaID SurfaceID
1 1
1 2
1 3
2 4
2 5
3 6
Etc...
But I want the SurfaceID to recount from 1 on every new areaID, so my desired result would be like this:
AreaID SurfaceID
1 1
1 2
1 3
2 1
2 2
3 1
Etc...
Does anyone know how this can be fixed?
You cannot easily do what you want. You can do it using triggers, but that is a rather ugly solution. You can get close to what you want by using a single identity primary key and then calculating the number you want on output:
Then when you query (or in a view):
Note: This does not check the maximum value of
surfaceId
. If you really need to implement this logic, then you will need to use triggers.Here is the solution that works with Multiple Rows.
Thanks to jFun for the work done for the single row insert, but the trigger is not really safe to use like that.
OK, Assuming this table:
In my case I needed "transactionNo" to always have the correct next value for each CONTRACT. Important for me in a legacy financial system is that there are no gaps in the transactionNo numbers.
So, we need the following trigger for to ensure referential integrity for the transactionNo column.
OK, I'll admit this is a pretty complex trigger with just about every trick in the book here, but this version should work all the way back to SQL 2005. The script utilises 2 CTE's, 2 cross applies and a Row_Num() over to work out the correct "next" TransactionNo for all of the rows in
Inserted
.It works using an
instead of insert
trigger and discards any incoming transactionNo and replaces them with the "NEXT" transactionNo.So, we can now run these updates:
We are updating single rows, and multiple rows with mixed contract numbers - but the correct TransactionNo overrides the passed in value and we get the expected result:
I am interested in people's opinions regarding concurrency. I am pretty certain the two CTEs will be treated as a single pass so, I am 99.99% certain the referential integrity will always be maintained.
I am agree with Mr. Linoff's answer but if you want to store it phisicaly, you can do it within an
insert trigger
:EDIT:*(as an example wanted for how to implement it)
In the question I have seen two table that's why I have wrote the code as above, but following is a sample for what I meant:
Sample table:
Trigger:
Insert:
Check the values:
Output:
IMPORTANT NOTICE: this trigger does not handle multi row insertion once and it is needed to insert single record once like the example. for handling multi record insertion it needs to change the body of and use row_number