如何强制SQL Server每3分钟只允许插入一条记录
我正在使用SQL Server,我需要一种方法来强制我的数据库在3分钟内只允许插入一条特定记录 例如,如果我插入一条电话号码为305333333的记录,我需要一种方法来强制在3分钟后再输入相同的电话号码如何强制SQL Server每3分钟只允许插入一条记录,sql,sql-server,Sql,Sql Server,我正在使用SQL Server,我需要一种方法来强制我的数据库在3分钟内只允许插入一条特定记录 例如,如果我插入一条电话号码为305333333的记录,我需要一种方法来强制在3分钟后再输入相同的电话号码 我需要在数据库级别执行此操作。任何帮助都将不胜感激。插入时在记录上存储时间戳 每次插入时,检查同一电话号码的当前时间与最后时间戳是否至少为3分钟 如果要通过存储过程进行插入,请在此处执行检查。否则,您将不得不在触发器中执行此操作不完全是您要求的,但它应该是一个足够好的近似值。 这种方法的优点是易
我需要在数据库级别执行此操作。任何帮助都将不胜感激。插入时在记录上存储时间戳 每次插入时,检查同一电话号码的当前时间与最后时间戳是否至少为3分钟
如果要通过存储过程进行插入,请在此处执行检查。否则,您将不得不在触发器中执行此操作不完全是您要求的,但它应该是一个足够好的近似值。 这种方法的优点是易于实现且高效。不需要触发器 将
int
列添加到将使用以下表达式自动填充默认值的表中:
datediff(minute,'2001-01-01',getdate())/(3)
此表达式计算自某个参考日期(2001-01-01)以来经过的分钟数(除以3)
然后在PhoneNumber
和该PhoneTimestamp
列上添加唯一索引
CREATE TABLE [dbo].[TestTable](
[ID] [int] IDENTITY(1,1) NOT NULL,
[PhoneNumber] [varchar](50) NOT NULL,
[PhoneTimestamp] [int] NOT NULL CONSTRAINT [DF_TestTable_PhoneTimestamp]
DEFAULT (datediff(minute,'2001-01-01',getdate())/(3)),
CONSTRAINT [PK_TestTable] PRIMARY KEY CLUSTERED
(
[ID] ASC
))
GO
CREATE UNIQUE NONCLUSTERED INDEX [IX_PhoneNumber] ON [dbo].[TestTable]
(
[PhoneNumber] ASC,
[PhoneTimestamp] ASC
)
GO
在表中插入值而不显式设置PhoneTimestamp
,让默认表达式工作:
INSERT INTO [dbo].[TestTable] ([PhoneNumber]) VALUES
('124');
第一次插入将正常工作,但如果您尝试在相同的3分钟“插槽”内再次运行,则会失败,并显示以下消息:
Msg 2601, Level 14, State 1, Line 1
Cannot insert duplicate key row in object 'dbo.TestTable' with unique index
'IX_PhoneNumber'. The duplicate key value is (124, 2649888).
The statement has been terminated.
这是一个近似值,因为您可以插入相隔不到3分钟的两行。如果在
10:02:58
插入第一行,在10:03:02
插入第二行,则将接受该行。但是,如果您在10:03:02
插入一行,那么只有在10:06:00
之后才能接受下一行,如果在最后三分钟内没有记录,只需插入即可
DECLARE @ThreeMinutesAgo datetime2 =
DATEDIFF(minute, -3, getdate())
IF NOT EXISTS (
SELECT * FROM PhoneRecords
WHERE PhoneNumber = @PhoneNumber
AND TimeStamp > @ThreeMinutesAgo)
INSERT INTO PhoneRecords
(PhoneNumber, TimeStamp, MoreStuff)
VALUES
(@PhoneNumber, getdate(), @MoreStuff)
编辑以回应弗拉基米尔的以下评论: 我将使用第二个表(以PhoneNumber为键)来跟踪最近的记录
CREATE TABLE PhoneNumbers (
PhoneNumber varchar(50) NOT NULL PRIMARY KEY,
LastTimeStamp datetime2
)
PhoneNumber将是PhoneRecords表中的外键。要添加新记录,请执行以下操作:
DECLARE @Timestamp datetime2 = getdate()
DECLARE @ThreeMinutesAgo datetime2 =
DATEDIFF(minute, -3, @Timestamp)
UPDATE PhoneNumbers
SET LastTimeStamp = @TimeStamp
WHERE PhoneNumber = @PhoneNumber AND LastTimeStamp < @ThreeMinutesAgo
你想把它放在交易中,还要检查是否需要在第一次在PhoneNumber表中插入电话号码。您不能限制插入的方式吗?首先想到的是一个insert而不是insert触发器。为什么需要在数据库级别?在数据库抽象级别很容易做到。我想使用存储使用where子句来处理插入,并检查where子句中的条件,但这不会100%准确,因为如果两个插入请求的执行时间相差微秒,会发生什么情况,可能在我检查第二个插入请求时,第一个插入请求尚未完成,那么,也许两个都完成了插入。当多次尝试同时插入相同的值时,如何使其100%可靠地工作?我指的是OP在对问题的评论中所写的担忧。在上面添加了一个编辑来解决这个问题@VladimirBaranov@VladimirBaranov:我喜欢你的方法,结果非常简洁:TRY--do insert--CATCH--do nothing。我的问题是,我尽量不对预期条件使用
CATCH
。是否有任何性能方面的考虑?如果这种情况发生的并不罕见,但可能大部分都失败了,那该怎么办?我想知道与先检查一个有效的外键相比,尝试插入一个坏的外键是否会有性能损失。这可能取决于与有效记录相比,您尝试添加不需要的记录的频率。我的答案是使用内置数据库机制声明所需的约束,而不是性能。你的方法也是有效的。我最近看到了亚伦·伯特兰的帖子,看看吧。不过,您应该为您的环境进行自己的测试。
IF @@ROWCOUNT = 1
INSERT INTO PhoneRecords
(PhoneNumber, TimeStamp, MoreStuff)
VALUES
(@PhoneNumber, @Timestamp, @MoreStuff)