Получить 1 верхний ряд каждой группы

530

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

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
Для более подробного обсуждения и сравнения возможных решений я рекомендую прочитать аналогичный вопрос на dba.se: получение n строк на группу .
Владимир Баранов
Я посмотрел на пост и попробовал. Использование группы по StoreID сгенерировало ошибку.
UltraJ

Ответы:

757
;WITH cte AS
(
   SELECT *,
         ROW_NUMBER() OVER (PARTITION BY DocumentID ORDER BY DateCreated DESC) AS rn
   FROM DocumentStatusLogs
)
SELECT *
FROM cte
WHERE rn = 1

Если вы ожидаете 2 входа в день, то это будет произвольно выбрать один. Чтобы получить обе записи за день, используйте DENSE_RANK вместо

Что касается нормализации или нет, это зависит от того, хотите ли вы:

  • поддерживать статус в 2 местах
  • сохранить историю статуса
  • ...

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

ГБН
источник
5
И ... что это Partition By? Withдля меня тоже новость :( В любом случае я использую mssql 2005.
dpp
6
@domanokz: Partition By сбрасывает счет. Так что в этом случае он говорит, чтобы рассчитывать на DocumentID
gbn
1
Хм, я беспокоюсь о производительности, я буду запрашивать миллионы строк. Влияет ли SELECT * FROM (SELECT ...) на производительность? Кроме того, это ROW_NUMBERкакой-то подзапрос для каждой строки?
ДПП
1
@domanokz: нет, это не подзапрос. Если у вас правильные индексы, миллионы не должны быть проблемой. В любом случае, существует только 2 способа на основе множеств: это и совокупность (решение Ариэль). Так что попробуйте их обоих ...
ГБН
1
@domanokz: Просто измените ORDER BY DateCreated DESC на ORDER BY ID DESC
gbn
184

Я только что научился пользоваться cross apply. Вот как это использовать в этом сценарии:

 select d.DocumentID, ds.Status, ds.DateCreated 
 from Documents as d 
 cross apply 
     (select top 1 Status, DateCreated
      from DocumentStatusLogs 
      where DocumentID = d.DocumentId
      order by DateCreated desc) as ds
ДПП
источник
2
Это на самом деле не имеет значения, так как проблема все еще решается.
ДПП
19
Я только что опубликовал результаты своих временных тестов по всем предложенным решениям, и ваши вышли на первое место. Голосование за вас :-)
Джон Фэрбенкс
3
+1 за огромное улучшение скорости. Это намного быстрее, чем оконная функция, такая как ROW_NUMBER (). Было бы неплохо, если бы SQL распознавал ROW_NUMBER () = 1 как запросы и оптимизировал их для применения. Примечание. Я использовал OUTER APPLY для получения необходимых результатов, даже если их не было в заявке.
TamusJRoyce
8
@TamusJRoyce вы не можете экстраполировать это только потому, что это было быстрее, если это всегда так. Это зависит. Как описано здесь sqlmag.com/database-development/optimizing-top-n-group-queries
Мартин Смит
2
Мой комментарий о наличии нескольких строк и о желании только одной из этих нескольких строк на группу. Соединения предназначены для тех, кто хочет один ко многим. Применяется для случаев, когда у вас есть один ко многим, но вы хотите отфильтровать все, кроме одного к одному. Сценарий: для 100 участников, дайте мне свой лучший номер телефона (где у каждого может быть несколько номеров). Это где Apply превосходит. Меньше чтений = меньше доступа к диску = лучшая производительность. Учитывая мой опыт работы с плохо спроектированными ненормализованными базами данных.
TamusJRoyce
53

Я провел несколько временных интервалов по различным рекомендациям, приведенным здесь, и результаты действительно зависят от размера используемой таблицы, но наиболее согласованным решением является использование CROSS APPLY. Эти тесты выполнялись на SQL Server 2008-R2 с использованием таблицы с 6500 записей и еще одна (идентичная схема) с 137 миллионами записей. Запрашиваемые столбцы являются частью первичного ключа таблицы, а ширина таблицы очень мала (около 30 байт). Время сообщается SQL Server из фактического плана выполнения.

Query                                  Time for 6500 (ms)    Time for 137M(ms)

CROSS APPLY                                    17.9                17.9
SELECT WHERE col = (SELECT MAX(COL)…)           6.6               854.4
DENSE_RANK() OVER PARTITION                     6.6               907.1

Я думаю, что действительно удивительным было то, насколько стабильно было время для CROSS APPLY независимо от количества задействованных строк.

Джон Фэрбенкс
источник
8
Все зависит от распределения данных и доступных индексов. Он обсуждался на больших длинах на dba.se .
Владимир Баранов
48

Я знаю, что это старая ветка, но TOP 1 WITH TIESрешения довольно хороши и могут быть полезны при чтении решений.

select top 1 with ties
   DocumentID
  ,Status
  ,DateCreated
from DocumentStatusLogs
order by row_number() over (partition by DocumentID order by DateCreated desc)

Подробнее о предложении TOP можно узнать здесь .

Джош Гилфиллан
источник
7
Это самое элегантное решение ИМО
Джордж Менутис
1
согласился - это лучше всего повторяет то, что очень легко сделать в других версиях SQL и других языках imo
Крис Умфлетт
27

Если вы беспокоитесь о производительности, вы также можете сделать это с MAX ():

SELECT *
FROM DocumentStatusLogs D
WHERE DateCreated = (SELECT MAX(DateCreated) FROM DocumentStatusLogs WHERE ID = D.ID)

ROW_NUMBER () требует сортировки всех строк в вашем операторе SELECT, а MAX - нет. Должно резко ускорить ваш запрос.

Дэниел Коттер
источник
2
Нельзя ли решить проблемы производительности с ROW_NUMBER () с помощью правильной индексации? (Я чувствую, что это должно быть сделано в любом случае)
Кристоффер Л
8
С datetime вы не можете гарантировать, что две записи не будут добавлены в одну и ту же дату и время. Точность недостаточно высока.
TamusJRoyce
+1 для простоты. @TamusJRoyce прав. Что о? 'select * from DocumentStatusLog D, где ID = (выберите ID из DocumentsStatusLog, где D.DocumentID = Порядок DocumentID по DateCreated DESC, предел 1);'
cibercitizen1
SELECT * FROM EventScheduleTbl D WHERE DatesPicked = (ВЫБЕРИТЕ топ 1 мин (DatesPicked) ОТ EventScheduleTbl WHERE EventIDf = D.EventIDf и DatesPicked> = convert (date, getdate ()))
Арун Прасад ES
Есть определенно случаи, когда это будет превосходить row_number()даже при правильной индексации. Я считаю это особенно ценным в сценариях самостоятельного объединения. Однако следует помнить, что этот метод часто приводит к большему количеству как логических операций чтения, так и количества сканирований, несмотря на сообщение о низкой стоимости поддерева. Вам нужно будет взвесить затраты / выгоды в вашем конкретном случае, чтобы определить, действительно ли это лучше.
pimbrouwers
26
SELECT * FROM
DocumentStatusLogs JOIN (
  SELECT DocumentID, MAX(DateCreated) DateCreated
  FROM DocumentStatusLogs
  GROUP BY DocumentID
  ) max_date USING (DocumentID, DateCreated)

Какой сервер базы данных? Этот код не работает на всех из них.

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

Кстати, если у вас уже есть DateCreatedстолбец в таблице «Документы», вы можете просто присоединиться, DocumentStatusLogsиспользуя его (пока DateCreatedон уникален DocumentStatusLogs).

Изменить: MsSQL не поддерживает использование, поэтому измените его на:

ON DocumentStatusLogs.DocumentID = max_date.DocumentID AND DocumentStatusLogs.DateCreated = max_date.DateCreated
Ariel
источник
5
Подсказка была в заголовке: MSSQL. SQL Server не имеет ИСПОЛЬЗОВАНИЯ, но идея в порядке.
Гбн
7
@gbn Глупые модераторы обычно удаляют важные ключевые слова из заголовков, как они это сделали здесь. Это затрудняет поиск правильных ответов в результатах поиска или в Google.
Ник
2
max(DateCreated)
Я
12

Это один из наиболее легко обнаруживаемых вопросов по этой теме, поэтому я хотел дать современный ответ на него (как для справки, так и для помощи другим). Используя first_valueи overвы можете сделать короткую работу над запросом выше:

Select distinct DocumentID
  , first_value(status) over (partition by DocumentID order by DateCreated Desc) as Status
  , first_value(DateCreated) over (partition by DocumentID order by DateCreated Desc) as DateCreated
From DocumentStatusLogs

Это должно работать в Sql Server 2008 и выше. First_valueможет рассматриваться как способ выполнения Select Top 1при использовании overпредложения. Overпозволяет группировать в списке выбора, поэтому вместо написания вложенных подзапросов (как это делают многие из существующих ответов) это делается более читабельно. Надеюсь это поможет.

Randall
источник
2
Это не работает в SQL Server 2008 R2. Я думаю, что first_value был введен в 2012 году!
НЛО,
1
Очень быстро! Я использовал решение Cross Apply, предлагаемое @dpp, но оно намного быстрее.
MattSlay
11

Это довольно старая тема, но я подумал, что добавлю свои два цента точно так же, как принятый ответ не очень хорошо для меня. Я попробовал решение gbn для большого набора данных и обнаружил, что оно ужасно медленное (> 45 секунд на 5 миллионов записей в SQL Server 2012). Глядя на план выполнения, становится очевидным, что проблема в том, что для этого требуется операция SORT, которая значительно замедляет процесс.

Вот альтернатива, которую я извлек из структуры сущностей, которая не требует операции SORT и выполняет поиск по некластерному индексу. Это сокращает время выполнения до <2 секунд для вышеупомянутого набора записей.

SELECT 
[Limit1].[DocumentID] AS [DocumentID], 
[Limit1].[Status] AS [Status], 
[Limit1].[DateCreated] AS [DateCreated]
FROM   (SELECT DISTINCT [Extent1].[DocumentID] AS [DocumentID] FROM [dbo].[DocumentStatusLogs] AS [Extent1]) AS [Distinct1]
OUTER APPLY  (SELECT TOP (1) [Project2].[ID] AS [ID], [Project2].[DocumentID] AS [DocumentID], [Project2].[Status] AS [Status], [Project2].[DateCreated] AS [DateCreated]
    FROM (SELECT 
        [Extent2].[ID] AS [ID], 
        [Extent2].[DocumentID] AS [DocumentID], 
        [Extent2].[Status] AS [Status], 
        [Extent2].[DateCreated] AS [DateCreated]
        FROM [dbo].[DocumentStatusLogs] AS [Extent2]
        WHERE ([Distinct1].[DocumentID] = [Extent2].[DocumentID])
    )  AS [Project2]
    ORDER BY [Project2].[ID] DESC) AS [Limit1]

Теперь я предполагаю что-то, что не полностью указано в исходном вопросе, но если ваш дизайн таблицы таков, что ваш столбец идентификатора является идентификатором автоинкремента, а DateCreated устанавливается на текущую дату при каждой вставке, то даже без выполнения моего запроса выше вы могли бы получить значительное повышение производительности решения gbn (примерно вдвое меньше времени выполнения), просто упорядочив по идентификатору вместо упорядоченного по DateCreated, поскольку это обеспечит идентичный порядок сортировки и более быструю сортировку.

Clint
источник
5

Мой код для выбора топ 1 из каждой группы

выберите. * из #DocumentStatusLogs где 
 дата, созданная в (выберите 1 верхнюю дату, созданную из #DocumentStatusLogs b
где 
a.documentid = b.documentid
упорядочить по дате создания
)
AnuPrakash
источник
3

Проверка Клинта удивительным и правильным ответом сверху:

Производительность между двумя запросами ниже интересна. 52% - лучшие. И 48% - второе. Улучшение производительности на 4% при использовании DISTINCT вместо ORDER BY. Но ORDER BY имеет преимущество сортировки по нескольким столбцам.

IF (OBJECT_ID('tempdb..#DocumentStatusLogs') IS NOT NULL) BEGIN DROP TABLE #DocumentStatusLogs END

CREATE TABLE #DocumentStatusLogs (
    [ID] int NOT NULL,
    [DocumentID] int NOT NULL,
    [Status] varchar(20),
    [DateCreated] datetime
)

INSERT INTO #DocumentStatusLogs([ID], [DocumentID], [Status], [DateCreated]) VALUES (2, 1, 'S1', '7/29/2011 1:00:00')
INSERT INTO #DocumentStatusLogs([ID], [DocumentID], [Status], [DateCreated]) VALUES (3, 1, 'S2', '7/30/2011 2:00:00')
INSERT INTO #DocumentStatusLogs([ID], [DocumentID], [Status], [DateCreated]) VALUES (6, 1, 'S1', '8/02/2011 3:00:00')
INSERT INTO #DocumentStatusLogs([ID], [DocumentID], [Status], [DateCreated]) VALUES (1, 2, 'S1', '7/28/2011 4:00:00')
INSERT INTO #DocumentStatusLogs([ID], [DocumentID], [Status], [DateCreated]) VALUES (4, 2, 'S2', '7/30/2011 5:00:00')
INSERT INTO #DocumentStatusLogs([ID], [DocumentID], [Status], [DateCreated]) VALUES (5, 2, 'S3', '8/01/2011 6:00:00')
INSERT INTO #DocumentStatusLogs([ID], [DocumentID], [Status], [DateCreated]) VALUES (6, 3, 'S1', '8/02/2011 7:00:00')

Опция 1:

    SELECT
    [Extent1].[ID], 
    [Extent1].[DocumentID],
    [Extent1].[Status], 
    [Extent1].[DateCreated]
FROM #DocumentStatusLogs AS [Extent1]
    OUTER APPLY (
        SELECT TOP 1
            [Extent2].[ID], 
            [Extent2].[DocumentID],
            [Extent2].[Status], 
            [Extent2].[DateCreated]
        FROM #DocumentStatusLogs AS [Extent2]
        WHERE [Extent1].[DocumentID] = [Extent2].[DocumentID]
        ORDER BY [Extent2].[DateCreated] DESC, [Extent2].[ID] DESC
    ) AS [Project2]
WHERE ([Project2].[ID] IS NULL OR [Project2].[ID] = [Extent1].[ID])

Вариант 2:

SELECT 
    [Limit1].[DocumentID] AS [ID], 
    [Limit1].[DocumentID] AS [DocumentID], 
    [Limit1].[Status] AS [Status], 
    [Limit1].[DateCreated] AS [DateCreated]
FROM (
    SELECT DISTINCT [Extent1].[DocumentID] AS [DocumentID] FROM #DocumentStatusLogs AS [Extent1]
) AS [Distinct1]
    OUTER APPLY  (
        SELECT TOP (1) [Project2].[ID] AS [ID], [Project2].[DocumentID] AS [DocumentID], [Project2].[Status] AS [Status], [Project2].[DateCreated] AS [DateCreated]
        FROM (
            SELECT 
                [Extent2].[ID] AS [ID], 
                [Extent2].[DocumentID] AS [DocumentID], 
                [Extent2].[Status] AS [Status], 
                [Extent2].[DateCreated] AS [DateCreated]
            FROM #DocumentStatusLogs AS [Extent2]
            WHERE [Distinct1].[DocumentID] = [Extent2].[DocumentID]
        )  AS [Project2]
        ORDER BY [Project2].[ID] DESC
    ) AS [Limit1]

M $ Management Studio: выделив и выполнив первый блок, выделите «Вариант 1» и «Вариант 2», щелкните правой кнопкой мыши -> [Показать примерный план выполнения]. Затем запустите все это, чтобы увидеть результаты.

Вариант 1 Результаты:

ID  DocumentID  Status  DateCreated
6   1   S1  8/2/11 3:00
5   2   S3  8/1/11 6:00
6   3   S1  8/2/11 7:00

Вариант 2 Результаты:

ID  DocumentID  Status  DateCreated
6   1   S1  8/2/11 3:00
5   2   S3  8/1/11 6:00
6   3   S1  8/2/11 7:00

Замечания:

Я склонен использовать APPLY, когда хочу, чтобы соединение было 1-к- (1 из многих).

Я использую JOIN, если хочу, чтобы соединение было 1-ко-многим или многим-ко-многим.

Я избегаю CTE с ROW_NUMBER (), если мне не нужно делать что-то более сложное, и я согласен с потерей производительности окон.

Я также избегаю подзапросов EXISTS / IN в предложении WHERE или ON, поскольку я испытал это, вызывая некоторые ужасные планы выполнения. Но пробег меняется. Просмотрите план выполнения и профиль производительности, где и когда это необходимо!

TamusJRoyce
источник
3

Это решение можно использовать для получения TOP N самых последних строк для каждого раздела (в этом примере N - 1 в операторе WHERE, а раздел - doc_id):

SELECT doc_id, status, date_created FROM 
(
    SELECT a.*, ROW_NUMBER() OVER (PARTITION BY doc_id ORDER BY date_created DESC) AS rnk FROM doc a
)
WHERE rnk = 1;
Praveen
источник
2
SELECT o.*
FROM `DocumentStatusLogs` o                   
  LEFT JOIN `DocumentStatusLogs` b                   
  ON o.DocumentID = b.DocumentID AND o.DateCreated < b.DateCreated
 WHERE b.DocumentID is NULL ;

Если вы хотите вернуть только последний порядок документов по DateCreated, он вернет только 1 верхний документ по DocumentID

чо
источник
2

CROSS APPLYбыл метод, который я использовал для моего решения, так как он работал для меня и для нужд моих клиентов. И из того, что я прочитал, должно обеспечить наилучшую общую производительность, если их база данных значительно вырастет.

Тони Дэвис-Койл
источник
1

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

Подход 1 : Использование ROW_NUMBER (). Если индекс хранилища строк не может повысить производительность, вы можете попробовать некластеризованный / кластеризованный индекс columnstore как для запросов с агрегацией и группировкой, так и для таблиц, которые всегда упорядочены по разным столбцам, индекс columnstore обычно является лучшим выбором.

;WITH CTE AS
    (
       SELECT   *,
                RN = ROW_NUMBER() OVER (PARTITION BY DocumentID ORDER BY DateCreated DESC)
       FROM     DocumentStatusLogs
    )
    SELECT  ID      
        ,DocumentID 
        ,Status     
        ,DateCreated
    FROM    CTE
    WHERE   RN = 1;

Подход 2 : Использование FIRST_VALUE. Если индекс хранилища строк не может повысить производительность, вы можете попробовать некластеризованный / кластеризованный индекс columnstore как для запросов с агрегацией и группировкой, так и для таблиц, которые всегда упорядочены по разным столбцам, индекс columnstore обычно является лучшим выбором.

SELECT  DISTINCT
    ID      = FIRST_VALUE(ID) OVER (PARTITION BY DocumentID ORDER BY DateCreated DESC)
    ,DocumentID
    ,Status     = FIRST_VALUE(Status) OVER (PARTITION BY DocumentID ORDER BY DateCreated DESC)
    ,DateCreated    = FIRST_VALUE(DateCreated) OVER (PARTITION BY DocumentID ORDER BY DateCreated DESC)
FROM    DocumentStatusLogs;

Подход 3 : Использование CROSS APPLY. Создание индекса хранилища строк в таблице DocumentStatusLogs, охватывающей столбцы, используемые в запросе, должно быть достаточным для покрытия запроса без необходимости использования индекса columnstore.

SELECT  DISTINCT
    ID      = CA.ID
    ,DocumentID = D.DocumentID
    ,Status     = CA.Status 
    ,DateCreated    = CA.DateCreated
FROM    DocumentStatusLogs D
    CROSS APPLY (
            SELECT  TOP 1 I.*
            FROM    DocumentStatusLogs I
            WHERE   I.DocumentID = D.DocumentID
            ORDER   BY I.DateCreated DESC
            ) CA;
Сан -
источник
1

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

Эти ответы излишни ..

SELECT
  d.DocumentID,
  MAX(d.Status),
  MAX(d1.DateCreated)
FROM DocumentStatusLogs d, DocumentStatusLogs d1
USING(DocumentID)
GROUP BY d.DocumentID
ORDER BY DateCreated DESC
Изучение статистики на примере
источник
0

В сценариях, где вы хотите избежать использования row_count (), вы также можете использовать левое соединение:

select ds.DocumentID, ds.Status, ds.DateCreated 
from DocumentStatusLogs ds
left join DocumentStatusLogs filter 
    ON ds.DocumentID = filter.DocumentID
    -- Match any row that has another row that was created after it.
    AND ds.DateCreated < filter.DateCreated
-- then filter out any rows that matched 
where filter.DocumentID is null 

Для примера схемы вы также можете использовать «не в подзапросе», который обычно компилируется в тот же вывод, что и левое соединение:

select ds.DocumentID, ds.Status, ds.DateCreated 
from DocumentStatusLogs ds
WHERE ds.ID NOT IN (
    SELECT filter.ID 
    FROM DocumentStatusLogs filter
    WHERE ds.DocumentID = filter.DocumentID
        AND ds.DateCreated < filter.DateCreated)

Обратите внимание, шаблон подзапроса не будет работать, если в таблице не будет хотя бы одного уникального ключа / ограничения / индекса из одного столбца, в данном случае первичного ключа «Id».

Оба эти запроса, как правило, более «дороги», чем запрос row_count () (по данным Query Analyzer). Однако вы можете столкнуться со сценариями, в которых они возвращают результаты быстрее или включают другие оптимизации.

BitwiseMan
источник
0
SELECT documentid, 
       status, 
       datecreated 
FROM   documentstatuslogs dlogs 
WHERE  status = (SELECT status 
                 FROM   documentstatuslogs 
                 WHERE  documentid = dlogs.documentid 
                 ORDER  BY datecreated DESC 
                 LIMIT  1) 
Кошал Гарг
источник
0

Попробуй это:

SELECT [DocumentID]
    ,[tmpRez].value('/x[2]', 'varchar(20)') AS [Status]
    ,[tmpRez].value('/x[3]', 'datetime') AS [DateCreated]
FROM (
    SELECT [DocumentID]
        ,cast('<x>' + max(cast([ID] AS VARCHAR(10)) + '</x><x>' + [Status] + '</x><x>' + cast([DateCreated] AS VARCHAR(20))) + '</x>' AS XML) AS [tmpRez]
    FROM DocumentStatusLogs
    GROUP BY DocumentID
    ) AS [tmpQry]
ГНГ
источник
Вы всегда должны описывать свой оператор SQL, как он будет работать, и решать запрос OP.
Сурадж Кумар
-1

Это самый ванильный TSQL, который я могу придумать

    SELECT * FROM DocumentStatusLogs D1 JOIN
    (
      SELECT
        DocumentID,MAX(DateCreated) AS MaxDate
      FROM
        DocumentStatusLogs
      GROUP BY
        DocumentID
    ) D2
    ON
      D2.DocumentID=D1.DocumentID
    AND
      D2.MaxDate=D1.DateCreated
богатый с
источник
К сожалению, MaxDate не уникален. Можно ввести две даты в одно и то же время. Так что это может привести к дубликатам на группу. Однако вы можете использовать столбец идентификаторов или GUID. Столбец идентификаторов даст вам последний введенный файл (используется расчет идентификатора по умолчанию, 1 ... x шаг 1).
TamusJRoyce
Ну я вроде согласен, но автор просил последнюю запись - которые , если не включать автоинкрементные средства столбца идентификаторов два элемента добавлен в одно и то же время одинаково «последний»
богатые с
Последняя запись будет одной записью. Так да. Вам необходимо рассмотреть столбец идентификаторов с автоинкрементом.
TamusJRoyce
-2

В SQLite проверено, что вы можете использовать следующий простой запрос с GROUP BY

SELECT MAX(DateCreated), *
FROM DocumentStatusLogs
GROUP BY DocumentID

Здесь МАКС помогают получить максимум DateCreated ОТ каждой группы.

Но похоже, что MYSQL не связывает * -колонки со значением max DateCreated :(

Malex
источник