Запрос умирает при сравнении двух дат


10 0

Писал запрос для банковской базы с классическими транзакциями кредита – дебита. Прибыл кредит, его можно потратить. Дебитовая транзакция связывается с кредитовой, чтобы было видно, какая кредитовая транзакция была потрачена. Запрос выглядит примерно так:

select credit.TxnID, credit.Expiry, debit.Transacted, debit.Amount
from credit
   inner join creditdebit on . . .
   inner join debit on . . .
where debit.Transacred > '2012.01.01' 
   and debit.Transacted < ''2012.01.02

Это образная копия того запроса, над которым я работал. Все имена таблиц изменены, но смысл примерно сохранен. Смысл в том, что запрос возвращает две даты – дата Expiry для кредитной транзакции и дата, когда были потрачены деньги. Все это выбирается за один день и запрос выполняется одну секунду и возвращал 50 строк.

И тут я решил сделать одно дополнительное условие:

select credit.TxnID, credit.Expiry, debit.Transacted, debit.Amount
from credit
   inner join creditdebit on . . .
   inner join debit on . . .
where debit.Transacred > '2012.01.01' 
   and debit.Transacted < ''2012.01.02
   and credit.Expiry < debit.Transacted

Я добавил всего одну дополнительную проверку credit.Expiry < debit.Transacted на поля, которые уже были в результате запроса, они выбирались без проблем, потому что были в индексе. Но это небольшое условие убило запрос наповал. Я ждал 10 минут, когда он завершит выполнение, но безуспешно. Вот честно, не понял логики SQL Server, а точнее его оптимизатора запросов.

Я пробился над этим запросом весь день, пытался делать вложенные запросы типа:

seelct * from
(
select credit.TxnID, credit.Expiry, debit.Transacted, debit.Amount
from credit
   inner join creditdebit on . . .
   inner join debit on . . .
where debit.Transacred > '2012.01.01' 
   and debit.Transacted < ''2012.01.02
) ttt
where ttt.Expiry < ttt.Transacted

Но SQL Server все равно выбирал какой-то идиотский план выполнения и запрос умирал. Единственный выход, который я нашел – выбирать все данные во временную таблицу, а потом уже выбирать из временной таблицы данные, где Expiry < Transacted. Только такой вариант выполнялся одну секунду.

Никогда с подобным не сталкивался. Если SQL Server выбирал где0то неправильный план выполнения, то просто перестраиваешь запрос, делаешь какое-то изменение, чтобы сервер понял, что он идиот и всегда работало. Но тут я просто сдался.

К чему я это? Я просто ненавижу временные таблицы и считаю их злом. Они приносят реальную пользу очень и очень редко.


Понравилось? Кликни Лайк, чтобы я знал, какой контент более интересен читателям. Заметку пока еще никто не лайкал и ты можешь быть первым


Комментарии

Максим

23 Февраля 2012

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


XHelp

23 Февраля 2012

В кода опечатка: в debit.Transacted дата вне кавычек.

А у тебя в фирме никаких профайлеров для запросов не используют? Было-бы интересно узнать что он по этому поводу скажет. Когда-то видел работу советы профайлера (помойму от oracle) на большом data-warehouse, так советы были довольно дельные (хоть и не все).


Евгений

24 Февраля 2012

Михаил, а как вы относитесь к технологии LINQ для получения данных из базы. И пользуете ли её в работе, на основном месте или в своих собственных проектах. А так же как вы считаете стоит ли её изучать и вообще имеет ли смысл её использовать на практике.


Михаил Фленов

24 Февраля 2012

Когда LINQ только появился, я еще тогда сказал, что это дерьмо и использовать его никто не будет. Изучать можно, но использовать не стоит.


klamm

25 Февраля 2012

LINQ часто используется на олимпиадах по спортивному программированию. Российская команда из Ижевска, Чемпионы Мира кстати, им точно пользуется.


Михаил Фленов

25 Февраля 2012

Да пусть хоть президент им пользуется, я не собираюсь


Михаил Фленов

02 Марта 2012

2BOBA

Ты не правильно закрыл b тэг, поэтому твой комментарий удаляю, но копирую текст сюда:


Если не секрет, каков твой опыт работы с MS SQL Server? select credit.TxnID, credit.Expiry, debit.Transacted, debit.Amount from credit inner join creditdebit on . . . inner join debit on (. . . (and credit.Expiry &lt; debit.Transacted) ) where debit.Transacred &gt; '2012.01.01' and debit.Transacted &lt; '2012.01.02'


Отвечаю сначала на первый вопрос - опыт работы дохренилетльный. Теперь по поводу того, что ты написал - ты думаешь от того, что ты перенес сравнение credit.Expiry &lt; debit.Transacted в блок on план выполнения изменится? SQL сервер иногда тупит, но не до такой степени.


BOBA

02 Марта 2012

Я обычно where пишу к from.  Остальное - в join.


Михаил Фленов

02 Марта 2012

Я не знаю, как обычно пишут, а для SQL сервера пофиг. Но спасибо, ты подал хорошую идею для заметки, которую я уже накатал. Реально будет полезно знать тем, у кого опыта работы с сервером не много.


BOBA

02 Марта 2012

Извини, сейчас не могу САМ проверить план выполнения запроса - нет сервера под рукой. А тэги - отвык без клавы :(


Добавить Комментарий

Еще что-нибудь

Хотите найти еще что-то интересное почитать? Можно попробовать отфильтровать заметки на блоге по категориям.

О блоге

Программист, автор нескольких книг серии глазами хакера и просто блогер. Интересуюсь безопасностью, хотя хакером себя не считаю

Обратная связь

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

Пишите мне