2014-11-06 48 views
0

如果我有事件像这样的表:如何在sql server 2008中插入数据?

 
    event_name  begin_date(pk) end_date(pk) 
    ------------------------------------------ 
    holiday  2014-11-01  2014-11-05 
    holiday  2014-11-10  2014-11-12 
    big sale  2014-11-18  2014-11-25 
    monthly sale 2014-11-28  2014-11-30 

如何防止插入数据如果插入数据的begin_dateend_date在任何事件的时期?

举例:
这些数据将不会被插入:

 
    holiday  2014-11-03  2014-11-08 

这个数据将被插入:

 
    holiday  2014-11-06  2014-11-09 

谁能帮我解决这个问题呢?

+0

了解SQL。阅读触发器。写一个停止插入。 – TomTom 2014-11-06 14:33:58

+1

汤姆是对的,你可以使用触发器。另外,你可以在表格上使用Check Constraint。检查此URL检查约束:http://msdn.microsoft.com/en-us/library/ms179491%28v=sql.105%29.aspx – 2014-11-06 14:38:57

+0

可能值得一读[存储没有重叠的时间间隔](http ://sqlblog.com/blogs/alexander_kuznetsov/archive/2009/03/08/storing-intervals-of-time-with-no-overlaps.aspx) – GarethD 2014-11-06 17:10:09

回答

0

最好的事情是避免触发和插入

IF NOT EXISTS (SELECT TOP 1 1 FROM MyTable WHERE @InsertedEndDate > begin_date AND @InsertedBeginDate < end_date) 
BEGIN 
    --do actual insert/work 
END 

它是一种简单的检查,发现第一重叠之前执行与是否存在检查。 Select TOP 1 1是避免实际获取数据的一种技巧,一旦它匹配与您实际尝试保存的日期范围重叠的行,就会立即返回

+0

这不是线程安全的方法。两个独立的线程可以同时插入行。另外,['TOP 1'在'EXISTS'中不是必需的](http://sqlinthewild.co.za/index.php/2011/04/05/to-top-or-not-to-top-an-存在/) – GarethD 2014-11-06 17:12:29

+0

@GarethD只有当你锁定的时候,这才是真正安全的。两行无法在同一时间锁定更新或插入强硬,所以一个将不可避免地失败。关于TOP的好处是存在的,我从来没有想过它 – Louis 2014-11-06 19:17:03

+0

可悲[这不是真的](http://weblogs.sqlteam.com/dang/archive/2007/10/28/Conditional-INSERTUPDATE-Race-Condition。 aspx),AFIK使用[MERGE WITH(HOLDLOCK)](http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx)是最好的(和可能只有)的方式来确保不符合比赛条件。 – GarethD 2014-11-06 21:08:03

0

触发器应该是您的最后手段。如果您的应用程序使用存储过程,那么最好是在那里进行验证。或者你可以使用check constraint。这是你需要使用,从我的理解你的问题的条件:

SELECT * 
FROM Table 
WHERE @begin_date BETWEEN begin_date AND end_date 
OR @end_date BETWEEN begin_date AND end_date 
OR @begin_date < begin_date AND @end_date > end_date 

如果该查询返回任何行,那些@begin_date@end_date值should't插入。

0

我一直认为,如果有什么东西可以在数据库中受到约束,应该是。您永远不知道哪个开发人员会禁用触发器,或绕过应用程序代码并直接运行插入,因此尽管触发器和业务逻辑良好,但这并不是很好的证明。

我会做的第一件事是限制BEGIN_DATE是END_DATE前:

CREATE TABLE dbo.T 
(
    ID INT IDENTITY(1, 1) NOT NULL, 
    Event_name VARCHAR(50) NOT NULL, 
    begin_date DATE NOT NULL, 
    end_date DATE NOT NULL 
); 
ALTER TABLE dbo.T ADD CONSTRAINT CHK_T_ValidDates CHECK (Begin_date <= end_date); 

然后(如果你不已经有一个),你可以创建一个日历表(which are incredibly useful anyway):

CREATE TABLE dbo.Calendar 
(
    Date DATE NOT NULL 
); 
CREATE UNIQUE CLUSTERED INDEX UQ_Calendar_Date ON dbo.Calendar (Date); 
GO 
INSERT dbo.Calendar (Date) 
SELECT TOP (7305) DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, '20000101') 
FROM sys.all_objects a, sys.all_objects; 
GO 

最后,你可以创建一个索引视图,以确保没有日期在表中重复:

CREATE VIEW dbo.TCheck 
WITH SCHEMABINDING 
AS 
    SELECT c.Date 
    FROM dbo.T 
      INNER JOIN dbo.Calendar AS c 
       ON c.Date >= t.begin_date 
       AND c.Date <= t.end_date; 
GO 
CREATE UNIQUE CLUSTERED INDEX UQ_TCheck_ID ON dbo.TCheck (Date); 

在我运行的测试中(与触发器相比),索引视图比触发器执行效果好了约50%,但都表现不佳。不幸的是,有时数据完整性会带来成本。

相关问题