2010-05-17 71 views
1

好吧......我不明白爲什麼這個查詢花費這麼長時間(MSSQL Server 2005中):
[典型輸出3K行,5.5分鐘執行時間]SQL服務器長的查詢

SELECT dbo.Point.PointDriverID, dbo.Point.AssetID, dbo.Point.PointID, dbo.Point.PointTypeID, dbo.Point.PointName, dbo.Point.ForeignID, dbo.Pointtype.TrendInterval, coalesce(dbo.Point.trendpts,5) AS TrendPts, LastTimeStamp = PointDTTM, LastValue=PointValue, Timezone 
FROM dbo.Point 
    LEFT JOIN dbo.PointType ON dbo.PointType.PointTypeID = dbo.Point.PointTypeID 
    LEFT JOIN dbo.PointData ON dbo.Point.PointID = dbo.PointData.PointID 
     AND PointDTTM = (SELECT Max(PointDTTM) FROM dbo.PointData WHERE PointData.PointID = Point.PointID) 
    LEFT JOIN dbo.SiteAsset ON dbo.SiteAsset.AssetID = dbo.Point.AssetID 
    LEFT JOIN dbo.Site ON dbo.Site.SiteID = dbo.SiteAsset.SiteID 
WHERE onlinetrended =1 and WantTrend=1 

PointData是biggun,但我認爲它的定義應該讓我拿起我想很容易就夠了:

CREATE TABLE [dbo].[PointData](
    [PointID] [int] NOT NULL, 
    [PointDTTM] [datetime] NOT NULL, 
    [PointValue] [real] NULL, 
    [DataQuality] [tinyint] NULL, 
CONSTRAINT [PK_PointData_1] PRIMARY KEY CLUSTERED 
(
    [PointID] ASC, 
    [PointDTTM] ASC 
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 

GO 

CREATE NONCLUSTERED INDEX [IX_PointDataDesc] ON [dbo].[PointData] 
(
    [PointID] ASC, 
    [PointDTTM] DESC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
GO 

PointData是550M行,點(點名的源)僅28K行。 我試圖做一個索引視圖,但我無法弄清楚如何以兼容的方式(最大值,沒有子查詢,沒有CTE)得到最後的時間戳/值。

它每小時運行兩次,運行後我將更多的數據放入我選擇的3K PointID中。我想直接在Point中創建LastTime/LastValue表,但這似乎是錯誤的方法。

我錯過了什麼,或者我應該重建一些東西? (我也是DBA,但我知道約A'ing一個DB非常少!)

+0

你可以在你的問題中包含你的查詢執行計劃嗎?這將很快指出您的查詢的哪個方面導致放緩。有許多連接,以及兩個過濾器,任何可能是罪魁禍首 - 一個執行計劃肯定會說。 – SqlRyan 2010-05-17 22:54:20

+0

除了rwmnau說的,我注意到你所有的連接都是左連接。 Point.PointTypeId是否可以爲空?那麼Point.AssetId呢? SiteAsset.SiteId怎麼樣? – Thomas 2010-05-17 22:59:24

+0

我該如何將執行計劃吐出文本? 我想要連接是左連接,所以我可以在沒有完全定義的點上運行它(例如,我沒有將它們分配給資產,因此Point.AssetID一段時間爲空)。 – user343508 2010-05-17 23:41:07

回答

1

對於初學者來說,嘗試擺脫相關子查詢的。我還用表別名重寫了它,使其更容易閱讀(並且輸入更少!)。

嘗試這樣:

SELECT p.PointDriverID, p.AssetID, p.PointID, 
    p.PointTypeID, p.PointName, p.ForeignID, 
    pt.TrendInterval, coalesce(p.trendpts,5) AS TrendPts, 
    LastTimeStamp = PointDTTM, LastValue=PointValue, Timezone 
FROM dbo.Point p 
    LEFT JOIN dbo.PointType pt ON pt.PointTypeID = p.PointTypeID 
    LEFT JOIN dbo.PointData pd ON p.PointID = pd.PointID 
    INNER JOIN (
     SELECT PointID, Max(PointDTTM) as MaxPointDTTM 
     FROM dbo.PointData 
     group by PointID 
    ) pdm on pd.PointID = pdm.PointID and pd.PointDTTM = pdm.MaxPointDTTM 
    LEFT JOIN dbo.SiteAsset sa ON sa.AssetID = p.AssetID 
    LEFT JOIN dbo.Site ON s.SiteID = sa.SiteID 
WHERE onlinetrended =1 and WantTrend=1 
+1

+1,但我沒有投票,對不起。 – 2010-05-17 23:00:23

+0

需要稍長些時間(6分鐘)。
真正的罪魁禍首是: SELECT PointID,Max(PointDTTM)as MaxPointDTTM FROM dbo.PointData group by PointID 需要5分鐘才能運行。
我想如果我將它們相關,它只會在「感興趣的3K」行上運行並且速度會更快。如果我可以爲每個PointID保存「Max Timestamp」,我會處於一個很好的位置,但Index Views不喜歡Max()聚合:( – user343508 2010-05-17 23:25:04

+0

@thormj:嘗試更新您的統計信息 – RedFilter 2010-05-18 12:11:09

0

我不是一個SQLServer的人,但我知道查詢表與子查詢同一個表where子句是個壞消息,尤其是在這樣的大的記錄。從概念上講,您正在通過該次選表再次查看數據的每一行。如果我記得對的話,SQLServer可以讓你在內存中存儲變量,如果沒有,那很好,你可以用一個表來做到這一點。

創建一個服務器變量(或一個表,它只需要一列,只有一行)。現在創建一個觸發器,以便在檢查變量(或記錄)的PointData中插入或更新記錄。如果插入或更新記錄的日期時間大於變量,則更新變量。現在,您可以在查詢中使用該變量或加入該表。應該減少很多時間。

0

PointData.PointDTTM上的非聚集索引可能會有所不同 - 您要求SQL從每個PointID的該字段中查找MAX值,並且SQL只有聚簇索引才能完成此操作。明顯好於桌面掃描,但仍不是最佳選擇。

此外,你加入到爲每個行運行一次子查詢 - 您可以使用以下修改消除它:

;WITH PointDataDTTMMax (PointID, PointDTTM) 
    AS (SELECT PointID, MAX(PointDTTM) 
      FROM PointData 
     GROUP BY PointID) 
SELECT ... 

這將使用CTE(公共表表達式),只執行一次聚合查詢。

0

要麼在非聚集索引中包含PointValue,以便覆蓋(甚至在執行計劃中使用它)還是更改聚簇索引以製作PointDTTM DESC。

還可以獲得在其他的答案(取決於優化是否已經處理那麼好)

0

我想通過更換子查詢開始提到擺脫相關子查詢的 - 我沒有嘗試這一點,希望也沒有錯字:

SELECT dbo.Point.PointDriverID, dbo.Point.AssetID, dbo.Point.PointID, dbo.Point.PointTypeID, dbo.Point.PointName, dbo.Point.ForeignID, dbo.Pointtype.TrendInterval, coalesce(dbo.Point.trendpts,5) AS TrendPts, LastTimeStamp = PointDTTM, LastValue=PointValue, Timezone 
FROM dbo.Point 
    LEFT JOIN dbo.PointType ON dbo.PointType.PointTypeID = dbo.Point.PointTypeID 

    INNER JOIN (SELECT dbo.PointData.PointID, Max(dbo.PointData.PointDTTM) AS MaxDT 
       FROM dbo.PointData 
        INNER JOIN dbo.Point ON dbo.PointData.PointID = dbo.Point.PointID 
       WHERE onlinetrended =1 and WantTrend=1 
       GROUP BY dbo.PointData.PointID) f 
     ON dbo.Point.PointID = f.PointID 
    INNER JOIN dbo.PointData 
      ON f.PointID = dbo.PointData.PointID AND f.MaxDT = dbo.PointData.PointDTTM 

    LEFT JOIN dbo.SiteAsset ON dbo.SiteAsset.AssetID = dbo.Point.AssetID 
    LEFT JOIN dbo.Site ON dbo.Site.SiteID = dbo.SiteAsset.SiteID 

然後我會檢查你是否能與內部連接更換部分或全部左聯接。每個點都有一個PointType嗎?如果是,請使用內部連接。每個點是否至少有一個PointData?然後使用內部連接。對SiteAsset和Site執行相同操作。

如果這還不夠,請檢查執行計劃的查詢:哪些步驟佔用大部分執行時間?找到大的,並嘗試優化它們。