2010-03-11 3 views
0

Geetings, Stackers.Подведение итогов (навсегда) данных в таблице SQL

У меня огромное количество точек данных в таблице SQL, и я хочу обобщить их так, чтобы они напоминали RRD.

Предполагая таблицу, такие как

ID | ENTITY_ID | SCORE_DATE | SCORE | SOME_OTHER_DATA 
----+-----------+------------+-------+----------------- 
    1 | A00000001 | 01/01/2010 | 100 | some data 
    2 | A00000002 | 01/01/2010 | 105 | more data 
    3 | A00000003 | 01/01/2010 | 104 | various text 
... | ......... | .......... | ..... | ... 
... | A00009999 | 01/01/2010 | 101 | 
... | A00000001 | 02/01/2010 | 104 | 
... | A00000002 | 02/01/2010 | 119 | 
... | A00000003 | 02/01/2010 | 119 | 
... | ......... | .......... | ..... | 
... | A00009999 | 02/01/2010 | 101 | arbitrary data 
... | ......... | .......... | ..... | ... 
... | A00000001 | 01/02/2010 | 104 | 
... | A00000002 | 01/02/2010 | 119 | 
... | A00000003 | 01/01/2010 | 119 | 

Я хочу, чтобы в конечном итоге с одной записью в сущности, за месяц:

ID | ENTITY_ID | SCORE_DATE | SCORE | 
----+-----------+------------+-------+ 
... | A00000001 | 01/01/2010 | 100 | 
... | A00000002 | 01/01/2010 | 105 | 
... | A00000003 | 01/01/2010 | 104 | 
... | A00000001 | 01/02/2010 | 100 | 
... | A00000002 | 01/02/2010 | 105 | 
... | A00000003 | 01/02/2010 | 104 | 

(я не забочусь о SOME_OTHER_DATA - Я буду выберите что-нибудь - возможно, первую или последнюю запись.)

Что такое простой способ сделать это на регулярной основе, чтобы все в последнем календарном месяце было суммировано таким образом?

На данный момент мой план вид:

  • Для каждого EntityId
    • Для каждого месяца
      • Найти средний балл для всех записей в данном месяце
      • Update первый рекорд с результатами предыдущего шага
      • Удалить все записи, которые не являются первыми

Я не могу думать о аккуратный способ сделать это, хотя, что не включает в себя множество обновлений и итерации.

Это может быть сделано в хранимой процедуре SQL или может быть включено в .Net-приложение, которое генерирует эти данные, поэтому решение действительно не должно быть «одним большим SQL-скриптом», но может быть :)

(SQL-2005)

+0

@ Цилиндрический, я перечитал вопрос и полностью изменил свой ответ. –

ответ

0

Дайте это попробовать:

--I am using @table variables here, you will want to use your actual table in place of @YourTable and a #Temptable for @YourTable2, with a PK on ID 
SET NOCOUNT ON 
DECLARE @YourTable table (ID int,ENTITY_ID char(9),SCORE_DATE datetime,SCORE int ,SOME_OTHER_DATA varchar(100)) 
DECLARE @YourTable2 table (ID int) 
INSERT INTO @YourTable VALUES (1 , 'A00000001','01/01/2010',100,'some data') 
INSERT INTO @YourTable VALUES (2 , 'A00000002','01/01/2010',105,'more data') 
INSERT INTO @YourTable VALUES (3 , 'A00000003','01/01/2010',104,'various text') 
INSERT INTO @YourTable VALUES (4 , 'A00009999','01/01/2010',101,null) 
INSERT INTO @YourTable VALUES (5 , 'A00000001','02/01/2010',104,null) 
INSERT INTO @YourTable VALUES (6 , 'A00000002','02/01/2010',119,null) 
INSERT INTO @YourTable VALUES (7 , 'A00000003','02/01/2010',119,null) 
INSERT INTO @YourTable VALUES (8 , 'A00009999','02/01/2010',101,'arbitrary data') 
INSERT INTO @YourTable VALUES (9 , 'A00000001','01/02/2010',104,null) 
INSERT INTO @YourTable VALUES (10, 'A00000002','01/02/2010',119,null) 
INSERT INTO @YourTable VALUES (11, 'A00000003','01/01/2010',119,null) 
SET NOCOUNT OFF 

SELECT 'BEFORE',* FROM @YourTable ORDER BY ENTITY_ID,SCORE_DATE 

UPDATE y 
    SET SCORE=dt_a.AvgScore 
    OUTPUT INSERTED.ID --capture all updated rows 
     INTO @YourTable2 
    FROM @YourTable y 
     INNER JOIN (SELECT --get avg score for each ENTITY_ID per month 
         ENTITY_ID 
          ,AVG(SCORE) as AvgScore 
          , DATEADD(month,DATEDIFF(month,0,SCORE_DATE),0) AS MonthOf,DATEADD(month,1,DATEADD(month,DATEDIFF(month,0,SCORE_DATE),0)) AS MonthNext 
         FROM @YourTable 
         --group by 1st day of current month and 1st day of next month 
         --so an index can be used when joining derived table to UPDATE table 
         GROUP BY ENTITY_ID, DATEADD(month,DATEDIFF(month,0,SCORE_DATE),0),DATEADD(month,1,DATEADD(month,DATEDIFF(month,0,SCORE_DATE),0)) 
        ) dt_a ON y.ENTITY_ID=dt_a.ENTITY_ID AND y.SCORE_DATE>=dt_a.MonthOf AND y.SCORE_DATE<dt_a.MonthNext 
     INNER JOIN (SELECT--get first row for each ENTITY_ID per month 
         ID,ENTITY_ID,SCORE_DATE,SCORE 
         FROM (SELECT 
            ID,ENTITY_ID,SCORE_DATE,SCORE 
             ,ROW_NUMBER() OVER(PARTITION BY ENTITY_ID,DATEADD(month,DATEDIFF(month,0,SCORE_DATE),0) ORDER BY ENTITY_ID,SCORE_DATE) AS RowRank 
            FROM @YourTable 
          ) dt 
         WHERE dt.RowRank=1 
        ) dt_f ON y.ID=dt_f.ID 

DELETE @YourTable 
    WHERE ID NOT IN (SELECT ID FROM @YourTable2) 


SELECT 'AFTER ',* FROM @YourTable ORDER BY ENTITY_ID,SCORE_DATE 

ВЫВОД:

 ID   ENTITY_ID SCORE_DATE    SCORE  SOME_OTHER_DATA 
------ ----------- --------- ----------------------- ----------- ---------------------------------------------------------------------------------------------------- 
BEFORE 1   A00000001 2010-01-01 00:00:00.000 100   some data 
BEFORE 9   A00000001 2010-01-02 00:00:00.000 104   NULL 
BEFORE 5   A00000001 2010-02-01 00:00:00.000 104   NULL 
BEFORE 2   A00000002 2010-01-01 00:00:00.000 105   more data 
BEFORE 10   A00000002 2010-01-02 00:00:00.000 119   NULL 
BEFORE 6   A00000002 2010-02-01 00:00:00.000 119   NULL 
BEFORE 3   A00000003 2010-01-01 00:00:00.000 104   various text 
BEFORE 11   A00000003 2010-01-01 00:00:00.000 119   NULL 
BEFORE 7   A00000003 2010-02-01 00:00:00.000 119   NULL 
BEFORE 4   A00009999 2010-01-01 00:00:00.000 101   NULL 
BEFORE 8   A00009999 2010-02-01 00:00:00.000 101   arbitrary data 

(11 row(s) affected) 

(8 row(s) affected) 

(3 row(s) affected) 

     ID   ENTITY_ID SCORE_DATE    SCORE  SOME_OTHER_DATA 
------ ----------- --------- ----------------------- ----------- ---------------------------------------------------------------------------------------------------- 
AFTER 1   A00000001 2010-01-01 00:00:00.000 102   some data 
AFTER 5   A00000001 2010-02-01 00:00:00.000 104   NULL 
AFTER 2   A00000002 2010-01-01 00:00:00.000 112   more data 
AFTER 6   A00000002 2010-02-01 00:00:00.000 119   NULL 
AFTER 3   A00000003 2010-01-01 00:00:00.000 111   various text 
AFTER 7   A00000003 2010-02-01 00:00:00.000 119   NULL 
AFTER 4   A00009999 2010-01-01 00:00:00.000 101   NULL 
AFTER 8   A00009999 2010-02-01 00:00:00.000 101   arbitrary data 

(8 row(s) affected) 
0

Это даст вам средние значения для всех ваших данных:

select ENTITY_ID, year(SCORE_DATE) as Year, month(SCORE_DATE) as Month, avg(SCORE) as Avg 
from MyTable 
group by ENTITY_ID, year(SCORE_DATE), month(SCORE_DATE) 

Чтобы ограничить в данном месяце, например,, В феврале прошлого года, вы можете сделать:

select ENTITY_ID, year(SCORE_DATE) as Year, month(SCORE_DATE) as Month, avg(SCORE) as Avg 
from MyTable 
where year(SCORE_DATE) = 2010 and month(SCORE_DATE) = 2 
group by ENTITY_ID, year(SCORE_DATE), month(SCORE_DATE) 

Эта версия будет на самом деле работают лучше, но параметры немного менее дружественные дело с:

select ENTITY_ID, year(SCORE_DATE) as Year, month(SCORE_DATE) as Month, avg(SCORE) as Avg 
from MyTable 
where SCORE_DATE >= '2/1/2010' and SCORE_DATE < '3/1/2010' 
group by ENTITY_ID, year(SCORE_DATE), month(SCORE_DATE) 

Если вы хотите запрос, который всегда возвращает последний данные месяца, вы можете сделать это:

select ENTITY_ID, year(SCORE_DATE) as Year, month(SCORE_DATE) as Month, avg(SCORE) as Avg 
from MyTable 
where year(SCORE_DATE) = year(dateadd(month, -1, getdate())) and month(dateadd(month, -1, getdate())) = 2 
group by ENTITY_ID, year(SCORE_DATE), month(SCORE_DATE) 

А лучше функционирующую версию:

select ENTITY_ID, year(SCORE_DATE) as Year, month(SCORE_DATE) as Month, avg(SCORE) as Avg 
from MyTable 
where SCORE_DATE >= dateadd(month, ((year(getdate()) - 1900) * 12) + month(getdate())-2, 0) 
    and SCORE_DATE < dateadd(month, ((year(getdate()) - 1900) * 12) + month(getdate())-1, 0) 
group by ENTITY_ID, year(SCORE_DATE), month(SCORE_DATE) 
Смежные вопросы