Каков наилучший запрос для получения текущих записей в архивной таблице (SQL Server 2005/2008) - PullRequest
4 голосов
/ 17 ноября 2008

Пример

Существует приложение, которое измеряет температуру в каждом городе мира. Каждое измерение проводится каждые 5 минут и записывается в таблицу измерений.

CREATE TABLE [dbo].[Measurement](
    [MeasurementID] [int] IDENTITY(1,1) NOT NULL,
    [Town] [varchar](50) NOT NULL,
    [Date] [datetime] NOT NULL,
    [Temp] [int] NOT NULL,
CONSTRAINT [PK_Measurement] PRIMARY KEY CLUSTERED 
(
    [MeasurementID] ASC
)) ON [PRIMARY]

Вопрос

Какой самый эффективный запрос для получения списка городов и их текущей температуры?

Предположим, есть 100 000 городов и 10 миллионов записей

ПРИМЕЧАНИЕ. Я добавил пару возможных ответов, но, возможно, есть и другие варианты.

Ответы [ 6 ]

4 голосов
/ 17 ноября 2008

Вот пара, которая должна работать:

SELECT
m1.Town, m1.Temp
ОТ
Измерение AS m1
LEFT JOIN
Измерение AS м2
ON
m1.Town = m2.Town
AND m1.Date ГДЕ
m2.MeasurementID НУЛЬ
ЗАКАЗАТЬ на m1.Town


Вам понадобится указатель по городу и дате.

Этот метод особенно полезен для ранних версий MySQL, которые не справляются с более очевидными

ВЫБЕРИТЕ Город, Темп
ОТ измерения AS m1
ГДЕ НЕ СУЩЕСТВУЕТ (
ВЫБЕРИТЕ 1 ИЗ ИЗМЕРЕНИЯ
ГДЕ Город = m1.Town
И Дата> m1.date
)
ЗАКАЗАТЬ ПО ГОРОДУ

1 голос
/ 18 ноября 2008

Рад видеть так много способов снять шкуру с этой кошки. Вот один из них, использующий CTE (вы также можете вложить запрос для большего количества ANSI-изма, но я считаю, что CTE отлично подходит для того, чтобы избежать большого количества отступов, а объявление заранее делает его довольно читабельным сверху вниз и ниже):

WITH LastMeasurements AS (
    SELECT [Town], MAX([Date]) AS LastMeasurementDate
    FROM [Measurement]
    GROUP BY [Town]
)
SELECT [Measurement].Town, [Measurement].[Date], [Measurement].Temp
FROM [Measurement]
INNER JOIN LastMeasurements
    ON [Measurement].[Town] = LastMeasurements.[Town]
    AND [Measurement].[Date] = LastMeasurements.LastMeasurementDate

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

Оптимизатор имеет тенденцию выполнять это довольно быстро на SQL Server - как и большинство решений, если у вас есть индекс для Town, Date, Temp, который будет охватываться и будет работать очень быстро. Даже если это только в Городе, Дата, большая часть работы в GROUP BY может быть выполнена очень быстро.

1 голос
/ 17 ноября 2008
select *
from
(
    select distinct *, --Keyword,Total,CreatedOn,EngineInstanceID,
    Rank() over (PARTITION by Town order by Date DESC) as DateOrder
    from Measurement
    where Town is not null
) CurrentMeasurement
where DateOrder = 1
0 голосов
/ 12 февраля 2012

Возможно, у вас есть таблица с четким списком городов? Учитывая, что у вас есть около 1000 измерений на город, решение оконной функции (такое как row_number (), rank () и т. Д.) Может не работать так же хорошо, как обычное агрегатное решение или эта версия APPLY:

SELECT
   M.*
FROM
   Towns T
   OUTER APPLY (
      SELECT TOP 1 * -- add 'WITH TIES' to the 'TOP 1' if you have/want ties on date.
      FROM Measurement M
      WHERE T.Town = M.Town
      ORDER BY M.Date DESC
   ) M

Если нет списка городов, вы можете попробовать это, хотя я не знаю, как это сложится против простого ванильного агрегата + lookup:

SELECT
   M.*
FROM
   (SELECT DISTINCT Town FROM Towns) T
   OUTER APPLY (
      SELECT TOP 1 *
      FROM Measurement M
      WHERE T.Town = M.Town
      ORDER BY M.Date DESC
   ) M

Производительность этих запросов будет полностью зависеть от индексов. Вам нужен один в [Город] как минимум, а вместо этого [Город, Дата] будет лучше. Если другие таблицы используют MeasurementID, но вы редко обращаетесь к таблице Measurement с помощью MeasurementID, затем отбросьте кластеризованный индекс, сделайте MeasurementID некластеризованным PK и добавьте (неуникальный) кластеризованный индекс для Town, Date. Если у вас нет других таблиц, использующих MeasurementID, то полностью удалите этот столбец - в этом случае это бесполезный синтетический / искусственный ключ, вздувающий вашу таблицу без всякой причины.

Эти рекомендуемые изменения в индексах помогут всем запросам в ответах здесь с использованием агрегатов или APPLY. Не уверен в их влиянии на функции управления окнами, это зависит от того, как оптимизатор разрабатывает планы выполнения (если он достаточно умен, чтобы понять, что ему нужен только доступ к максимальной дате и не касаться всех других строк, тогда тот же индекс повысит его невероятно, хотя я сомневаюсь, что оптимизатор может сделать это).

Кроме того, для повышения производительности я бы наверняка предложил таблицу Town с TownID вместо размещения всего города. Что если название города изменится? Переключение с 15 или около того байтов в среднем для каждого имени на только 4 байта для int TownID поможет скорости. (Хотя тестирование для того, чтобы доказать это наверняка).

0 голосов
/ 17 ноября 2008
select m.town, m.temperature, m.date
from Measurement m
where m.date = (select max(m2.date) from Measurement m2 where m2.town = m.town)
order by 1
0 голосов
/ 17 ноября 2008
select s.*
from Measurement s
where exists ( 
   select 1
   from Measurement s1
   where s.Town = s1.Town
   group by s1.Town
   having max( s1.Date )= s.Date)
   order by s.Town
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...