2016-01-19 61 views
2

我正試圖跟蹤是否需要更新某些表。我有一個事件表,我想監視更改,並有另一個名爲DictionaryRefresh的表,它跟蹤對該表所做的更改。如果事件表被編輯,它的編輯時間將被保存並比DictionaryRefresh表的上一次刷新時間晚,因此表示需要刷新。另外,如果將新行添加到Events表中,那麼還需要在DictionaryRefresh表中關聯新條目 - 因此是LEFT JOIN。優化涉及IF EXISTS的SQL

這裏是表結構

CREATE TABLE [dbo].[DictionaryRefresh] 
(
    [LookupKey] [varchar](31) NOT NULL, 
    [LookupValue] [varchar](255) NOT NULL, 
    [RecordNumber] [int] NULL, 
    [RefreshTime] [datetime] NULL, 
    [EventKey] [varchar](31) NULL, 
    [MappedLookupKey] [varchar](31) NULL 
) ON [PRIMARY] 

的指標(按照用到dbengine優化顧問)

CREATE NONCLUSTERED INDEX [idx_DictionaryRefresh2146B4EB] 
ON [dbo].[Ifx_DictionaryRefresh] ([LookupKey] ASC) 

CREATE NONCLUSTERED INDEX [idx_DictionaryRefresh51EC6492] 
ON [dbo].[Ifx_DictionaryRefresh] ([MappedLookupKey] ASC, [RefreshTime] ASC, [RecordNumber] ASC, [EventKey] ASC) 

CREATE NONCLUSTERED INDEX [idx_DictionaryRefreshFCDAD7FA] 
ON [dbo].[Ifx_DictionaryRefresh] ([LookupValue] ASC) 

事件表如下:

CREATE TABLE [dbo].[Events](
    [RecordNumber] [int] NOT NULL, 
    ... 
    [EventKey] [varchar](31) NOT NULL, 
    ... 
    [EditTime] [datetime] NULL, 
    ... 
PRIMARY KEY CLUSTERED([RecordNumber] ASC) 

CREATE NONCLUSTERED INDEX [idxEvents299ADAC8] 
ON [dbo].[Events]([EditTime] ASC) 

CREATE NONCLUSTERED INDEX [idxEvents5B151A5E] 
ON [dbo].[Events]([EventKey] ASC) 

現在SQL我正在運行如下 - 它需要幾分鐘才能返回。如果我只執行子查詢,它會立即返回。

IF EXISTS (
    SELECT 1 
    FROM (
     SELECT 
      e.EventKey AS DictionaryKey 
      ,ISNULL(e.EditTime, '1 Jan 1900 01:00') AS EditTime 
      ,e.RecordNumber AS DictionaryRecordNumber 
     FROM Events e) d 
    LEFT JOIN DictionaryRefresh r ON r.RecordNumber = DictionaryRecordNumber 
     AND r.EventKey = DictionaryKey 
     AND r.MappedLookupKey = 'M18E2I501' 
    WHERE r.RefreshTime < d.EditTime 
     OR r.RecordNumber IS NULL) 
BEGIN 
    PRINT 'TRUE' 
END 

有大約130K行的DictionaryRefresh表,約8K排在Events

DictionaryRefresh表是空的或小的速度非常快,但下降變緩行的數DictionaryRefresh增加,特別是如果沒有符合標準。

這是執行計劃。

enter image description here

和突出的統計數據(該指數以尋求94%的成本 - 訪問行的數量實際上是在活動表中的行數的平方)...

enter image description here

我曾嘗試

IF EXISTS 

更換
IF (SELECT COUNT ...) <> 0 

以及

IF (SELECT TOP 1 1 ...) = 1 

,但似乎沒有任何更快。

我當然會很感激你的任何建議。

在此先感謝。

小號

+0

你有沒有嘗試過這樣的IF EXISTS(SELECT TOP 11 從事件Ë LEFT JOIN dictionaryrefresh [R R上。 RecordNumber = e.DictionaryRecordNumber AND r.EventKey = e.DictionaryKey AND r.MappedLookupKey = 'M18E2I501' WHERE r.RefreshTime StackUser

+0

請提供Events表的結構,就像Dictionaryrefresh – StackUser

+0

Thx那樣。不幸的是,它花費了大致相同的時間。 –

回答

1
CREATE NONCLUSTERED INDEX ix1 
    ON dbo.DictionaryRefresh (RecordNumber, EventKey, MappedLookupKey, RefreshTime) 

CREATE NONCLUSTERED INDEX ix2 
    ON dbo.[Events] (RecordNumber, EventKey, EditTime) 

IF EXISTS (
    SELECT TOP(1) 1 
    FROM dbo.[Events] e /*WITH(INDEX(ix2))*/ 
    LEFT JOIN dbo.DictionaryRefresh r /*WITH(INDEX(ix1))*/ ON r.RecordNumber = e.RecordNumber 
     AND r.EventKey = e.EventKey 
     AND r.MappedLookupKey = 'M18E2I501' 
    WHERE (r.RefreshTime < e.EditTime AND e.EditTime IS NOT NULL) 
     OR r.RecordNumber IS NULL 
) 
BEGIN 
    PRINT 'TRUE' 
END 
+0

Thx非常多。那麼你會在每次運行查詢時重新創建索引,或者只是檢查是否存在,如果不是則創建?你是否也能解釋你爲什麼這樣做? Thx再次 –

+0

不需要重新創建索引...我只是更改索引列的順序。這有幫助嗎? – Devart

+0

初步測試似乎表明它正在幫助...需要再測試一下..但thx vm再次...儘快回覆您 –

2

重新格式化您的查詢了一下我來到這所:

IF EXISTS (SELECT 1 
       FROM (SELECT e.EventKey AS DictionaryKey 
          ,ISNULL(e.EditTime, '1 Jan 1900 01:00') AS EditTime 
          ,e.RecordNumber AS DictionaryRecordNumber 
         FROM Events e) d 
       LEFT OUTER JOIN DictionaryRefresh r 
          ON r.RecordNumber = d.DictionaryRecordNumber 
          AND r.EventKey = d.DictionaryKey 
          AND r.MappedLookupKey = 'M18E2I501' 
      WHERE r.RefreshTime < d.EditTime 
       OR r.RecordNumber IS NULL) 
BEGIN 
    PRINT 'TRUE' 
END 

我沒有看到一個很好的理由對Events子查詢,所以相當於查詢就變成了這樣:

IF EXISTS (SELECT * 
       FROM Events e 
       LEFT OUTER JOIN DictionaryRefresh r 
          ON r.RecordNumber = e.RecordNumber 
          AND r.EventKey = e.EventKey 
          AND r.MappedLookupKey = 'M18E2I501' 
      WHERE r.RefreshTime < ISNULL(e.EditTime, '1 Jan 1900 01:00') 
       OR r.RecordNumber IS NULL 
     ) 
BEGIN 
    PRINT 'TRUE' 
END 

首先要注意的是,你在01使用條款。由於<運算符只在左側爲DEFINED且小於右側時返回true,這意味着每次r.RefreshTime爲NULL時,記錄都將被跳過。但是,下一行顯然提到您需要r.RecordNumber爲NULL的所有記錄,只有當值實際爲NULL時或LEFT OUTER JOIN找不到匹配項時纔會發生這種情況。所以這裏有一點衝突。您要麼執行INNER JOIN,要麼確實需要OUTER JOIN,但需要將r.RefreshTime < d.EditTime移至JOIN ON子句。

現在,看看你的表格定義,我認爲有一些改進的餘地。通過您在Events表上方給出的解釋是所有數據的「來源」。它會隨着時間的推移而被追加,然後偶爾運行一個掃描「新」和「更新」記錄的過程,做一些魔術,然後更新DictionorayRefresh(UPDATE現有記錄到新的RefreshTimeINSERT新的記錄,如

  • [DBO]。[事件]

    • [EDITTIME]作爲NUL-能。也許你認爲NULL作爲 '記錄被插入,但從來沒有更新'?在這種情況下,我會去定義因爲寧願使用'1 Jan 1900'作爲'魔術'值,並且使該字段不可空,它將使生活在以後變得更加容易。
  • [DBO]。[DictionaryRefresh]

    • 我不知道爲什麼你要RecordNumber爲NULL,能?它不應該總是被填滿,否則記錄有什麼用途?
    • 你也應該提上指向Events表領域的FOREIGN KEY,這樣,服務器知道的所有值從那裏來的
    • RefreshTime也被定義爲NULL-能,我再次想你會想,要總是被填寫。否則記錄是如何進入表格的?
    • 很確定你想要MappedLookupKey,但這並不重要。

不管怎樣,回到查詢。你想知道的是,如果Events中的記錄對於給定的MappedLookupKey和更新的EditTimeDictionaryRefresh中具有匹配的記錄,則該記錄比對應的RefreshTime具有匹配的記錄。或者,這根本沒有這樣的記錄(此MappedLookupKey

個人而言,我會寫這樣的:

IF EXISTS (SELECT * 
       FROM Events e 
      WHERE NOT EXISTS (SELECT * 
            FROM DictionaryRefresh r 
           WHERE r.RecordNumber = e.RecordNumber 
            AND r.EventKey = e.EventKey 
            AND r.MappedLookupKey = 'M18E2I501' 
            AND r.RefreshTime >= e.EditTime) 

     ) 
BEGIN 
    PRINT 'TRUE' 
END 

爲了使這項工作快,你需要以下指標:

CREATE INDEX idx1 ON DictionaryRefresh (MappedLookupKey, RecordNumber, EventKey, RefreshTime) 

Events表我覺得PK會做...

有趣的事實:你JOIN同時使用RecordNumberEventKey(可能沒有很好的理由,也是一個NULL字段)。但是,我們已經知道RecordNumber在[Events](它是PK!)中唯一標識了一條記錄,因此如果您加入的實際上應該是RecordNumber,除非在DictonaryRefresh中可以有不同的EventKey值?這對我來說是沒有意義的......實際上,這個字段似乎並不是真的需要在DictionaryRefresh中,因爲它可以在Events中找到。如果這個假設是正確的,你可以將它從表格中刪除,這樣就可以再次提高速度。的

位長閱讀,希望我沒有偷懶太多=)

+0

哇。謝謝。我需要一點時間來消化。雖然thx再次非常有幫助。 –