У меня есть таблица, в которой я хочу получить последнюю запись для каждой группы. Вот таблица:
DocumentStatusLogs
Таблица
|ID| DocumentID | Status | DateCreated |
| 2| 1 | S1 | 7/29/2011 |
| 3| 1 | S2 | 7/30/2011 |
| 6| 1 | S1 | 8/02/2011 |
| 1| 2 | S1 | 7/28/2011 |
| 4| 2 | S2 | 7/30/2011 |
| 5| 2 | S3 | 8/01/2011 |
| 6| 3 | S1 | 8/02/2011 |
Таблица будет сгруппирована DocumentID
и отсортирована по DateCreated
убыванию. Для каждого DocumentID
я хочу получить последний статус.
Мой предпочтительный вывод:
| DocumentID | Status | DateCreated |
| 1 | S1 | 8/02/2011 |
| 2 | S3 | 8/01/2011 |
| 3 | S1 | 8/02/2011 |
Есть ли какие-либо агрегатные функции, чтобы получить только верх из каждой группы? Смотрите псевдокод
GetOnlyTheTop
ниже:SELECT DocumentID, GetOnlyTheTop(Status), GetOnlyTheTop(DateCreated) FROM DocumentStatusLogs GROUP BY DocumentID ORDER BY DateCreated DESC
Если такой функции не существует, могу ли я получить желаемый результат?
- Или, во-первых, это может быть вызвано ненормализованной базой данных? Я думаю, поскольку то, что я ищу, это всего лишь одна строка, должна ли она
status
быть также расположена в родительской таблице?
Пожалуйста, смотрите родительскую таблицу для получения дополнительной информации:
Текущая Documents
таблица
| DocumentID | Title | Content | DateCreated |
| 1 | TitleA | ... | ... |
| 2 | TitleB | ... | ... |
| 3 | TitleC | ... | ... |
Должна ли родительская таблица быть такой, чтобы я мог легко получить доступ к ее состоянию?
| DocumentID | Title | Content | DateCreated | CurrentStatus |
| 1 | TitleA | ... | ... | s1 |
| 2 | TitleB | ... | ... | s3 |
| 3 | TitleC | ... | ... | s1 |
ОБНОВЛЕНИЕ Я только что узнал, как использовать «применить», что облегчает решение таких проблем.
Ответы:
Если вы ожидаете 2 входа в день, то это будет произвольно выбрать один. Чтобы получить обе записи за день, используйте DENSE_RANK вместо
Что касается нормализации или нет, это зависит от того, хотите ли вы:
В таком виде вы сохраняете историю статусов. Если вы хотите, чтобы последний статус также был в родительской таблице (который является денормализацией), вам понадобится триггер для поддержания «статуса» в родительской таблице. или удалите эту таблицу истории состояний.
источник
Partition By
?With
для меня тоже новость :( В любом случае я использую mssql 2005.ROW_NUMBER
какой-то подзапрос для каждой строки?Я только что научился пользоваться
cross apply
. Вот как это использовать в этом сценарии:источник
Я провел несколько временных интервалов по различным рекомендациям, приведенным здесь, и результаты действительно зависят от размера используемой таблицы, но наиболее согласованным решением является использование CROSS APPLY. Эти тесты выполнялись на SQL Server 2008-R2 с использованием таблицы с 6500 записей и еще одна (идентичная схема) с 137 миллионами записей. Запрашиваемые столбцы являются частью первичного ключа таблицы, а ширина таблицы очень мала (около 30 байт). Время сообщается SQL Server из фактического плана выполнения.
Я думаю, что действительно удивительным было то, насколько стабильно было время для CROSS APPLY независимо от количества задействованных строк.
источник
Я знаю, что это старая ветка, но
TOP 1 WITH TIES
решения довольно хороши и могут быть полезны при чтении решений.Подробнее о предложении TOP можно узнать здесь .
источник
Если вы беспокоитесь о производительности, вы также можете сделать это с MAX ():
ROW_NUMBER () требует сортировки всех строк в вашем операторе SELECT, а MAX - нет. Должно резко ускорить ваш запрос.
источник
row_number()
даже при правильной индексации. Я считаю это особенно ценным в сценариях самостоятельного объединения. Однако следует помнить, что этот метод часто приводит к большему количеству как логических операций чтения, так и количества сканирований, несмотря на сообщение о низкой стоимости поддерева. Вам нужно будет взвесить затраты / выгоды в вашем конкретном случае, чтобы определить, действительно ли это лучше.Какой сервер базы данных? Этот код не работает на всех из них.
Что касается второй половины вашего вопроса, мне кажется разумным включить статус в качестве столбца. Вы можете оставить
DocumentStatusLogs
в виде журнала, но по-прежнему хранить последнюю информацию в основной таблице.Кстати, если у вас уже есть
DateCreated
столбец в таблице «Документы», вы можете просто присоединиться,DocumentStatusLogs
используя его (покаDateCreated
он уникаленDocumentStatusLogs
).Изменить: MsSQL не поддерживает использование, поэтому измените его на:
источник
max(DateCreated)
Это один из наиболее легко обнаруживаемых вопросов по этой теме, поэтому я хотел дать современный ответ на него (как для справки, так и для помощи другим). Используя
first_value
иover
вы можете сделать короткую работу над запросом выше:Это должно работать в Sql Server 2008 и выше.
First_value
может рассматриваться как способ выполненияSelect Top 1
при использованииover
предложения.Over
позволяет группировать в списке выбора, поэтому вместо написания вложенных подзапросов (как это делают многие из существующих ответов) это делается более читабельно. Надеюсь это поможет.источник
Это довольно старая тема, но я подумал, что добавлю свои два цента точно так же, как принятый ответ не очень хорошо для меня. Я попробовал решение gbn для большого набора данных и обнаружил, что оно ужасно медленное (> 45 секунд на 5 миллионов записей в SQL Server 2012). Глядя на план выполнения, становится очевидным, что проблема в том, что для этого требуется операция SORT, которая значительно замедляет процесс.
Вот альтернатива, которую я извлек из структуры сущностей, которая не требует операции SORT и выполняет поиск по некластерному индексу. Это сокращает время выполнения до <2 секунд для вышеупомянутого набора записей.
Теперь я предполагаю что-то, что не полностью указано в исходном вопросе, но если ваш дизайн таблицы таков, что ваш столбец идентификатора является идентификатором автоинкремента, а DateCreated устанавливается на текущую дату при каждой вставке, то даже без выполнения моего запроса выше вы могли бы получить значительное повышение производительности решения gbn (примерно вдвое меньше времени выполнения), просто упорядочив по идентификатору вместо упорядоченного по DateCreated, поскольку это обеспечит идентичный порядок сортировки и более быструю сортировку.
источник
Мой код для выбора топ 1 из каждой группы
источник
Проверка Клинта удивительным и правильным ответом сверху:
Производительность между двумя запросами ниже интересна. 52% - лучшие. И 48% - второе. Улучшение производительности на 4% при использовании DISTINCT вместо ORDER BY. Но ORDER BY имеет преимущество сортировки по нескольким столбцам.
Опция 1:
Вариант 2:
M $ Management Studio: выделив и выполнив первый блок, выделите «Вариант 1» и «Вариант 2», щелкните правой кнопкой мыши -> [Показать примерный план выполнения]. Затем запустите все это, чтобы увидеть результаты.
Вариант 1 Результаты:
Вариант 2 Результаты:
Замечания:
Я также избегаю подзапросов EXISTS / IN в предложении WHERE или ON, поскольку я испытал это, вызывая некоторые ужасные планы выполнения. Но пробег меняется. Просмотрите план выполнения и профиль производительности, где и когда это необходимо!
источник
Это решение можно использовать для получения TOP N самых последних строк для каждого раздела (в этом примере N - 1 в операторе WHERE, а раздел - doc_id):
источник
Если вы хотите вернуть только последний порядок документов по DateCreated, он вернет только 1 верхний документ по DocumentID
источник
CROSS APPLY
был метод, который я использовал для моего решения, так как он работал для меня и для нужд моих клиентов. И из того, что я прочитал, должно обеспечить наилучшую общую производительность, если их база данных значительно вырастет.источник
Вот 3 отдельных подхода к рассматриваемой проблеме вместе с лучшими вариантами индексации для каждого из этих запросов (пожалуйста, попробуйте сами индексы и посмотрите логическое чтение, истекшее время, план выполнения. Я предоставил предложения из моего опыта по такие запросы без выполнения для этой конкретной проблемы).
Подход 1 : Использование ROW_NUMBER (). Если индекс хранилища строк не может повысить производительность, вы можете попробовать некластеризованный / кластеризованный индекс columnstore как для запросов с агрегацией и группировкой, так и для таблиц, которые всегда упорядочены по разным столбцам, индекс columnstore обычно является лучшим выбором.
Подход 2 : Использование FIRST_VALUE. Если индекс хранилища строк не может повысить производительность, вы можете попробовать некластеризованный / кластеризованный индекс columnstore как для запросов с агрегацией и группировкой, так и для таблиц, которые всегда упорядочены по разным столбцам, индекс columnstore обычно является лучшим выбором.
Подход 3 : Использование CROSS APPLY. Создание индекса хранилища строк в таблице DocumentStatusLogs, охватывающей столбцы, используемые в запросе, должно быть достаточным для покрытия запроса без необходимости использования индекса columnstore.
источник
Я считаю, что это можно сделать так же, как это. Это может потребовать некоторой настройки, но вы можете просто выбрать максимум из группы.
Эти ответы излишни ..
источник
В сценариях, где вы хотите избежать использования row_count (), вы также можете использовать левое соединение:
Для примера схемы вы также можете использовать «не в подзапросе», который обычно компилируется в тот же вывод, что и левое соединение:
Обратите внимание, шаблон подзапроса не будет работать, если в таблице не будет хотя бы одного уникального ключа / ограничения / индекса из одного столбца, в данном случае первичного ключа «Id».
Оба эти запроса, как правило, более «дороги», чем запрос row_count () (по данным Query Analyzer). Однако вы можете столкнуться со сценариями, в которых они возвращают результаты быстрее или включают другие оптимизации.
источник
источник
Попробуй это:
источник
Это самый ванильный TSQL, который я могу придумать
источник
В SQLite проверено, что вы можете использовать следующий простой запрос с GROUP BY
Здесь МАКС помогают получить максимум DateCreated ОТ каждой группы.
Но похоже, что MYSQL не связывает * -колонки со значением max DateCreated :(
источник