2009-05-26 16 views
7

Próbuję mieć średnią bieżącą kolumnę w instrukcji SELECT na podstawie kolumny z n poprzednich wierszy w tej samej instrukcji SELECT. Średnia, której potrzebuję, jest oparta na n poprzednich wierszach w zestawie wyników.Polecenie SQL Select dla obliczania kolumny z bieżącą średnią wartością

Wytłumaczę

Id  Number  Average 
1    1   NULL 
2    3   NULL 
3    2   NULL 
4    4    2 <----- Average of (1, 3, 2),Numbers from previous 3 rows 
5    6    3 <----- Average of (3, 2, 4),Numbers from previous 3 rows 
.    .    . 
.    .    . 

3 pierwsze wiersze Średnia kolumny są nieważne, ponieważ nie ma żadnych poprzednich wierszach. Wiersz 4 w kolumnie Średnia pokazuje średnią z kolumny Liczba z 3 poprzednich wierszy.

Potrzebuję pomocy przy konstruowaniu instrukcji SQL Select, która to zrobi.

+0

Jakiego rodzaju bazy danych SQL używasz? –

+0

Używam programu SQL Server 2008. – HYP

+0

Myślę, że jest to jeden z tych naprawdę rzadkich przypadków, w których kursory będą najszybsze ... po prostu zachowaj ostatnie 3 wiersze w vars ... –

Odpowiedz

11

ten powinien zrobić:

--Test Data 
CREATE TABLE RowsToAverage 
    (
    ID int NOT NULL, 
    Number int NOT NULL 
    ) 

INSERT RowsToAverage(ID, Number) 
SELECT 1, 1 
UNION ALL 
SELECT 2, 3 
UNION ALL 
SELECT 3, 2 
UNION ALL 
SELECT 4, 4 
UNION ALL 
SELECT 5, 6 
UNION ALL 
SELECT 6, 8 
UNION ALL 
SELECT 7, 10 

--The query 
;WITH NumberedRows 
AS 
(
SELECT rta.*, row_number() OVER (ORDER BY rta.ID ASC) AS RowNumber 
FROM RowsToAverage rta 
) 

SELECT nr.ID, nr.Number, 
     CASE 
      WHEN nr.RowNumber <=3 THEN NULL 
      ELSE ( SELECT avg(Number) 
        FROM NumberedRows 
        WHERE RowNumber < nr.RowNumber 
        AND  RowNumber >= nr.RowNumber - 3 
       ) 
     END AS MovingAverage 
FROM NumberedRows nr 
+0

+1, działa poprawnie w moim systemie –

+0

+ 1- Bardzo eleganckie rozwiązanie. MVP, MVP, MVP! –

+0

Bardzo eleganckie rozwiązanie. Z 9000 wierszy zajmuje około 45 sekund na moim serwerze dev. Czy istnieje sposób na bardziej efektywne wykorzystanie tej techniki? –

0

Sprawdź niektóre rozwiązania here. Jestem pewien, że możesz łatwo zaadaptować jedną z nich.

+1

Chociaż może to teoretycznie odpowiedzieć na pytanie, [byłoby lepiej (http://meta.stackexchange.com/q/8259), aby dołączyć zasadnicze części odpowiedzi tutaj, i podać link dla odniesienia. – Sklivvz

1

Edycja: Tęskniłem za punkt, że powinien on średnio trzy poprzednie rekordy ...

Dla ogólnego średniego biegu, myślę, że coś jak to będzie działać :

SELECT 
    id, number, 
    SUM(number) OVER (ORDER BY ID)/
     ROW_NUMBER() OVER (ORDER BY ID) AS [RunningAverage] 
FROM myTable 
ORDER BY ID 
+0

przy korzystaniu z tabeli @Aaron Alton RowsToAverage (zmieniłem z MyTable na FROM RowsToAverage), pojawia się błąd: Msg 102, Level 15, State 1, Line 3 Niepoprawna składnia w pobliżu "order". –

+0

Z jakiego RDBMS korzystasz? Funkcje okienkowania są dostępne tylko w SQL 2005 i nowszych. –

+0

Powinienem dodać, że OP wspomniał, że używają SQL 2008. –

0

Jeśli chcesz to być naprawdę wydajnych i ar nie boi się kopać w rzadko używanym obszarze SQL Server, należy spojrzeć na pisanie zwyczaj łączną funkcję. SQL Server 2005 i 2008 wprowadziły integrację CLR do tabeli, w tym możliwość pisania funkcji agregujących użytkownika. Niekontrolowany ogólny agregat bieżący byłby najskuteczniejszym sposobem na obliczenie średniej bieżącej.

8

Zakładając, że kolumna Id jest sekwencyjna, oto uproszczony zapytania dla tabeli o nazwie „MojaTabela”:

SELECT 
    b.Id, 
    b.Number, 
    (
     SELECT 
     AVG(a.Number) 
     FROM 
     MyTable a 
    WHERE 
     a.id >= (b.Id - 3) 
     AND a.id < b.Id 
     AND b.Id > 3 
    ) as Average 
FROM 
    MyTable b; 
+0

Uhmm. Zakładając identyfikatory sekwencyjne, to też działa. +1 –

+0

Może to również działać, jeśli w tabeli nie zostały usunięte żadne wiersze. Zaakceptowałem rozwiązanie Aarona Altona z powodu parametru row_number() OVER (ORDER BY rta.ID ASC) działa we wszystkich przypadkach. – HYP

2

Prosty własny dołączyć wydaje się wykonać znacznie lepiej niż z rzędu przedstawieniu Podzapytanie

Generowanie 10k wierszy danych z badania:

drop table test10k 
create table test10k (Id int, Number int, constraint test10k_cpk primary key clustered (id)) 

;WITH digits AS (
    SELECT 0 as Number 
    UNION SELECT 1 
    UNION SELECT 2 
    UNION SELECT 3 
    UNION SELECT 4 
    UNION SELECT 5 
    UNION SELECT 6 
    UNION SELECT 7 
    UNION SELECT 8 
    UNION SELECT 9 
) 
,numbers as (
    SELECT 
     (thousands.Number * 1000) 
     + (hundreds.Number * 100) 
     + (tens.Number * 10) 
     + ones.Number AS Number 
    FROM digits AS ones 
    CROSS JOIN digits AS tens 
    CROSS JOIN digits AS hundreds 
    CROSS JOIN digits AS thousands 
) 
insert test10k (Id, Number) 
select Number, Number 
from numbers 

chciałbym ciągnąć szczególnym przypadku pierwszych 3 rzędach z główne zapytanie, możesz UNION WSZYSTKIE z powrotem, jeśli naprawdę chcesz go w zestawie wierszy. Własna dołączyć zapytania:

;WITH NumberedRows 
AS 
(
    SELECT rta.*, row_number() OVER (ORDER BY rta.ID ASC) AS RowNumber 
    FROM test10k rta 
) 

SELECT nr.ID, nr.Number, 
    avg(trailing.Number) as MovingAverage 
FROM NumberedRows nr 
    join NumberedRows as trailing on trailing.RowNumber between nr.RowNumber-3 and nr.RowNumber-1 
where nr.Number > 3 
group by nr.id, nr.Number 

Na moim komputerze to trwa około 10 sekund, podejście Podzapytanie że Aaron Alton wykazać trwa około 45 sekund (po I zmienił go, aby odzwierciedlić moje testowej tabeli źródłowej):

;WITH NumberedRows 
AS 
(
    SELECT rta.*, row_number() OVER (ORDER BY rta.ID ASC) AS RowNumber 
    FROM test10k rta 
) 
SELECT nr.ID, nr.Number, 
    CASE 
      WHEN nr.RowNumber <=3 THEN NULL 
      ELSE ( SELECT avg(Number) 
          FROM NumberedRows 
          WHERE RowNumber < nr.RowNumber 
          AND    RowNumber >= nr.RowNumber - 3 
        ) 
    END AS MovingAverage 
FROM NumberedRows nr 

Jeśli wykonasz SET STATISTICS PROFILE ON, możesz zobaczyć, że samo łączenie ma 10k wykonywanych w szpuli stołu. Podzapytanie wykonuje 10k na filtrze, agregacie i innych czynnościach.

Powiązane problemy