复合主键自动递增
auto increment on composite primary key
我有一个名为 'Workspaces' 的 table,其中列 'AreaID' 和 'SurfaceID' 作为复合主键。 AreaID 引用另一个名为 'Areas' 的 table,它只有 AreaID 作为主键。我现在要做的是让每个新的 AreaID 上的 surfaceID 从 1 重新排列。现在我正在为 tables 'Areas' 和 '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)
)
当我使用上面的代码时,在不同区域创建新工作区时得到这样的结果:
AreaID SurfaceID
1 1
1 2
1 3
2 4
2 5
3 6
Etc...
但是我希望 SurfaceID 在每个新的 areaID 上从 1 重新计数,所以我想要的结果是这样的:
AreaID SurfaceID
1 1
1 2
1 3
2 1
2 2
3 1
Etc...
有人知道如何解决这个问题吗?
你不能轻易为所欲为。您 可以 使用触发器来完成它,但这是一个相当丑陋的解决方案。您可以通过使用单个身份主键来接近您想要的内容,然后计算您想要的输出数字:
CREATE TABLE Workspaces (
WorkspacesId int not null identity(1, 1) primary key,
AreaID INT,
Description VARCHAR(300) NOT NULL,
CONSTRAINT ck_a_areaid REFERENCES Areas(AreaID) ON DELETE CASCADE ON UPDATE NO ACTION,
);
然后当你查询(或在视图中)时:
select w.*, row_number() over (partition by areaId
order by WorkspaceId) as SurfaceId
from Workspaces
注意:这不会检查 surfaceId
的最大值。如果你真的需要实现这个逻辑,那么你将需要使用触发器。
我同意 但如果你想物理存储它,你可以在 insert trigger
:
内完成
Update Your_Table
set SurfaceID = ( select max(isnull(SurfaceID,0))+1 as max
from Workspaces t
where t.AreaID = INSERTED.AreaID )
编辑:*(作为实现它的示例)
在问题中我看到了两个 table 这就是为什么我写了上面的代码,但下面是我的意思的示例:
样本table:
CREATE TABLE testTbl
(
AreaID INT,
SurfaceID INT, --we want this to be auto increment per specific AreaID
Dsc VARCHAR(60)NOT NULL
)
触发器:
CREATE TRIGGER TRG
ON testTbl
INSTEAD OF INSERT
AS
DECLARE @sid INT
DECLARE @iid INT
DECLARE @dsc VARCHAR(60)
SELECT @iid=AreaID FROM INSERTED
SELECT @dsc=DSC FROM INSERTED
--check if inserted AreaID exists in table -for setting SurfaceID
IF NOT EXISTS (SELECT * FROM testTbl WHERE AreaID=@iid)
SET @sid=1
ELSE
SET @sid=( SELECT MAX(T.SurfaceID)+1
FROM testTbl T
WHERE T.AreaID=@Iid
)
INSERT INTO testTbl (AreaID,SurfaceID,Dsc)
VALUES (@iid,@sid,@dsc)
插入:
INSERT INTO testTbl(AreaID,Dsc) VALUES (1,'V1');
INSERT INTO testTbl(AreaID,Dsc) VALUES (1,'V2');
INSERT INTO testTbl(AreaID,Dsc) VALUES (1,'V3');
INSERT INTO testTbl(AreaID,Dsc) VALUES (2,'V4');
INSERT INTO testTbl(AreaID,Dsc) VALUES (2,'V5');
INSERT INTO testTbl(AreaID,Dsc) VALUES (2,'V6');
INSERT INTO testTbl(AreaID,Dsc) VALUES (2,'V7');
INSERT INTO testTbl(AreaID,Dsc) VALUES (3,'V8');
INSERT INTO testTbl(AreaID,Dsc) VALUES (4,'V9');
INSERT INTO testTbl(AreaID,Dsc) VALUES (4,'V10');
INSERT INTO testTbl(AreaID,Dsc) VALUES (4,'V11');
INSERT INTO testTbl(AreaID,Dsc) VALUES (4,'V12');
检查值:
SELECT * FROM testTbl
输出:
AreaID SurfaceID Dsc
1 1 V1
1 2 V2
1 3 V3
2 1 V4
2 2 V5
2 3 V6
2 4 V7
3 1 V8
4 1 V9
4 2 V10
4 3 V11
4 4 V12
重要提示:此触发器不处理一次多行插入,需要像示例一样插入一次单条记录。为了处理多记录插入,它需要更改正文并使用 row_number
这是适用于多行的解决方案。
感谢 jFun 为单行插入所做的工作,但这样使用触发器并不安全。
好的,假设这个 table:
create table TestingTransactions (
id int identity,
transactionNo int null,
contract_id int not null,
Data1 varchar(10) null,
Data2 varchar(10) null
);
在我的例子中,我需要 "transactionNo" 始终为每个 CONTRACT 提供正确的下一个值。在遗留金融系统中对我来说重要的是交易号没有差距。
因此,我们需要以下触发器来确保 transactionNo 列的引用完整性。
CREATE TRIGGER dbo.Trigger_TransactionNo_Integrity
ON dbo.TestingTransactions
INSTEAD OF INSERT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Discard any incoming transactionNo's and ensure the correct one is used.
WITH trans
AS (SELECT F.*,
Row_number()
OVER (
ORDER BY contract_id) AS RowNum,
A.*
FROM inserted F
CROSS apply (SELECT Isnull(Max(transactionno), 0) AS
LastTransaction
FROM dbo.testingtransactions
WHERE contract_id = F.contract_id) A),
newtrans
AS (SELECT T.*,
NT.minrowforcontract,
( 1 + lasttransaction + ( rownum - NT.minrowforcontract ) ) AS
NewTransactionNo
FROM trans t
CROSS apply (SELECT Min(rownum) AS MinRowForContract
FROM trans
WHERE T.contract_id = contract_id) NT)
INSERT INTO dbo.testingtransactions
SELECT Isnull(newtransactionno, 1) AS TransactionNo,
contract_id,
data1,
data2
FROM newtrans
END
GO
好吧,我承认这是一个非常复杂的触发器,几乎包含本书中的所有技巧,但这个版本应该可以一直追溯到 SQL 2005 年。该脚本使用 2 个 CTE, 2 交叉应用和 Row_Num() 计算出 Inserted
中所有行的正确 "next" TransactionNo。
它使用 instead of insert
触发器工作并丢弃任何传入的 transactionNo 并将它们替换为 "NEXT" transactionNo。
所以,我们现在可以 运行 这些更新:
delete from dbo.TestingTransactions
insert into dbo.TestingTransactions (transactionNo, Contract_id, Data1)
values (7,213123,'Blah')
insert into dbo.TestingTransactions (transactionNo, Contract_id, Data2)
values (7,333333,'Blah Blah')
insert into dbo.TestingTransactions (transactionNo, Contract_id, Data1)
values (333,333333,'Blah Blah')
insert into dbo.TestingTransactions (transactionNo, Contract_id, Data2)
select 333 ,333333,'Blah Blah' UNION All
select 99999,44443,'Blah Blah' UNION All
select 22, 44443 ,'1' UNION All
select 29, 44443 ,'2' UNION All
select 1, 44443 ,'3'
select * from dbo.TestingTransactions
order by Contract_id,TransactionNo
我们正在更新单行和具有混合合同编号的多行 - 但正确的 TransactionNo 覆盖了传入的值,我们得到了预期的结果:
id transactionNo contract_id Data1 Data2
117 1 44443 NULL Blah Blah
118 2 44443 NULL 1
119 3 44443 NULL 2
120 4 44443 NULL 3
114 1 213123 Blah NULL
115 1 333333 NULL Blah Blah
116 2 333333 Blah Blah NULL
121 3 333333 NULL Blah Blah
我对人们对并发的看法很感兴趣。我非常确定这两个 CTE 将被视为一次通过,所以我 99.99% 确定参照完整性将始终保持。
我有一个名为 'Workspaces' 的 table,其中列 'AreaID' 和 'SurfaceID' 作为复合主键。 AreaID 引用另一个名为 'Areas' 的 table,它只有 AreaID 作为主键。我现在要做的是让每个新的 AreaID 上的 surfaceID 从 1 重新排列。现在我正在为 tables 'Areas' 和 '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)
)
当我使用上面的代码时,在不同区域创建新工作区时得到这样的结果:
AreaID SurfaceID
1 1
1 2
1 3
2 4
2 5
3 6
Etc...
但是我希望 SurfaceID 在每个新的 areaID 上从 1 重新计数,所以我想要的结果是这样的:
AreaID SurfaceID
1 1
1 2
1 3
2 1
2 2
3 1
Etc...
有人知道如何解决这个问题吗?
你不能轻易为所欲为。您 可以 使用触发器来完成它,但这是一个相当丑陋的解决方案。您可以通过使用单个身份主键来接近您想要的内容,然后计算您想要的输出数字:
CREATE TABLE Workspaces (
WorkspacesId int not null identity(1, 1) primary key,
AreaID INT,
Description VARCHAR(300) NOT NULL,
CONSTRAINT ck_a_areaid REFERENCES Areas(AreaID) ON DELETE CASCADE ON UPDATE NO ACTION,
);
然后当你查询(或在视图中)时:
select w.*, row_number() over (partition by areaId
order by WorkspaceId) as SurfaceId
from Workspaces
注意:这不会检查 surfaceId
的最大值。如果你真的需要实现这个逻辑,那么你将需要使用触发器。
我同意 insert trigger
:
Update Your_Table
set SurfaceID = ( select max(isnull(SurfaceID,0))+1 as max
from Workspaces t
where t.AreaID = INSERTED.AreaID )
编辑:*(作为实现它的示例)
在问题中我看到了两个 table 这就是为什么我写了上面的代码,但下面是我的意思的示例:
样本table:
CREATE TABLE testTbl
(
AreaID INT,
SurfaceID INT, --we want this to be auto increment per specific AreaID
Dsc VARCHAR(60)NOT NULL
)
触发器:
CREATE TRIGGER TRG
ON testTbl
INSTEAD OF INSERT
AS
DECLARE @sid INT
DECLARE @iid INT
DECLARE @dsc VARCHAR(60)
SELECT @iid=AreaID FROM INSERTED
SELECT @dsc=DSC FROM INSERTED
--check if inserted AreaID exists in table -for setting SurfaceID
IF NOT EXISTS (SELECT * FROM testTbl WHERE AreaID=@iid)
SET @sid=1
ELSE
SET @sid=( SELECT MAX(T.SurfaceID)+1
FROM testTbl T
WHERE T.AreaID=@Iid
)
INSERT INTO testTbl (AreaID,SurfaceID,Dsc)
VALUES (@iid,@sid,@dsc)
插入:
INSERT INTO testTbl(AreaID,Dsc) VALUES (1,'V1');
INSERT INTO testTbl(AreaID,Dsc) VALUES (1,'V2');
INSERT INTO testTbl(AreaID,Dsc) VALUES (1,'V3');
INSERT INTO testTbl(AreaID,Dsc) VALUES (2,'V4');
INSERT INTO testTbl(AreaID,Dsc) VALUES (2,'V5');
INSERT INTO testTbl(AreaID,Dsc) VALUES (2,'V6');
INSERT INTO testTbl(AreaID,Dsc) VALUES (2,'V7');
INSERT INTO testTbl(AreaID,Dsc) VALUES (3,'V8');
INSERT INTO testTbl(AreaID,Dsc) VALUES (4,'V9');
INSERT INTO testTbl(AreaID,Dsc) VALUES (4,'V10');
INSERT INTO testTbl(AreaID,Dsc) VALUES (4,'V11');
INSERT INTO testTbl(AreaID,Dsc) VALUES (4,'V12');
检查值:
SELECT * FROM testTbl
输出:
AreaID SurfaceID Dsc
1 1 V1
1 2 V2
1 3 V3
2 1 V4
2 2 V5
2 3 V6
2 4 V7
3 1 V8
4 1 V9
4 2 V10
4 3 V11
4 4 V12
重要提示:此触发器不处理一次多行插入,需要像示例一样插入一次单条记录。为了处理多记录插入,它需要更改正文并使用 row_number
这是适用于多行的解决方案。
感谢 jFun 为单行插入所做的工作,但这样使用触发器并不安全。
好的,假设这个 table:
create table TestingTransactions (
id int identity,
transactionNo int null,
contract_id int not null,
Data1 varchar(10) null,
Data2 varchar(10) null
);
在我的例子中,我需要 "transactionNo" 始终为每个 CONTRACT 提供正确的下一个值。在遗留金融系统中对我来说重要的是交易号没有差距。
因此,我们需要以下触发器来确保 transactionNo 列的引用完整性。
CREATE TRIGGER dbo.Trigger_TransactionNo_Integrity
ON dbo.TestingTransactions
INSTEAD OF INSERT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Discard any incoming transactionNo's and ensure the correct one is used.
WITH trans
AS (SELECT F.*,
Row_number()
OVER (
ORDER BY contract_id) AS RowNum,
A.*
FROM inserted F
CROSS apply (SELECT Isnull(Max(transactionno), 0) AS
LastTransaction
FROM dbo.testingtransactions
WHERE contract_id = F.contract_id) A),
newtrans
AS (SELECT T.*,
NT.minrowforcontract,
( 1 + lasttransaction + ( rownum - NT.minrowforcontract ) ) AS
NewTransactionNo
FROM trans t
CROSS apply (SELECT Min(rownum) AS MinRowForContract
FROM trans
WHERE T.contract_id = contract_id) NT)
INSERT INTO dbo.testingtransactions
SELECT Isnull(newtransactionno, 1) AS TransactionNo,
contract_id,
data1,
data2
FROM newtrans
END
GO
好吧,我承认这是一个非常复杂的触发器,几乎包含本书中的所有技巧,但这个版本应该可以一直追溯到 SQL 2005 年。该脚本使用 2 个 CTE, 2 交叉应用和 Row_Num() 计算出 Inserted
中所有行的正确 "next" TransactionNo。
它使用 instead of insert
触发器工作并丢弃任何传入的 transactionNo 并将它们替换为 "NEXT" transactionNo。
所以,我们现在可以 运行 这些更新:
delete from dbo.TestingTransactions
insert into dbo.TestingTransactions (transactionNo, Contract_id, Data1)
values (7,213123,'Blah')
insert into dbo.TestingTransactions (transactionNo, Contract_id, Data2)
values (7,333333,'Blah Blah')
insert into dbo.TestingTransactions (transactionNo, Contract_id, Data1)
values (333,333333,'Blah Blah')
insert into dbo.TestingTransactions (transactionNo, Contract_id, Data2)
select 333 ,333333,'Blah Blah' UNION All
select 99999,44443,'Blah Blah' UNION All
select 22, 44443 ,'1' UNION All
select 29, 44443 ,'2' UNION All
select 1, 44443 ,'3'
select * from dbo.TestingTransactions
order by Contract_id,TransactionNo
我们正在更新单行和具有混合合同编号的多行 - 但正确的 TransactionNo 覆盖了传入的值,我们得到了预期的结果:
id transactionNo contract_id Data1 Data2
117 1 44443 NULL Blah Blah
118 2 44443 NULL 1
119 3 44443 NULL 2
120 4 44443 NULL 3
114 1 213123 Blah NULL
115 1 333333 NULL Blah Blah
116 2 333333 Blah Blah NULL
121 3 333333 NULL Blah Blah
我对人们对并发的看法很感兴趣。我非常确定这两个 CTE 将被视为一次通过,所以我 99.99% 确定参照完整性将始终保持。