2009-08-05 4 views
2

У меня проблема с производительностью при выборе данных в моем проекте.Tricky SQL SELECT Statement

Существует таблица с 3-мя столбцами: «Идентификатор», «время» и «группа»

  • Идентификаторы просто уникальные идентификаторы, как обычно.
  • Время создания записи.
  • Группа должна объединить некоторые записи вместе.

Таким образом, данные таблицы могут выглядеть следующим образом:

ID | TIME  | GROUP 
------------------------ 
1 | 20090805 | A 
2 | 20090804 | A 
3 | 20090804 | B 
4 | 20090805 | B 
5 | 20090803 | A 
6 | 20090802 | B 

... и так далее.

Задача теперь выбрать «текущие» записи (их идентификаторы) в каждой группе на определенную дату. То есть, для каждой группы найдите самую последнюю запись для данной даты.

следующие предпосылки применяются:

  • Я не знаю, различные группы заранее - может быть много различных те изменения с течением времени
  • дата выбора может лежать «между» даты совершения записи в таблице. Тогда я должен найти ближайший в каждой группе. То есть TIME меньше даты выбора, но максимум тех, к которым это правило применяется в группе.

Что я в настоящее время сделать это многоэтапный процесс, который я хотел бы изменить в одном ЗЕЬЕСТА:

  1. SELECT DISTINCT group FROM table найти доступные группы
  2. Для каждой группы найдено в 1), SELECT * FROM table WHERE time<selectionDate AND group=loop ORDER BY time DESC
  3. Возьмет первую строку каждого результата найденной в 2)

Очевидно, что это не является оптимальным.

Таким образом, я был бы очень рад, если бы более опытный эксперт по SQL мог помочь мне найти решение для ввода этих шагов в один оператор.

Спасибо!

+0

Какую базу данных вы используете? Более продвинутые функции сильно различаются. – Thilo

+0

'TIME',' TABLE' и 'GROUP' являются зарезервированными ключевыми словами SQL и, как таковые, я нахожу их немного вводящими в заблуждение в качестве имен объектов SQL, таких как таблицы и столбцы. – pilcrow

+0

Прежде всего, СПАСИБО много за быстрые и отличные ответы. Я действительно забыл некоторые детали: я работаю с ORACLE, но предпочтительно решение должно также работать с Postgresql. И да, имена действительно вводят в заблуждение ключевые слова - извините. –

ответ

10

Следующие действия будут работать на SQL Server 2005+ и Oracle 9i +:

WITH groups AS (
     SELECT t.group, 
       MAX(t.time) 'maxtime' 
     FROM TABLE t 
    GROUP BY t.group) 
SELECT t.id, 
     t.time, 
     t.group 
    FROM TABLE t 
    JOIN groups g ON g.group = t.group AND g.maxtime = t.time 

Любая база данных должна поддерживать:

SELECT t.id, 
     t.time, 
     t.group 
    FROM TABLE t 
    JOIN (SELECT t.group, 
       MAX(t.time) 'maxtime' 
      FROM TABLE t 
     GROUP BY t.group) g ON g.group = t.group AND g.maxtime = t.time 
+0

+1. Совсем как вторая версия, хотя она предполагает, что группа появляется только один раз за «время». У Thilo было эквивалентное решение для вашего второго запроса, используя 'WHERE ... IN * subquery *', но это, кажется, было удалено. – pilcrow

+0

+1: Я думаю, что поеду за вторым решением. Первые тесты кажутся многообещающими. СПАСИБО СНОВА к вам и всем остальным, помогающим мне так быстро и профессионально. СПАСИБО! –

5

Вот как я хотел бы сделать это в SQL Server:

SELECT * FROM table WHERE id in 
(SELECT top 1 id FROM table WHERE time<selectionDate GROUP BY [group] ORDER BY [time]) 
1

Решение будет варьироваться в зависимости от сервера базы данных, так как синтаксис для ТОП запросов меняется. В основном вы ищете «top n на группу», так что вы можете Google, если хотите.

Решения на SQL Server. Ниже приводятся 10 лучших игроков, которые с 1990 года попадают в большинство домашних запусков в год. Ключом является подсчет «Раннего ранга» каждого игрока за каждый год.

select 
    HRRanks.* 
from 
(
    Select 
     b.yearID, b.PlayerID, sum(b.Hr) as TotalHR, 
     rank() over (partition by b.yearID order by sum(b.hr) desc) as HR_Rank 
    from 
     Batting b 
    where 
     b.yearID > 1990 
    group by 
     b.yearID, b.playerID 
) 
    HRRanks 
where 
    HRRanks.HR_Rank <= 10 

Вот решение в Oracle (Top Коммивояжёры в Департамент)

SELECT deptno, avg_sal 
FROM( 
     SELECT deptno, AVG(sal) avg_sal 
     GROUP BY deptno 
     ORDER BY AVG(sal) DESC 
    ) 
WHERE ROWNUM <= 10; 

Или, используя аналитические функции:

SELECT deptno, avg_sal 
FROM (
     SELECT deptno, avg_sal, RANK() OVER (ORDER BY sal DESC) rank 
     FROM 
     (
     SELECT deptno, AVG(sal) avg_sal 
     FROM emp 
     GROUP BY deptno 
     ) 
    ) 
WHERE rank <= 10; 

Или же снова, но с использованием DENSE_RANK() вместо RANK()

0
select * from TABLE where (GROUP, TIME) in (
    select GROUP, max(TIME) from things 
     where TIME >= 20090804 
     group by GROUP 
    ) 

Испытано с MySQL (но мне пришлось изменить имена таблиц и столбцов, поскольку они являются ключевыми словами).

0

SELECT * FROM TABB T1

QUALIFY ROW_NUMBER() OVER (PARTITION BY Groupp, порядок TIMEE по идентификатору DESC) = 1