我有一张名为' Workspaces'列' AreaID'和' SurfaceID'作为复合主键工作。 AreaID引用另一个名为' Areas'只有AreaID作为主键。我现在要做的是使每个新的AreaID上的surfaceID从1重复。现在我使用以下代码表格'区域'和'工作区':
--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...
有谁知道如何解决这个问题?
答案 0 :(得分:4)
你不能轻易做你想做的事。你可以使用触发器来做,但这是一个相当丑陋的解决方案。您可以使用单个标识主键,然后计算输出所需的数字来接近您想要的内容:
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
的最大值。如果你真的需要实现这个逻辑,那么你需要使用触发器。
答案 1 :(得分:4)
我同意Mr. Linoff's answer但是如果你想将它存储起来,你可以在insert trigger
内完成:
Update Your_Table
set SurfaceID = ( select max(isnull(SurfaceID,0))+1 as max
from Workspaces t
where t.AreaID = INSERTED.AreaID )
编辑:* (作为如何实现它的示例)
在这个问题中,我看到了两个表,这就是为什么我编写了上面的代码,但以下是我的意思的样本:
样本表:
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
答案 2 :(得分:1)
以下是适用于多行的解决方案。
感谢jFun为单行插入所做的工作,但触发器并不是那么安全。
好的,假设这个表:
create table TestingTransactions (
id int identity,
transactionNo int null,
contract_id int not null,
Data1 varchar(10) null,
Data2 varchar(10) null
);
就我而言,我需要&#34; transactionNo&#34;每个合同始终具有正确的下一个值。在遗留金融系统中,对我来说重要的是,在交易号码中没有空白。
因此,我们需要以下触发器来确保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&#39; s, 2交叉应用和Row_Num()结束以计算出正确的&#34; next&#34;对于Inserted
中的所有行的TransactionNo。
它使用instead of insert
触发器工作并丢弃任何传入的transactionNo并用&#34; NEXT&#34;替换它们。 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%肯定会始终保持参照完整性。