2009-08-22 179 views
2

我有數據樣本的表格,有時間戳和一些數據。每個表在時間戳上都有一個聚集索引,然後是一個特定於數據的密鑰。數據樣本不一定是等距的。避免在SQL Server GROUP BY中進行不必要的排序?

我需要在特定的時間範圍內對數據進行縮減採樣以繪製圖表 - 例如,從100,000行到N,其中N大約是50.雖然我可能不得不妥協於算法的「正確性」從DSP的角度來看,我想保留在SQL中出於性能原因。

我目前的想法是將時間範圍內的樣本分組到N個框中,然後取每組的平均值。在SQL中實現此目的的一種方法是將分區函數應用於範圍從0到N-1(含)的日期,然後是GROUP BY和AVG。

我認爲這個GROUP BY可以在沒有排序的情況下執行,因爲日期來自聚簇索引,分區函數是單調的。但是,SQL Server似乎沒有注意到這一點,它發出的代碼佔執行代價的78%(在下面的例子中)。假設我是對的,這種類型是不必要的,我可以使查詢速度提高5倍。

有沒有辦法強制SQL Server跳過排序?還是有更好的方法來解決這個問題?

乾杯。 本

IF EXISTS(SELECT name FROM sysobjects WHERE name = N'test') DROP TABLE test 

CREATE TABLE test 
(
    date DATETIME NOT NULL, 
    v FLOAT NOT NULL, 
    CONSTRAINT PK_test PRIMARY KEY CLUSTERED (date ASC, v ASC) 
) 

INSERT INTO test (date, v) VALUES ('2009-08-22 14:06:00.000', 1) 
INSERT INTO test (date, v) VALUES ('2009-08-22 17:09:00.000', 8) 
INSERT INTO test (date, v) VALUES ('2009-08-24 00:00:00.000', 2) 
INSERT INTO test (date, v) VALUES ('2009-08-24 03:00:00.000', 9) 
INSERT INTO test (date, v) VALUES ('2009-08-24 14:06:00.000', 7) 

-- the lower bound is set to the table min for demo purposes; in reality 
-- it could be any date 
declare @min float 
set @min = cast((select min(date) from test) as float) 

-- similarly for max 
declare @max float 
set @max = cast((select max(date) from test) as float) 

-- the number of results to return (assuming enough data is available) 
declare @count int 
set @count = 3 

-- precompute scale factor 
declare @scale float 
set @scale = (@count - 1)/(@max - @min) 
select @scale 

-- this scales the dates from 0 to n-1 
select (cast(date as float) - @min) * @scale, v from test 

-- this rounds the scaled dates to the nearest partition, 
-- groups by the partition, and then averages values in each partition 
select round((cast(date as float) - @min) * @scale, 0), avg(v) from test 
group by round((cast(date as float) - @min) * @scale, 0) 

回答

2

SQL Server確實沒有辦法知道date集羣密鑰可以用於像round(cast.. as float))這樣的表達式以保證順序。只有這樣,纔會拋棄賽道。加入(... [email protected]) * @scale,你自己搞得一團糟。如果您需要對這些表達式進行排序和分組,請將它們存儲在保留的計算列中並由它們索引。儘管您可能想要使用DATEPART,但由於經歷了不精確的類型(例如float),可能會使表達式無法用於保留的計算列。

更新

datefloat等同的話題:

declare @f float, @d datetime; 
select @d = cast(1 as datetime); 
select @f = cast(1 as float); 
select cast(@d as varbinary(8)), cast(@f as varbinary(8)), @d, cast(@d as float) 

產生以下:

0x0000000100000000 0x3FF0000000000000 1900-01-02 00:00:00.000 1 

所以你可以看到,altough它們都存儲在8字節(至少float(25...53)),0123的內部表示不是float,其中整數部分是白天,小數部分是時間(正如通常假定的那樣)。

再舉一個例子:

declare @d datetime; 
select @d = '1900-01-02 12:00 PM'; 
select cast(@d as varbinary(8)), cast(@d as float) 

0x0000000100C5C100 1.5 

再次澆鑄@dfloat的結果是1.5,但0x0000000100C5C100日期時間內部表示將是IEEE雙值2.1284E-314,不1.5

+0

在這個例子中,應該是很容易分析至少(... - @ min)* @scale部分。不幸的是,將「日期」列存儲爲浮點數似乎沒有什麼區別。 但是,最終你說得對:期待SQL Server自動解決這個問題有點樂觀。我真正希望的是一種告訴它假設數據已經被排序的方法。 :) 關於FLOAT不精確,我認爲DATETIME只是一個FLOAT內部? – 2009-08-22 19:03:25

+0

查看我的日期和浮動'內部'假設的更新。 – 2009-08-22 20:20:08

+0

啊,這很有趣!謝謝。 – 2009-08-22 21:04:38

1

是,SQL-Server一直有一些問題,這種時間分配summary選擇的。分析服務有多種方式來處理它,但數據服務方面則更加有限。

我建議你嘗試(我不能嘗試或測試任何東西從這裏)是做一個次要的「分區表」,其中包含yor分區定義,然後加入反對它。你將需要一些mathcing索引讓他有機會工作:

0

有兩個問題:

此查詢需要多長時間?

你確定它是排序日期嗎?另外在計劃中它是在排序日期?它分區後?這將是我的猜測。我懷疑它就像它做的第一件事情......也許是它劃分或組合它需要再做一次排序的方式。

不管怎麼說,即使它的排序已排序列表中,也不會認爲,這將需要很長時間,因爲它是alredy整理...