Rabee3-F1.787545 Ответов: 3

Выберите идентификатор с максимальной датой из SQL server


TransID	        Date
0000018801	2017-08-29 00:00:00.000
0000025950	2017-09-18 00:00:00.000
0000066677	2017-12-07 00:00:00.000
0000096617	2018-02-05 00:00:00.000
0000181068	2018-08-02 00:00:00.000
0000183129	2018-08-07 00:00:00.000
0000206606	2018-09-27 00:00:00.000
0000215028	2018-10-15 00:00:00.000
0000232571	2018-10-31 00:00:00.000
0000260763	2018-11-21 00:00:00.000
0000259321	2018-12-07 00:00:00.000
0000260762	2018-12-11 00:00:00.000


Select TransID,Date from #RNT1  group by TransID,Date Having Date=max(Date)


Я хочу получить последний идентификатор с максимальной датой "0000260762"

Что я уже пробовал:

<pre>Select TransID,Date from #RNT1  group by TransID,Date Having Date=max(Date)

3 Ответов

Рейтинг:
20

OriginalGriff

Попробуй:

SELECT TOP 1 TransID, [Date] FROM #RNT1 ORDER BY [Date] DESC


Herman&lt;T&gt;.Instance

Производительность мудрый лучший вариант!

MadMyche

На основании чего?

Herman&lt;T&gt;.Instance

Из БД будет взята только последняя запись, так как запрос запрашивает порядок убывания. Несмотря на размеры коллекции. Вы находите максимальное значение в мгновение ока. Макс (по крайней мере, в прошлом) был гарантированным сканированием таблицы. Чем больше коллекция, тем больше времени она занимала.

MadMyche

Нет никакой гарантии, что последняя дата будет указана в последней вставленной записи. Там также нет упомянутых ключей, которые могли бы все это изменить. Хотя Max() может быть легко найти, ORDER BY автоматически генерирует курсор

Herman&lt;T&gt;.Instance

ПОРЯДОК ПО УБЫВАНИЮ. Это гарантия! Если поле индексируется то это не гарантируется

0x01AA

Думаю, что нет. С точки зрения производительности точно так же, как SELECT(MAX([Date]))... как показано в решении 2. оба имеют точно такой же план выполнения.

Herman&lt;T&gt;.Instance

..(по крайней мере, в прошлом)... Наверное, я слишком долго работаю....

Рейтинг:
2

MadMyche

Есть несколько способов, которыми это можно было бы реализовать, я приведу 2 наиболее распространенных способа; вариант 1-использовать Top (1) / Заказать По метод и вариант 2 заключается в использовании Подзапрос с агрегатом метод. Оба они дадут одинаковые правильные результаты.

Вариант 1 легче кодировать; однако он принимает удар по производительности, поскольку он внутренне генерирует курсор для выполнения сортировки. Вариант 2 может быть немного сложнее в программировании, но имеет гораздо меньший вес. стоимость запроса (безразмерная мера общей стоимости, первоначально она была основана на секундах для выполнения... в 1990-е годы)

Я проверил с вашими данными, сначала с TransID как BIGINT, а затем снова как CHAR(10). Результаты были почти идентичны, с QueryCost Option1/Option2 = 2.23. Я также попробовал использовать TransID в качестве первичного ключа и уникальный кластеризованный индекс on (TransID, [Date]). Соотношение запросов изменилось до 2,25 к 1.

Сценарии Тестирования

DECLARE @Rnt1 TABLE (
	TransID	CHAR(10) NOT NULL ,
	[Date]	Date NULL
)

INSERT @Rnt1
VALUES	('0000018801','2017/08/29')
,		('0000025950','2017/09/18')
,		('0000066677','2017/12/07')
,		('0000096617','2018/02/05')
,		('0000181068','2018/08/02')
,		('0000183129','2018/08/07')
,		('0000206606','2018/09/27')
,		('0000215028','2018/10/15')
,		('0000232571','2018/10/31')
,		('0000260763','2018/11/21')
,		('0000259321','2018/12/07')
,		('0000260762','2018/12/11')

-- [=====[ Option 1 ]=====]
SELECT	TOP 1 TransID, [Date] 
FROM		@RNT1 
ORDER BY	[Date] DESC

-- [=====[ Option 2 ]=====]
SELECT	TransID,[Date]
FROM		@RNT1
WHERE	[Date]= (SELECT Max([Date]) FROM @Rnt1)


0x01AA

Оба варианта имеют, по крайней мере, один и тот же план выполнения. Поэтому я предполагаю, что они одинаково эффективны. Во всяком случае, 5.

Wendelius

Согласитесь, есть много способов сделать это, но эти два, вероятно, самые простые и эффективные.

Рейтинг:
0

Wendelius

I want to get the last id with max date

Из вашего вопроса у меня сложилось впечатление, что одна дата может иметь несколько строк, содержащих разные значения в TransId. Если это так, то вы должны убедиться, что действительно извлекаете самое высокое значение идентификатора. Для этого вам нужно добавить дополнительные условия или сортировку, чтобы выбрать правильное значение.

Модифицированный из запросов, уже предоставленных в предыдущем посте, вы можете использовать что-то вроде
-- [=====[ Option 1 ]=====]
SELECT	TOP 1 
        TransID, [Date] 
FROM    #RNT1 
ORDER BY [Date] DESC, 
         TransID DESC

-- [=====[ Option 2 ]=====]
SELECT TOP 1 
       TransID, [Date]
FROM   #RNT1
WHERE  [Date]= (SELECT Max([Date]) FROM #RNT1)
ORDER BY TransID DESC


Чтобы добавить некоторые данные в разговор о производительности, вы можете создать несколько тестовых строк:
CREATE TABLE #RNT1 (
	TransID	CHAR(10) NOT NULL ,
	[Date]	Date NULL
)

DECLARE  @counter int
BEGIN
   SET @counter = 1;
   WHILE @counter < 10000000 
   BEGIN
		INSERT INTO #RNT1 VALUES (RAND()*1000000, GETDATE()-RAND()*500);
		SET @counter = @counter + 1;
   END;
END;

Если вы исследуете планы, они выглядят совсем по-другому
Option 1
--------
|--Sort(TOP 1, ORDER BY:([tempdb].[dbo].[#RNT1].[Date] DESC, [tempdb].[dbo].[#RNT1].[TransID] DESC))
     |--Table Scan(OBJECT:([tempdb].[dbo].[#RNT1]))

Option 2
--------
|--Sort(TOP 1, ORDER BY:([tempdb].[dbo].[#RNT1].[TransID] DESC))
     |--Nested Loops(Inner Join, WHERE:([Expr1006]=[tempdb].[dbo].[#RNT1].[Date]))
          |--Stream Aggregate(DEFINE:([Expr1006]=MAX([tempdb].[dbo].[#RNT1].[Date])))
          |    |--Table Scan(OBJECT:([tempdb].[dbo].[#RNT1]))
          |--Table Scan(OBJECT:([tempdb].[dbo].[#RNT1]))

Итак, каков же эффект на практике, когда тестовые случаи, сгенерированные выше, выполняются почти одинаково

Option 1
--------
Table '#RNT1'. Scan count 1, logical reads 28572, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 1515 ms,  elapsed time = 1525 ms.

Option 2
--------
SQL Server parse and compile time: 
   CPU time = 0 ms, elapsed time = 1 ms.
Table '#RNT1'. Scan count 2, logical reads 57144, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 1594 ms,  elapsed time = 1593 ms.

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

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


0x01AA

А 5 для вашего сведения. Для меня "[=====[ Вариант 2 ]=====]" не имеет смысла в двух точках:
а.) выберите верх
б.) заказать по

С моей точки зрения, "WHERE [Date]= (SELECT Max([Date]) FROM #RNT1)" достаточно. Где я ошибаюсь, чего я здесь не понимаю? Заранее спасибо.

[Редактировать]
Выберите TOP теперь я вижу причину...

Wendelius

Спасибо :)

Вы уже заметили причину для вершины но для других которые могут задаться тем же вопросом:

В варианте 2 Порядок выполняется для поля вторичной сортировки. Предложение WHERE выбирает строки только для одного дня, в то время как ORDER BY + TOP вызывает возврат самого высокого TransId в течение этого дня .