2012-02-16 68 views
5

我有一張表,其中包含大量的數據,我們特別關注date字段。原因是數據量增加了30倍,舊的方式很快就會崩潰。查詢我希望你可以幫我優化需求:試圖優化一個查詢,選擇「近似最接近的記錄」

  • 採取日期的列表(由CTE基於表值函數生成)
  • 檢索單個記錄爲每個日期的
    • 基於的「最接近的」一些定義

例如,當前表包含以5秒的數據(+/-一點)的間隔。我需要對該表格進行採樣並獲得最接近30秒間隔的記錄。

我現在擁有的作品很好。我只是很好奇,如果有更好的方法來優化它。如果我可以在Linq To SQL中使用它,那也會很整潔。鑑於日期值的數量(大約200萬行),我甚至對索引建議感興趣。

declare @st datetime ; set @st = '2012-01-31 05:05:00'; 
declare @end datetime ; set @end = '2012-01-31 05:10:00'; 

select distinct 
    log.* -- id, 
from 
    dbo.fn_GenerateDateSteps(@st, @end, 30) as d 
     inner join lotsOfLogData log on l.Id = (
      select top 1 e.[Id] 
      from 
       lotsOfLogData as log -- contains data in 5 second intervals 
      where 
       log.stationId = 1000 
       -- search for dates in a certain range 
       AND utcTime between DateAdd(s, -10, dt) AND DateAdd(s, 5, dt) 
      order by 
       -- get the 'closest'. this can change a little, but will always 
       -- be based on a difference between the date 
       abs(datediff(s, dt, UtcTime)) 
     ) 
    -- updated the query to be correct. stadionId should be inside the subquery 

lotsOfLogData的表結構如下。站點ID(可能是50)相對較少,但每個站點都有很多記錄。當我們查詢時,我們知道電臺ID。

create table ##lotsOfLogData (
    Id   bigint  identity(1,1) not null 
, StationId int   not null 
, UtcTime  datetime not null 
    -- 20 other fields, used for other calculations 
) 

fn_GenerateDateSteps返回這樣的數據集,爲參數給出:

[DT] 
2012-01-31 05:05:00.000 
2012-01-31 05:05:30.000 
2012-01-31 05:06:00.000 
2012-01-31 05:06:30.000 (and so on, every 30 seconds) 

我有一個臨時表做到了這一點爲好,以這種方式,但說出來一點點有點貴。

declare @dates table (dt datetime, ClosestId bigint); 
insert into @dates (dt) select dt from dbo.fn_GenerateDateSteps(@st, @end, 30) 
update @dates set closestId = (-- same subquery as above) 
select * from lotsOfLogData inner join @dates on Id = ClosestId 

編輯:搞掂

了200K +行與現在的工作。我嘗試了兩種方式,並且使用適當的索引(id/time + includes(..所有列...)工作得很好。然而,我最終使用了一個簡單的(和現有的) 。在[ID +時間]指標的更廣泛的理解查詢是爲什麼我在一個結算也許還有更好的方式來做到這一點,但我不能看到它:d

-- subtree cost (crossapply) : .0808 
-- subtree cost (id based) : .0797 

-- see above query for what i ended up with 

回答

1

你可以嘗試

  • 改變inner joincross apply
  • where log.stationid移動到子選擇。

SQL語句

SELECT DISTINCT log.* -- id, 
FROM dbo.fn_GenerateDateSteps(@st, @end, 30) AS d 
     CROSS APPLY (
      SELECT TOP 1 log.* 
      FROM lotsOfLogData AS log -- contains data in 5 second intervals 
      WHERE -- search for dates in a certain range 
        utcTime between DATEADD(s, -10, d.dt) AND DATEADD(s, 5, d.dt) 
        AND log.stationid = 1000 
      ORDER BY 
        -- get the 'closest'. this can change a little, but will always 
        -- be based on a difference between the date 
        ABS(DATEDIFF(s, d.dt, UtcTime)) 
     ) log 
+0

交叉應用要求我在stationid/time上做一個索引包括*表中的所有*其他數據。沒有索引,它的運行與裸體查詢完全一樣,所以在這種情況下,交叉是不會工作的:)雖然我甚至不知道它,所以謝謝! – 2012-02-20 03:50:16

+0

哦,而且我確實在該查詢中有一個錯誤;)我需要*將stationId放入子查詢中,否則我會匹配範圍內的任何stationId。這樣做後,正確的指數被使用,一切都超快(ish) – 2012-02-20 03:51:31

+0

@AndrewBacker - 走開,但謝謝你讓我們知情。 – 2012-02-24 00:16:54

1

只是一些想法...不會真的叫這個答案,但它太大了評論框

首先,我會看看執行計劃的查詢,如果你還沒有這樣做。

更深奧的:你是否可以選擇將日期表示爲原始值(如自定義好的時間以來代表秒/分鐘的整數)?儘管我相信SQL Server將日期作爲數值存儲在引擎蓋下,但對基元的操作可能會稍微快一點,因爲它會消除對DateAdd()DateDiff()的重複調用。

This (fairly old) article舉例說明SQL Server如何實際存儲日期。也許你可以把日期保留爲DATETIME,但用基本的數學運算。

無論數據類型如何,我都會在日期列上嘗試使用聚簇索引,因爲看起來您的搜索可能受益於聚簇索引提供的物理排序,尤其是在搜索範圍較窄的情況下。再次,執行計劃可能會很有啓發性。

我還可以看到用於表示數據的星型模式,其中包含日期泛化的日期維度。然後你可以反對一般化。即使不使用概括,實際的日期數量也會減少,因爲所有具有相同日期的事實都可能指向維度中的同一記錄,因此日期只需要評估一次。

最後,SQL性能調優嚮導(我相信它是在2005年,我知道它是在2008年)對您的查詢有什麼建議?我不建議盲目地實施它的建議,但我經常在它推薦的東西中找到好主意。

+0

不幸的是我不能做太多的數據格式。外部服務接收數據並將其記錄在那裏。我已經看過執行計劃,但它只是巨大的=)我有一個聚集索引在車站ID和性病。索引日期。我只是沒有足夠的實際數據來測試它,而且設置足夠小,如果有索引或不是很重要 – 2012-02-20 01:52:33