Рубрики
sql Без рубрики

Нахождение последней записи

Я по работе часто сталкиваюсь с ситуацией, когда нужно восстановить состояние таблицы на какой-то момент в прошлом. Мне всегда казалось, что это довольно простая и очевидная задача, но несколько дней назад я всерьёз над ней задумался и пришёл к выводу, что не всё так очевидно. Мы рассмотрим эту задачу на примере таблицы с отправленными SMS. Мы хотим узнать какого типа было последнее сообщение отправленное на каждый номер.

IDPhoneSMSType
Уникальный монотонно возрастающий идентификатор SMS.Номер телефона на который отправлено сообщениеТип сообщения

Первое решение, которое пришло мне в голову — использовать cross apply, чтобы для каждого уникального номера телефона подзапросом найти соответствующий тип:

;WITH [cte] 
     AS (SELECT [S].[phone] 
         FROM   [dbo].[smslog] AS [S] 
         GROUP  BY [S].[phone]) 
SELECT [ca].[id], 
       [cte].[phone], 
       [ca].[smstype] 
FROM   [cte] 
       CROSS apply (SELECT TOP 1 [ca_s].[id], 
                                 [ca_s].[smstype] 
                    FROM   [dbo].[smslog] AS [ca_s] 
                    WHERE  ( 1 = 1 ) 
                           AND ( [ca_s].[phone] = [cte].[phone] ) 
                    ORDER  BY [ca_s].[id] DESC) AS [ca] 

Довольно очевидным шагом является избавление от CROSS APPLY и замена его на JOIN:

;WITH [cte] 
     AS (SELECT [S].[phone], 
                Max ([S].[id]) AS ‘maxID’ 
         FROM   [dbo].[smslog] AS [S] 
         GROUP  BY [S].[phone]) 
SELECT [S].[id], 
       [S].[phone], 
       [S].[smstype], 
       [cte].[phone], 
       [cte].[maxid] 
FROM   [dbo].[smslog] AS [S] 
       INNER JOIN [cte] 
               ON ( 1 = 1 ) 
                  AND ( [S].[id] = [cte].[maxid] ) 

Даже без использования индексов второй вариант гораздо быстрее. Но, оказывается, есть ещё интересный вариант, который позволяет вообще избежать соединений. Для этого нам придётся прибегнуть к оконной функции:

;WITH [cte] 
     AS (SELECT [S].[id], 
                [S].[phone], 
                [S].[smstype], 
                Row_number () 
                  OVER ( 
                    partition BY [S].[phone] 
                    ORDER BY [S].[id] DESC ) AS ‘rn’ 
         FROM   [dbo].[smslog] AS [S]) 
SELECT [cte].[id], 
       [cte].[phone], 
       [cte].[smstype] 
FROM   [cte] 
WHERE  ( 1 = 1 ) 
       AND ( [cte].[rn] = 1 ) 

Все представленные выше планы запросов получены, когда на таблице нет никаких индексов, но важно понимать, что их добавление может значительно изменить план. Например, после добавления некластеризованного индекса по полям (Phone;ID) и включённым полем smstype в третьем запросе вообще исчезает этап сортировки, а время тратится практически исключительно на просмотр индекса.

Рубрики
blogpost Без рубрики

FaceID и коронавирус

Лично для меня самое большое неудобство, связанное с коронавирусом, — необходимость носить маску. И если к запотевающим очкам и ощущению маски на лице привыкнуть ещё можно, то к неработающему FaceID нельзя.

Рубрики
blogpost Без рубрики

Предположение — мать всех ошибок

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

«For sale, baby shoes, never worn» («Продаются детские ботиночки. Неношеные»)

Негласно подразумевается, что ботиночки не пригодились, потому что с ребёнком что-то случилось. Но, если задуматься, причин может быть множество: например, ребёнку не понравился цвет, обувь оказалась не по размеру, погода не позволила носить эту обувь, да даже просто молодые родители могли не угадать пол ребёнка, когда покупали ботинки.

По большому счёту в самом рассказе нет ничего такого, что должно растрогать читателя. В отсутствие деталей мы вынуждены делать предположение, которое, как известно, ведёт к ошибкам.

Рубрики
blogpost sql Без рубрики

О важности первичных ключей

Сегодня ходил со своей девушкой на один концерт. Непосредственно перед началом выяснилось, что все билеты каким-то образом оказались проданы дважды. Если это не лучшая иллюстрация важности первичных ключей в базе, то что?

Рубрики
blogpost Без рубрики

Порядок против здравого смысла

Я очень люблю фотографировать всё подряд. Во время путешествий я чаще смотрю на мир через экран фотоаппарата, а не своими глазами.

Тысячи полученных фотографий нуждаются в сортировке. Я стараюсь не ограничиваться альбомами и к каждой фотографии добавляю теги, чтобы потом было проще найти нужный объект. Но вот недавно моя любовь к точным тегам привела к том, что фотографии стало искать сложнее.

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