2014-11-06 41 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%,但都表現不佳。不幸的是,有時數據完整性會帶來成本。

相關問題