Ale*_*lee 4 trigger sql-server sql-server-2014
所以我需要创建一个触发器,为每个案例创建一个新的证据编号。例如,案例 #1 可以有证据 # 的 1、2、3、4 等等。案例 #2 也可以有证据 # 的 1、2、3 等。
所以我有一个“Case”表(CaseID 为 PK)和一个“Evidence”表(EvidenceNum 为 PK,CaseID 为 FK 和相关属性)
因此,每次我搜索特定的 caseID 时,我都希望填充一个新的“EvidenceID”列。例如证据项目#1、#2 等。所以这些数字可以在每种情况下重复。因此,为什么这不是主键。EvidenceNum 是主键,但最终用户不会看到它。有什么帮助吗??
我会使用存储过程而不是触发器来实现这一点。使用单独的密钥表来存储每个案例的最后使用的证据编号。
我模拟了一个最小可行的完整示例。
如果对象已经存在,则从 tempdb 中删除它们,以便我们可以根据需要修改代码。
USE tempdb;
IF OBJECT_ID(N'dbo.AddCase', N'P') IS NOT NULL
DROP PROCEDURE dbo.AddCase;
IF OBJECT_ID(N'dbo.AddEvidence', N'P') IS NOT NULL
DROP PROCEDURE dbo.AddEvidence;
IF OBJECT_ID(N'dbo.EvidenceKeys', N'U') IS NOT NULL
DROP TABLE dbo.EvidenceKeys;
IF OBJECT_ID(N'dbo.Evidence', N'U') IS NOT NULL
DROP TABLE dbo.Evidence;
IF OBJECT_ID(N'dbo.Cases', N'U') IS NOT NULL
DROP TABLE dbo.Cases;
GO
Run Code Online (Sandbox Code Playgroud)
创建一个 Cases and Evidence 表,以及一个 EvidenceKey 表来存储递增的证据编号。
CREATE TABLE dbo.Cases
(
CaseID int NOT NULL IDENTITY(1,1)
CONSTRAINT PK_Cases
PRIMARY KEY CLUSTERED
) ON [PRIMARY];
CREATE TABLE dbo.Evidence
(
EvidenceID int NOT NULL IDENTITY(1,1)
CONSTRAINT PK_Evidence
PRIMARY KEY CLUSTERED
, CaseID int NOT NULL
CONSTRAINT FK_Evidence_CaseID
FOREIGN KEY
REFERENCES dbo.Cases(CaseID)
, EvidenceNum int NOT NULL
, CONSTRAINT UQ_EvidenceNum
UNIQUE (CaseID, EvidenceNum)
);
CREATE TABLE dbo.EvidenceKeys
(
CaseID int NOT NULL
CONSTRAINT PK_EvidenceKeys
PRIMARY KEY CLUSTERED
, MaxEvidenceNum int NOT NULL
);
GO
Run Code Online (Sandbox Code Playgroud)
创建用于添加新案例的过程。您需要为此添加参数,例如案例名称、日期等。
CREATE PROCEDURE dbo.AddCase
(
@CaseID int OUTPUT
)
AS
BEGIN
SET NOCOUNT ON;
DECLARE @Cases TABLE
(
CaseID int NOT NULL
);
INSERT INTO dbo.Cases
OUTPUT inserted.CaseID
INTO @Cases (CaseID)
DEFAULT VALUES;
SELECT @CaseID = CaseID
FROM @Cases;
END
GO
Run Code Online (Sandbox Code Playgroud)
创建一个程序来添加证据。同样,这只是一个概念验证,因此您需要添加参数来处理实际的证据项目详细信息。
CREATE PROCEDURE dbo.AddEvidence
(
@CaseID int
, @EvidenceID int OUTPUT
)
AS
BEGIN
SET NOCOUNT ON;
DECLARE @MaxEvidences TABLE
(
MaxEvidenceNum int NOT NULL
);
SET @EvidenceID = NULL;
UPDATE dbo.EvidenceKeys
SET MaxEvidenceNum += 1
OUTPUT inserted.MaxEvidenceNum
INTO @MaxEvidences(MaxEvidenceNum)
WHERE dbo.EvidenceKeys.CaseID = @CaseID;
SELECT @EvidenceID = MaxEvidenceNum
FROM @MaxEvidences;
IF @EvidenceID IS NULL
BEGIN
INSERT INTO dbo.EvidenceKeys (CaseID, MaxEvidenceNum)
VALUES (@CaseID, 1);
SET @EvidenceID = 1;
END
INSERT INTO dbo.Evidence (CaseID, EvidenceNum)
VALUES (@CaseID, @EvidenceID);
END;
GO
Run Code Online (Sandbox Code Playgroud)
插入一些示例数据:
DECLARE @CaseID int;
DECLARE @EvidenceID int;
EXEC dbo.AddCase @CaseID OUT;
EXEC dbo.AddEvidence @CaseID, @EvidenceID OUT;
SELECT @EvidenceID;
EXEC dbo.AddEvidence @CaseID, @EvidenceID OUT;
SELECT @EvidenceID;
EXEC dbo.AddEvidence @CaseID, @EvidenceID OUT;
SELECT @EvidenceID;
EXEC dbo.AddCase @CaseID OUT;
EXEC dbo.AddEvidence @CaseID, @EvidenceID OUT;
SELECT @EvidenceID;
EXEC dbo.AddEvidence @CaseID, @EvidenceID OUT;
SELECT @EvidenceID;
EXEC dbo.AddEvidence @CaseID, @EvidenceID OUT;
SELECT @EvidenceID;
Run Code Online (Sandbox Code Playgroud)
每次执行dbo.AddEvidence都会在单个原子操作dbo.EvidenceKeys中为给定的表增加值@CaseID,从而减少锁定成为问题的机会。
SELECT *
FROM dbo.Cases c
INNER JOIN dbo.Evidence e ON c.CaseID = e.CaseID
Run Code Online (Sandbox Code Playgroud)
select以上结果:
??????????????????????????????????????????????????? ? 案例ID ?证据 ID ? 案例ID ?证据编号 ? ??????????????????????????????????????????????????? ? 1 ? 1 ? 1 ? 1 ? ? 1 ? 2 ? 1 ? 2 ? ? 1 ? 3 ? 1 ? 3 ? ? 2 ? 4 ? 2 ? 1 ? ? 2 ? 5 ? 2 ? 2 ? ? 2 ? 6 ? 2 ? 3 ? ???????????????????????????????????????????????????
由于获取EvidenceKey任何给定的最大值CaseID并更新dbo.EvidenceKeys表发生在单个原子语句中,因此死锁的机会大大减少,无需锁定提示。
为了测试这个设计,我运行了以下代码。第一部分创建了 100 个“案例”,每个案例有 3 行“证据”。然后,在 3 个单独的会话中,第二段代码将 100,000 行插入Evidence表中,将每个证据行随机分配给随机选择的案例。没有发生死锁,这个过程在我旧的、缓慢的开发工作站上花费了不到 1 分钟的时间。
DECLARE @loop int = 0;
DECLARE @CaseID int;
DECLARE @EvidenceID int;
WHILE @loop < 100
BEGIN
EXEC dbo.AddCase @CaseID OUT;
EXEC dbo.AddEvidence @CaseID, @EvidenceID OUT;
EXEC dbo.AddEvidence @CaseID, @EvidenceID OUT;
EXEC dbo.AddEvidence @CaseID, @EvidenceID OUT;
SET @loop += 1;
END
GO
Run Code Online (Sandbox Code Playgroud)
这件作品应该在 3 个(或更多)单独的会话中运行:
DECLARE @loop int = 0;
DECLARE @CaseID int;
DECLARE @EvidenceID int;
WHILE @loop < 100000
BEGIN
SET @CaseID = (SELECT TOP (1) CaseID FROM dbo.Cases ORDER BY CRYPT_GEN_RANDOM(10));
EXEC dbo.AddEvidence @CaseID, @EvidenceID OUT;
SET @loop += 1;
END
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
443 次 |
| 最近记录: |