Шрифт:
Интервал:
Закладка:
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.
При этом если вы передаете в VALUES() данные для BLOB В виде текста, Firebird примет символьную строку в качестве ввода, например:
INSERT INTO ATABLE (BLOBMEMO)
VALUES ('Now is the time for all good men to come to the aid of the party');
Эта возможность будет подходящей при условии, что сохраняемый текст никогда не превысит 32 767 байтов (или 32 765, если вы помещаете поле VARCHAR в BLOB). Может показаться, что для многих интерфейсов программирования подобная проблема не существует, потому что они не могут обрабатывать такие большие строки. Однако, поскольку Firebird принимает выражения конкатенации строк SQL, такие как MYVARCHARI 11 MYVARCHAR2, следует принять меры по защите входных строк от переполнения.
Добавление в столбцы массивовВ приложениях со встроенным SQL (ESQL) возможно конструирование оператора SQL для добавления целого массива в столбец массива. Могут появиться ошибки, если данные не полностью заполняют массив.
В DSQL вовсе нет возможности добавлять данные в столбцы массивов. Необходимо реализовывать пользовательский метод в приложении или в коде компонента, который будет вызывать API-функцию isc_array_put_siice.
Использование INSERT для автоматических полей
Определение таблицы может содержать столбцы, чьи значения автоматически заполняются данными при добавлении новой строки. Это может происходить в нескольких случаях:
* столбец определен с предложением COMPUTED BY;
* столбец или домен, на котором он основан, включает предложение DEFAULT;
* для таблицы был создан триггер BEFORE INSERT для автоматического заполнения столбца.
Автоматические поля могут воздействовать на тот способ, каким вы формулируете операторы INSERT для таблицы.
Столбцы COMPUTED BYВключение вычисляемого столбца в список входных столбцов является неверным и приводит к исключениям, как показано в следующем примере:
CREATE TABLE EMPLOYEE (
EMP_NO INTEGER NOT NULL PRIMARY KEY,
FIRST_NAME VARCHAR (15),
LAST_NAME VARCHAR(20),
BIRTH_COUNTRY VARCHAR(30) DEFAULT 'TAIWAN',
FULL_NAME COMPUTED BY FIRST_NAME || ' ' || LAST_NAME);
COMMIT;
INSERT INTO EMPLOYEE (EMP_NO, FIRST_NAME, LAST_NAME, FULL_NAME)
VALUES (99, 'Jiminy', 'Cricket', 'Jiminy Cricket');
Столбцы, имеющие значение no умолчаниюЕсли столбец определен со значением по умолчанию, то это значение будет работать только при добавлении и только если этот столбец отсутствует в списке входных столбцов. Если немного изменить оператор добавления в предыдущем примере, то в столбец BIRTH_COUNTRY будет записано значение 'TAIWAN':
INSERT INTO EMPLOYEE (EMP_NO, FIRST_NAME, LAST_NAME)
VALUES (99, 'Jiminy', 'Cricket');
COMMIT;
SELECT * FROM EMPLOYEE WHERE EMP_NO = 99;
EMP_NO FIRST_NAME LAST_NAME BIRTH_COUNTRY FULL_NAME
99 Jiminy Cricket TAIWAN Jiminy Cricket
Значения по умолчанию никогда не заменяют значений NULL:
INSERT INTO EMPLOYEE (EMP_NO, FIRST_NAME, LAST_NAME, BI RTH_COUNTRY)
VALUES (100, 'Maria', 'Callas', NULL);
COMMIT;
SELECT * FROM EMPLOYEE WHERE EMP_NO = 100;
EMP_NO FIRST_NAME LAST_NAME BIRTH_COUNTRY FULL_NAME
100 Maria Callas Maria Callas
Если вы разрабатываете приложения с использованием компонентов, которые генерируют операторы INSERT из спецификаций столбцов операторов SELECT для наборов данных - например, Delphi или JBuilder - учитывайте такое поведение, Если ваш компонент не поддерживает метод получения значений по умолчанию с сервера, может оказаться необходимым изменить оператор, записывающий данные.
Триггеры BEFORE INSERTПри добавлении данных в таблицу, для которой существуют триггеры, автоматически заполняющие некоторые столбцы, убедитесь, что вы не задали эти столбцы в вашем входном списке, если вы хотите, чтобы триггер выполнил свою работу.
Это вовсе не означает, что вы всегда должны опускать поля, обрабатываемые триггером, во входном списке. Очень рекомендуется для поддержания целостности данных создавать триггеры, которые присваивают значения столбцам в случае, когда такие значения не задаются в операторе INSERT - особенно когда используется множество приложений и инструментов для доступа к вашей базе данных. Триггер должен проверять входное значение (например, NULL) и выполнять свои действия в соответствии с условиями.
В следующем примере первичный ключ OID заполняется в триггере:
CREATE TABLE AIRCRAFT (
OID INTEGER NOT NULL,
REGISTRATION VARCHAR(8) NOT NULL,
CONSTRAINT PK_AIRCRAFT
PRIMARY KEY (OID));
COMMIT;
/**/
SET TERM ^;
CREATE TRIGGER BI_AIRCRAFT FOR AIRCRAFT
ACTIVE BEFORE INSERT POSITION 0
AS
BEGIN
IF (NEW.OID IS NULL) THEN
NEW.OID = GEN_ID(ANYGEH, 1) ;
END ^
SET TERM ;^
COMMIT;
/* */
INSERT INTO AIRCRAFT (REGISTRATION)
SELECT FIRST 3 SKIP 2 REGISTRATION FROM AIRCRAFT_OLD
ORDER BY REGISTRATION;
COMMIT;
В этом случае триггер получает значение генератора для первичного ключа, потому что ему не было передано значение во входном списке. При этом, поскольку триггер выполнит свою работу только в том случае, если обнаружит NULL, следующий оператор INSERT также будет прекрасно работать - конечно, при условии, что предоставляемое значение OID не нарушает ограничения уникальности для первичного ключа:
- Delphi. Учимся на примерах - Сергей Парижский - Программирование
- Сделай видеоигру один и не свихнись - Слава Грис - Программирование / Руководства
- Психбольница в руках пациентов - Алан Купер - Программирование