Выбор из подмножества на основе значения внутри того же подмножества? - PullRequest
1 голос
/ 27 июля 2011

Я создал такую ​​таблицу:

CREATE TABLE #TEMP(RecordDate datetime, First VARCHAR(255), Last VARCHAR(255), Value int)

INSERT INTO #TEMP VALUES('2011-03-01 00:00:00.000','john','smith','10')
INSERT INTO #TEMP VALUES('2011-03-01 00:00:00.000','john','adams','60')
INSERT INTO #TEMP VALUES('2011-03-01 00:00:00.000','john','resig','90')
INSERT INTO #TEMP VALUES('2011-03-01 00:00:00.000','john','balte','95')

INSERT INTO #TEMP VALUES('2011-03-01 01:00:00.000','john','smith','98')
INSERT INTO #TEMP VALUES('2011-03-01 01:00:00.000','john','adams','67')
INSERT INTO #TEMP VALUES('2011-03-01 01:00:00.000','john','resig','24')
INSERT INTO #TEMP VALUES('2011-03-01 01:00:00.000','john','balte','20')

SELECT * FROM #TEMP

DROP TABLE #TEMP

, которая теперь содержит следующие записи:

RecordDate              First   Last    Value
2011-03-01 00:00:00.000 john    smith   10
2011-03-01 00:00:00.000 john    adams   60
2011-03-01 00:00:00.000 john    resig   90
2011-03-01 00:00:00.000 john    balte   95
2011-03-01 01:00:00.000 john    smith   98
2011-03-01 01:00:00.000 john    adams   67
2011-03-01 01:00:00.000 john    resig   24
2011-03-01 01:00:00.000 john    balte   20

Я пытаюсь получить таблицу, подобную следующей:

RecordDate                first    Good     Bad
2011-03-01 00:00:00.000   john     3        1
2011-03-01 01:00:00.000   john     2        2

То, как я вычисляю «хорошо» и «плохо», состоит в том, чтобы взять MAX всех людей с именем john на определенную дату, а затем применить его в качестве фильтра к исходному набору данных на эту конкретную датуи имяТолько значения больше 0.5*MAXValue считаются Good.

В таблице результатов имеется 3 правильных значения, поскольку максимальное значение для первой даты было 95, и только 60,90,95 больше 0.5*95, поэтому результат имеет (Good,Bad) = (3,1).Во втором результате, аналогично, это (2,2).

Моя таблица достаточно большая и имеет около 300 миллионов записей, и я не могу понять, с чего начать, чтобы сделать это эффективно.Любые предложения о том, как эффективный способ может выглядеть?

Мой текущий (работающий, но дорогой) подход приведен ниже:

SELECT    RecordDate
        , FirstName
        , 
        (
            SELECT COUNT(*) 
            FROM #TEMP
            WHERE Value > 0.5*(SELECT MAX(Value) FROM #TEMP WHERE RecordDate = A.RecordDate AND FirstName = A.FirstName)
            AND RecordDate = A.RecordDate AND FirstName = A.FirstName
        ) AS Good
        ,
        (
            SELECT COUNT(*) 
            FROM #TEMP
            WHERE Value < 0.5*(SELECT MAX(Value) FROM #TEMP WHERE RecordDate = A.RecordDate AND FirstName = A.FirstName)
            AND RecordDate = A.RecordDate AND FirstName = A.FirstName
        ) AS Bad
FROM #TEMP A
GROUP BY RecordDate, FirstName;

Ответы [ 2 ]

3 голосов
/ 27 июля 2011

Вот, пожалуйста:

select 
   t.RecordDate,
   COUNT(case 
           when t.Value > MV.MaxValue * 0.5 then 1
           else null
         end) Good,
   COUNT(case 
           when t.Value <= MV.MaxValue * 0.5 then 1
           else null
         end) Bad
from #Temp t inner join
(select RecordDate, MAX(Value) MaxValue
 from #Temp Group By RecordDate) MV on t.RecordDate = MV.RecordDate
Group by t.RecordDate

Хитрость заключается в создании производной таблицы с максимальными значениями для каждой даты записи, а затем INNER JOIN в самой таблице.Как только вы решите максимальные значения, вы можете получить к ним прямой доступ.

Обновить

Я вижу, вы обновили свой вопрос и включили имя в результат.Не бойтесь, вот решение:

select 
   t.RecordDate,
   t.First,
   COUNT(case 
           when t.Value > MV.MaxValue * 0.5 then 1
           else null
         end) Good,
   COUNT(case 
           when t.Value <= MV.MaxValue * 0.5 then 1
           else null
         end) Bad
from #Temp t inner join
(select RecordDate, First, MAX(Value) MaxValue
 from #Temp Group By RecordDate, First) MV 
   on (t.RecordDate = MV.RecordDate and t.First = MV.First)
Group by t.RecordDate, t.First
1 голос
/ 27 июля 2011

Вложенные запросы, которые ссылаются на внешний запрос, могут вызывать много повторяющихся действий.Это просто вычислит все МАКС для всех имен и дат за один раз:

SELECT RecordDate, FirstName, MAX(Value) FROM #TEMP GROUP BY RecordDate, FirstName  

Теперь присоединитесь к исходным данным:

SELECT A.RecordDate, A.FirstName,
       SUM(CASE WHEN Value > MaxVal*0.5 THEN 1 ELSE 0 END) AS GOOD,
       SUM(CASE WHEN Value > MaxVal*0.5 THEN 0 ELSE 1 END) AS BAD,
FROM #TEMP A INNER JOIN
     (SELECT RecordDate, FirstName, MAX(Value) as MaxVal 
      FROM #TEMP GROUP BY RecordDate, FirstName) B 
         ON (A.RecordDate = B.RecordDate AND A.FirstName = B.FirstName)
GROUP BY A.RecordDate, A.FirstName
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...