2013-12-10 88 views
0

有一個簡單的表,用於存儲IIS日誌。查詢優化大約需要一分鐘的時間執行

表架構如下:

CREATE TABLE [dbo].[TBL_iisLog](
    [cdate] [varchar](50) NULL, 
    [ctime] [varchar](50) NULL, 
    [serverip] [varchar](50) NULL, 
    [uri] [varchar](255) NULL, 
    [port] [varchar](50) NULL, 
    [username] [varchar](50) NULL, 
    [clientip] [varchar](50) NULL, 
    [useragent] [nvarchar](max) NULL 
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] 

請注意,我已經添加在以下領域這三個指標:

  • username
  • clientip
  • 的(cdatectime組合)

的表,下面的查詢大約需要50 - 60秒,執行:

SELECT TOP 10 
    username Usename, 

    (SELECT COUNT(DISTINCT SUBSTRING(useragent, PATINDEX('%SIG:+%',useragent) + 5, 36)) 
     FROM tbl_iislog AS SoftwareSignatureCountTempTable 
     WHERE (PATINDEX('%SIG:+%',useragent) > 0) 
      AND SoftwareSignatureCountTempTable.username = MainTBL.username 
    ) AS SoftwareSignatureCount 

    , 
    (SELECT COUNT(DISTINCT 
     LEFT(useragent, IIF((PATINDEX('%VDB%',useragent) -1) > 0, PATINDEX('%VDB%',useragent) -1, 0)) 
     + 
     RIGHT(useragent, IIF((PATINDEX('%BPC%',useragent) -1) > 0, PATINDEX('%BPC%',useragent) -1, 0)) 
     ) 
     FROM TBL_iislog UseragentTempTable 
     WHERE UseragentTempTable.username = MainTBL.username 
    ) AS UserAgentCount 

    , 
    (SELECT COUNT(DISTINCT clientip) 
     FROM tbl_iislog AS IPTempTable 
     WHERE IPTempTable.username = MainTBL.username 
    ) AS IPCount 

    , 
    (SELECT COUNT(clientip) 
     FROM tbl_iislog ConnectionsTempTable 
     WHERE ConnectionsTempTable.uri = '/version_checker.ver' 
     AND ConnectionsTempTable.username = MainTBL.username 
    ) AS Connections 

    FROM TBL_iisLog AS MainTBL 
    WHERE (username LIKE 'softgsg-%') OR (username LIKE 'sg-%') 
    GROUP BY username HAVING COUNT(clientip) > 0 
    ORDER BY SoftwareSignatureCount DESC, Connections DESC 

我會感謝任何意見幫我優化我的查詢。

+1

你可以發佈執行計劃嗎? –

+2

我認爲你可以大大簡化這一點。看起來你所有的計數都來自同一個表('TBL_iislog'),但你多次旋轉。嘗試並用case語句替換所有那些時髦的子查詢,這樣你只能擊中表格一次。 – Andrew

+0

我正在看看可以做些什麼......你是否爲clientip和username創建了單獨的索引,並且它們是非聚簇索引嗎?你能提供你創建的索引腳本嗎? – PCG

回答

2

首先,其他人評論和真實......查詢是一團糟,並且有許多字段級別的選擇語句可以爲每個記錄執行每個查詢的KILL性能,而每一個記錄都是針對您運行的每一個列。它們全部基於當前用戶的身份,恰好是某些標準的IIF()。我已經簡化了,可能需要稍微調整一下,但應該能夠幫助你。無論特定列的

首先,您的具體clientIP作爲HAVING子句並不重要,該列的基礎上的數量,但會是最好的只是*指示記錄存在。此外,在這個相同的查詢級別,通過執行SUM(IIF())可以處理您的版本檢查器「連接數」。

爲您的簽名詞條我做從相同的運行這兩個方面。首先,我正在通過MAX(IIF())標誌,以查看是否有任何的記錄不具有「%SIG:+%」的參考,然後還得到一個COUNT(即簽字發現的不同)。這樣,COUNT 扣除任何非簽名(這將全部被視爲空格「」所以最大1項的扣除無論有多少非「SIG」項目將如何給最終的實際數量。

我也不知道爲什麼你有一個IP> 0的HAVING COUNT ...從IIS日誌文件中,一切都應該有一個客戶端IP,所以我相信這是無關的,因此刪除。

我也覺得我狠如果沒有找到'%VDB%'或'%BPC%'條目,只需使用空字符串,否則獲取每個字符串的子字符串上下文

因此,通過所有聚合的表格ns完成一次應該幫助。現在,索引。由於您使用的是像你的where子句中,但好東西是你不喜歡的領先「%」,否則表明您正在查找的字符串「softgsg-」或「SG-」隨處字符串中,你是在可以通過索引進行優化的字符串的LEADING部分中尋找它。

話雖這麼說,我將會對

(用戶名,clientip)

表一個複合索引由於您的列處理255甚至MAX列寬的文本,我不想殺死該指數只是爲了讓它成爲「覆蓋」指數。

select 
     PQ.* 
    from 
     (SELECT 
       L.username, 
       COUNT(*) TotalRecs, 
       COUNT(DISTINCT clientip) DistinctIPs, 
       SUM(IIF(L.uri = '/version_checker.ver', 1, 0)) as Connections, 
       MAX(IIF(PATINDEX('%SIG:+%', L.useragent) = 0, 1, 0)) as NonSigEntries, 
       COUNT(DISTINCT IIF(PATINDEX('%SIG:+%', L.useragent) = 0, ' ', 
            SUBSTRING(L.useragent, PATINDEX('%SIG:+%', L.useragent) + 5, 36))) SoftwareSignatureCount, 
       COUNT(DISTINCT 
         IIF(PATINDEX('%VDB%', L.useragent) = 0, '', LEFT(L.useragent, PATINDEX('%VDB%', L.useragent) -1)) 
         + IIF(PATINDEX('%BPC%', L.useragent) = 0, '', RIGHT(L.useragent, PATINDEX('%BPC%', L.useragent) -1)) 
        ) AS UserAgentCount 
      FROM 
       tbl_iislog L 
      WHERE 
       L.username LIKE 'softgsg-%' 
       OR L.username LIKE 'sg-%' 
      GROUP BY 
       L.username) PQ 
    ORDER BY 
     PQ.SoftwareSignatureCount - PQ.NonSigEntries DESC, 
     PQ.Connections DESC 

查詢運行,因爲我對一個空表結構進行測試,通過查詢提供。由於順序,我預先包裝了查詢(別名PQ for PreQuery),所以我會得到軟件簽名計數和非簽名條目標記的最終值,然後連接,所以我不必重新複製將相同的COUNT(複雜語句)放入字段列表中並按順序排列...某些引擎允許您使用列結果名稱別名與整個表達式。