2009-08-22 7 views
2

Ich habe Tabellen mit Datenproben, mit einem Zeitstempel und einigen Daten. Jede Tabelle hat einen gruppierten Index für den Zeitstempel und dann einen datenspezifischen Schlüssel. Datenproben sind nicht notwendigerweise äquidistant.Vermeidung unnötiger Sortierung in SQL Server GROUP BY?

Ich muss die Daten in einem bestimmten Zeitbereich downsampling, um Graphen zu zeichnen - sagen wir von 100.000 Zeilen zu N, wo N ist etwa 50. Während ich muss möglicherweise Kompromisse bei der "Richtigkeit" des Algorithmus Aus DSP-Sicht möchte ich dies aus Performancegründen in SQL behalten.

Meine aktuelle Idee ist es, Proben im Zeitbereich in N-Boxen zu gruppieren, und dann den Durchschnitt jeder Gruppe zu nehmen. Eine Möglichkeit, dies in SQL zu erreichen, besteht darin, eine Partitionsfunktion auf das Datum anzuwenden, das von 0 bis N-1 (einschließlich) und dann GROUP BY und AVG reicht.

Ich denke, dass diese GROUP BY ohne eine Sortierung durchgeführt werden kann, da das Datum aus einem gruppierten Index stammt und die Partitionsfunktion monoton ist. SQL Server scheint dies jedoch nicht zu bemerken und gibt eine Sortierung aus, die 78% der Ausführungskosten ausmacht (im folgenden Beispiel). Angenommen, ich habe Recht, und diese Art ist unnötig, könnte ich die Abfrage 5 mal schneller machen.

Gibt es eine Möglichkeit, SQL Server zu zwingen, die Sortierung zu überspringen? Oder gibt es einen besseren Weg, das Problem anzugehen?

Prost. Ben

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) 

Antwort

2

Es gibt wirklich keine Möglichkeit, SQL Server würde wissen, dass die date gruppierten Schlüssel für einen Ausdruck wie round(cast.. as float)) verwendet werden, um die Reihenfolge zu gewährleisten. Nur das und würde es von der Strecke werfen. Fügen Sie die (... [email protected]) * @scale hinzu und Sie haben sich ein perfektes Durcheinander geschaffen. Wenn Sie nach solchen Ausdrücken sortieren und gruppieren müssen, lassen Sie sie in persistenten berechneten Spalten speichern und indexieren Sie sie. Wahrscheinlich möchten Sie DATEPART verwenden, da das Durchlaufen eines unpräzisen Typs wie float den Ausdruck wahrscheinlich für eine persistente berechnete Spalte unbrauchbar macht.

aktualisieren

Zum Thema date und float gleichwertig:

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) 

Dies erzeugt:

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

Sie können also sehen, dass altough sie beide auf 8 gespeichert sind Bytes (mindestens die float(25...53)), die interne Darstellung von datetime ist kein float mit ganzzahligem Teil als Tag und Bruchteil als Zeit (wie oft angenommen).

ein anderes Beispiel geben:

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

0x0000000100C5C100 1.5 

Auch das Ergebnis @d zu float Gießen ist 1.5, aber die Datumzeit interne Darstellung 0x0000000100C5C100 würde der IEEE doppelte Wert 2.1284E-314, nicht 1.5 sein.

+0

In diesem Beispiel sollte es recht einfach sein, zumindest zu analysieren die (... - @ min) * @skalen Teil. Das Speichern der Spalte "Datum" als Float scheint jedoch keinen Unterschied zu machen. Letztendlich haben Sie jedoch recht: Es ist ein wenig optimistisch zu erwarten, dass SQL Server dies automatisch löst. Was ich wirklich hoffe, ist eine Möglichkeit, zu sagen, dass die Daten bereits sortiert sind. :) In Bezug auf FLOAT ungenau, dachte ich, dass DATETIME nur ein FLOAT intern ist? –

+0

Siehe meine Aktualisierung am Datum und float 'interne' Annahme. –

+0

Ah, das ist sehr interessant! Vielen Dank. –

1

Ja, SQL-Server immer einige Probleme mit dieser Art von Zeit-Partitionierung Zusammenfassung SELECTs hat. Analysis Services bietet eine Vielzahl von Möglichkeiten, um damit umzugehen, aber die Data Servies-Seite ist begrenzter.

Was ich vorschlagen würde versuchen Sie (ich kann nicht versuchen oder nichts von hier) ist eine sekundäre "Partitionstabelle", die Ihre Partition Definitionen enthält und dann gegen sie zu machen. Sie werden einige Indizes und Gleiches für seine brauchen eine Chance zu haben, zu arbeiten:

0

Zwei Fragen:

Wie lange dauert diese Abfrage?

Und sind Sie sicher, dass es das Datum sortiert? Auch wo im Plan ist das Sortieren des Datums? Nach der Partitionierung? Das wäre meine Vermutung. Ich würde bezweifeln, dass es so ist wie das erste, was es tut ... Vielleicht muss die Art, wie es sich teilt oder gruppiert, es wieder tun.

Anyways, selbst wenn es so wäre Art eine bereits sortierte Liste, wäre es nicht glauben, dass es sehr lange dauern würde, weil es alredy sortiert ...