Выбрать главу

Клиентское приложение использует isc_dsqi_fetch для запроса строки, которая только что заполнила XSQLDA. Обычное поведение большинства современных клиентских приложений- выполнение в цикле обращений к isc_dsqi_fetch для получения выходных строк в пакете и буферизация их в структурах клиентской стороны, которые называются наборами записей, наборами данных или результирующими наборами.

Некоторые приложения API реализуют именованные курсоры и используют поведение TOR UPDATE, однако большинство этого не делают.

Вложенное предложение WITH LOCK

Firebird 1.5 вводит необязательное расширение WITH LOCK, используемое с/без предложения FOR UPDATE, для поддержки ограниченного уровня явной пессимистической блокировки (pessimistic locking) на уровне строки. Пессимистическая блокировка является прямой противоположностью архитектуры транзакций в Firebird и добавляет запутанность. Ее использование рекомендуется только тем разработчикам, которые хорошо понимают, как параллельная работа многих пользователей реализована в Firebird. Пессимистическая блокировка обсуждается в главе 27.

Запросы, подсчитывающие строки

Среди некоторых программистов существует закрепившаяся практика разработки приложений, которым нужно выполнить подсчет строк в выходном наборе. В Firebird не существует быстрого надежного способа получения количества строк, возвращаемых в выходном наборе. Поскольку Firebird имеет многоверсионную архитектуру, у него нет механизма "узнавать" количество строк в постоянных таблицах. Если приложению требуется количество строк, оно может получить приблизительное значение с использованием запроса SELECT COUNT (*).

Запросы SELECT COUNT(*)

Оператор SELECT с вызовом функции COUNT() на месте идентификатора столбца вернет приблизительную мощность набора, определенного в предложении WHERE. Функция COUNT() принимает практически все в качестве входного аргумента: идентификатор столбца, список столбцов, символ *, который представляет "все столбцы", и даже константу.

Например, все следующие операторы эквивалентны или близки. При этом SELECT COUNT(<имя-некоторого-столбца>) не включает в счетчик строки, где <имя-некоторого-столбца> имеет значение NULL:

SELECT COUNT (*) FROM ATABLE WHERE COL1 BETWEEN 40 AND 75;

SELECT COUNT (COL1) FROM ATABLE WHERE COL1 BETWEEN 40 AMD 75;

SELECT COUNT (COL1, COL2, COL3) FROM ATABLE WHERE COL1 BETWEEN 40 AND 75;

SELECT COUNT 1 FROM ATABLE WHERE COL1 BETWEEN 40 AND 75;

SELECT COUNT ('Sticky toffee') FROM ATABLE WHERE COL1 BETWEEN 40 AND 75;

COUNT(*) является очень дорогой операцией, потому что она может работать только пройдя по всему набору данных и точно подсчитав каждую строку, которая видима как подтвержденная для текущей транзакции. Это число должно трактоваться как "грубый счетчик", потому что может оказаться неверным, если другая транзакция подтверждает работу.

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

Исключением является ситуация, когда COUNT (*) включается в выходной набор, который агрегируется на основании предложения GROUP BY. При этих условиях счетчик не будет дорогим - он будет рассчитываться для агрегированной группы в процессе выполнения агрегирования. Например:

SELECT COL1, SUM(COL2), COUNT(*) FROM TABLEA

GROUP BY COL1;

Подробности использования COUNT С агрегированием см. в главе 23.

Проверка существования

Не используйте SELECT COUNT(*) как способ проверки существования строк, соответствующих некоторому критерию. Такая техника часто обнаруживается в приложениях, которые были переведены в Firebird из основанных на файлах базах данных с блокировкой таблиц, таких как Paradox или MySQL. От этой техники нужно отказаться. Вместо этого используйте функциональный предикат EXISTS(), который был разработан для этих целей и является очень быстрым. См. в следующей главе подробности об EXISTS() и других функциональных предикатах.

Вычисления "следующего значения"

Другая техника, от которой нужно отказаться в Firebird, это использование COUNT(*) и прибавление единицы для "генерации" значения первичного ключа. Это ненадежно в любой многопользовательской СУБД, которая изолирует параллельные задачи. В Firebird это к тому же выполняется крайне медленно, потому что система управления таблицей не имеет "файла записей", которые могли бы быть подсчитаны методами управления файлами на компьютере.

Используйте генераторы для любых целей, которые преследуют уникальность числовых последовательностей. Подробнее о генераторах см .разд. "Генераторы" главы 9.

Варианты COUNT()

Результатом COUNT() никогда не будет NULL, потому что он подсчитывает строки. Если счетчик будет использован для пустого набора, он вернет ноль. Он никогда не может быть отрицательным.

COUNT(*) для таблицы подсчитывает все строки без проверки существования данных в столбцах. Оптимизатор может использовать индекс, если запрос содержит соответствующее условие WHERE.

Например, оператор

SELECT COUNT(*) FROM EMPLOYEE

WHERE LAST_NAME BETWEEN 'A%' AND 'M%';

может быть чуть менее дорогим, если существует индекс для LAST_NAME.

COUNT (имя-столбца) подсчитывает только строки, где имя-столбца не является NULL.

COUNT (DISTINCT имя-столбца) подсчитывает только отличающиеся значения в этом столбце. То есть все повторения одного и того же значения учитываются как один элемент.

в COUNT (DISTINCT ...), если столбец допускает значение NULL, все строки, содержащие в этом столбце NULL, исключаются из подсчета. Если вы должны их сосчитать, это может быть выполнено "хакерским" способом:

SELECT COUNT (DISTINCT TABLE.COLX) +

(SELECT COUNT(*) FROM RDB$DATABASE

WHERE EXISTS(SELECT * FROM TABLE T

WHERE T.COLX IS NULL))

FROM TABLE

Оператор INSERT

Оператор INSERT используется для добавления строк в одну таблицу. SQL не позволяет в одном операторе INSERT добавлять строки более чем в одну таблицу.

При некоторых условиях оператор INSERT может работать с просмотрами. Обсуждение просмотров, для которых можно применять добавление данных в лежащие в основе просмотра таблицы, см. в главе 24.

Оператор INSERT имеет две основные формы передачи значений в список входных столбцов.

Используйте следующую форму для добавления списка констант:

INSERT INTO имя-таблицы | имя-просмотра (<список столбцов>)

VALUES (<соответствующей список значений>)

Следующая форма используется для добавления из встроенного запроса:

INSERT INTO <таблица> (<список столбцов>)

SELECT [[FIRST m] [SKIP n]] <соответствующий список значений из другого набора>

[ORDER BY <встроенный столбец (столбцы)> [DESC]]

В следующем примере предложение INSERT INTO определяет входной набор для таблицы TABLEB, а предложение SELECT определяет соответствующий встроенный запрос к таблице TABLEA для получения значений для входного набора:

INSERT INTO TABLEB(COLA, COLB, COLC)

SELECT COL1, COL2, COL3 FROM TABLEA;

! ! !

ПРИМЕЧАНИЕ. He существует возможности добавлять данные во встроенный запрос.

. ! .

Добавление данных в столбцы BLOB

Техника INSERT INTO ... SELECT напрямую передает столбец BLOB В столбец BLOB. Как правило, если вам нужно добавить столбец BLOB как часть списка VALUES(), он должен конструироваться клиентским приложением с использованием функций и структур API. В приложениях DSQL такие данные обычно передаются в виде потоков (stream). Размер сегмента может быть проигнорирован везде, кроме приложений ESQL.