Страница 27 из 38 Прокрутка датасетов-таблиц Допустим, что мы открыли таблицу и листаем её в заданном направлении. Перед BDE встаёт задача: есть текущая запись и нужно найти следующую (предыдущую) в заданном порядке. На уровне SQL-запросов, идущих в сервер, листание превращается в запросы (предполагаем, что для таблицы назначена сортировка по одному полю и листание идёт вперёд): select имена_всех_полей
from таблица
where ключ is null or ключ > ключ_текущей_записи
order by ключ asc Особенности: - Всегда читаются все поля запрошенной таблицы, независимо от того, нужны они приложению или нет.
- При обратном направлении прокрутки меняются направления сортировки и сравнения.
- Ключ всегда проверяется на null, даже если он в метаданных БД прописан, как not null. InterBase при любых order by всегда выдаёт записи с null последними, так что "нормальную" сортировку это не портит, хотя и тормозит.
- BDE, как правило, вычитывает из этого запроса не все записи, а столько, на сколько пользователь пролистал таблицу в данном направлении.
- Комбинация where и order by приводит к тому, что записи внутри сервера сортируются физически, причём все, попадающие под условие, несмотря на предыдущий пункт. Так что на больших таблицах такая прокрутка в принципе не может быть эффективной. Об этом и последующих эффектах писалось в планировании запросов.
- Прокрутка "в начало" или "в конец" приводит к примерно аналогичным запросам с немного другой формулировкой, но не менее тормозным.
- Сортировка по двум полям (ключ1;ключ2) приводит к условию поиска вида "where (ключ1 > ключ1_текущей_записи) or ((ключ1 = ключ1_текущей_записи or ключ1 is null) and (ключ2 > ключ2_текущей записи or ключ2 is null))". Суть в том, что проверяются все возможные комбинации сравнения ключей и наличия null'ов в них, чтобы сгенерировать все записи которые либо "точно больше" текущей, либо "возможно больше". При трёх полях запрос становится ещё более страшным. Причём страшность обратно пропорциональна эффективности.
- В случае с наложенным фильтром условие может быть ещё сложнее, но это отдельный разговор.
Прокрутка датасетов-запросов В стандарте SQL предусмотрено, что одни запросы могут быть произвольно прокручиваемыми, а другие - нет. Условия, когда произвольная прокрутка возможна, примерно в том же духе, что и для обновляемости представлений. Это означает, что большинство полезных запросов на уровне InterBase прокручиваются строго вперёд по одной записи. Тем не менее, BDE эмулирует для них произвольную прокрутку. И делается это варварским способом, который я лично называю маниакальное чтение. Суть в том, что после открытия запрос читается от начала до нужного места и все полученные записи сохраняются в памяти на клиенте. При попытке листать назад приложению предъявляются эти прочитанные записи. К чему это приводит, я расписывать не буду. В Delphi существует свойство запроса, именуемое UniDirectional. Согласно документации, оно должно определять, как открывается запрос: в однонаправленном режиме или в режиме неограниченной прокрутки. На самом деле если запрос допускает только однонаправленный режим, то он и будет открываться в этом режиме независимо от UniDirectional. Реально эта опция позволяет лишь чуть-чуть сэкономить в очень простых случаях. В остальных маниакальное чтение гарантировано. Фильтрация Когда я разобрался, как действует этот механизм, у меня сложилось впечатление, что у Borland правая рука не знает, что делает левая. Дело в том, что существуют два способа реализации фильтра: - Путём формирования соответствующего условия в SQL-запросе.
- Путём маниакального вычитывания всех записей на клиента с последующей их проверкой.
В двух случаях фильтрация реализуется гарантированно по второму сценарию: когда фильтруется SQL-запрос (у BDE не хватает умственных способностей, чтобы его осмыслить и модифицировать, хотя существуют реляционные системы, в которых такие фокусы являются нормой) и когда фильтрация делается пользовательским обработчиком (в Delphi по событию OnFilterRecord). В случае с таблицей теоретически возможно реализовать все фильтрующие конструкции средствами SQL. Ведь InterBase поддерживает приведение строк к верхнему регистру через UPPER() и сравнение с подстрокой с помощью containing, с шаблоном с помощью like. Вот только BDE (или его драйвер InterBase) об этом почему-то не знает. Таким образом, если условие состоит лишь из сравнений по полному точному значению с учётом регистра символов (или сравнений чисел и т. п.), то такой запрос будет сконвертирован в часть where отправлен на сервер. Но стоит это требование хоть в чём-то нарушить - и проверка всего фильтрового выражения перейдёт к клиенту с соответствующей прокачкой всех записей таблицы (разве что с учётом фильтра по MasterSource) через сеть. |