|
Как построить в запросе "Дерево"?
Как избавиться от ошибки "multiple rows in singleton select"?
Почему после выдачи Commit или RollBack при открытых таблицах или запросах они
перечитываются полностью?
Что такое SHADOW в InterBase?
Поддерживает-ли Interbase поля типа autoincrement?
Как определить реальный размер поля типа BLOB, которое сохранено в таблице?
Конвертация типов полей через COMPUTED BY.
Как правильно получить список таблиц, просмотров, функций и процедур в БД?
Как ускорить выполнение множественных записей?
Как выбрать размер страницы БД?
Как определить дисковое пространство, необходимое для анения БД?
Можно-ли использовать имя таблицы как параметр хранимой процедуры?
Как оптимизировать запросы?
Можно-ли в запросах делать поиск по BLOB?
Есть-ли способ восстановить несохраненные (uncommitted) изменения БД, например
в случае отключения питания?
Я создал БД с правами пользователей в Local IB. После переноса этой БД на
InterBase for NT все пользователи куда-то "пропали". В чем дело?
Что такое ISC4.GDB? Для чего нужна эта БД?
Можно-ли создать пользователя БД при помощи SQL-команды?
Почему таблица или Select показывается в Grid быстро, а перемещение в конец таблицы
происходит долго?
Как получить значение генератора?
Как получить список пользователей Interbase?
Как программно сменить пароль входа на Interbase?
InterBase и использование Кирилицы.
Получение значения поля типа Memo без использования объекта TDBMemo.
Как построить в запросе "Дерево"?
Много прекрасных предложений по этому поводу проходилов Интернете. Но на
мой взгляд, самым интересным является решение Киевского программиста Правоверова
Олега -
1. Для примера, предлагается создать таблицу следующей конструкции:
-
CREATE TABLE TABLEBOOK(
-
POLE_ID INTEGER,
-
POLE_NAME VARCHAR(50),
-
POLE_PARENT INTEGER)
Важно чтобы дочерние записи в этой таблице своим полем "POLE_PARENT" соответствовали
родительскому полю "POLE_ID".
2. Для построения "Дерева" построим следующую рекурсивную хранимую процедуру:
-
CREATE PROCEDURE TABLE_BOOK (
-
PARENT_ID INTEGER,
-
I_LEVELS INTEGER,
-
) RETURNS (
-
POLE_ID INTEGER,
-
POLE_NAME VARCHAR(50),
-
POLE_PARENT INTEGER,
-
LEVELS INTEGER
-
) AS
-
DECLARE VARIABLE I INTEGER;
-
DECLARE VARIABLE C INTEGER;
-
BEGIN
-
FOR
-
SELECT
-
POLE_ID,
-
POLE_NAME,
-
POLE_PARENT
-
FROM
-
TABLEBOOK
-
WHERE
-
(POLE_ID> :PARENT_ID) AND
-
(POLE_PARENT=: PARENT_ID)
-
INTO :POLE_ID, :POLE_NAME, :POLE_PARENT
-
DO
-
BEGIN
-
SELECT
-
COUNT(*)
-
FROM
-
TABLEBOOK
-
WHERE
-
(POLE_PARENT=:POLE_ID)
-
INTO :C;
-
BEGIN
-
LEVELS=:I_LEVELS + 1;
-
SUSPEND;
-
END
-
IF (:C> 0) THEN
-
BEGIN
-
FOR
-
SELECT
-
POLE_ID,
-
POLE_NAME,
-
POLE_PARENT,
-
LEVELS
-
FROM
-
TABLE_BOOK(:POLE_ID, :LEVELS)
-
INTO :POLE_ID, :POLE_NAME, :POLE_PARENT, :LEVELS
-
DO
-
BEGIN
-
SUSPEND;
-
END
-
END
-
END
-
END
Теперь достаточно послать таблице TABLEBOOK запрос:
-
SELECT * FROM TABLE_BOOK(0, 0)
а дальше, как говорится дело, техники - поместить упорядоченное содержимое,
на пример в TTreeView.
Назад к содержанию
Как
избавиться от ошибки "multiple rows in singleton select"?
Данная ошибка может происходить в Вашем триггере или хранимой процедуре.
Обычный SELECT внутри триггера или процедуры должен возвращать одну
строку (row), т.к. при двух и более строках InterBase не знает куда
поместить значения полей этих строк. Если ваш SELECT возвращает
несколько записей, то нужно пользоваться конструкцией FOR
SELECT ... INTO ... DO ... которая производит обработку возвращаемого
набора записей в цикле. Если-же Вы уверены, что Ваш SELECT должен
вернуть только одну запись, а ошибка все-таки возникает, то давайте рассмотрим
следующую ситуацию:
Существуют таблицы ORDERS (заказы) и CLIENTS
(клиенты). Обе эти таблицы имеют поле связи CLIENT_ID
INTEGER. Для того чтобы вытащить информацию о клиенте используется
запрос
-
SELECT
-
CLIENT_ID,
-
CLIENT_NAME
-
FROM CLIENTS
-
WHERE CLIENT_ID=?
где ? - либо значение либо переменная.
Теперь представим себе, что этот запрос должен выполняться в триггере при
вставке записи в таблицу ORDERS
-
CREATE TRIGGER TI_ORDERS FOR ORDERS
-
ACTIVE AFTER INSERT POSITION 0
-
AS
-
DECLARE VARIABLE CID INTEGER;
-
DECLARE VARIABLE CNAME CHAR(30);
-
BEGIN
-
SELECT
-
C.CLIENT_ID,
-
C.CLIENT_NAME
-
FROM CLIENTS C
-
WHERE C.CLIENT_ID=CLIENT_ID
-
INTO :CID, :CNAME;
-
...
Итак, поскольку в запросе использован псевдоним C (FROM
CLIENTS C), то якобы существует гарантия что в предложении WHERE
будут сравниваться поле C.CLIENT_ID из таблицы
CLIENTS
и поле CLIENT_ID из таблицы ORDERS
(в триггере доступны имена полей собственной таблицы). Hа самом деле даже
использование псевдонимов не дает гарантии что переменные будут разичаться,
и получается что в предложении
WHERE сравнивается
само с собой поле таблицы
CLIENTS.CLIENT_ID,
и в запросе возвращается
ВСЯCLIENTS. Вот почему возникает вышеупомянутое
сообщение об ошибке. Избавиться от него можно несколькими путями:
1. Использовать разные имена полей для связи между CLIENTS
и ORDERS. например OCLIENT_ID
и CCLIENT_ID.
2. Использовать уточнитель new.CLIENT_ID,
несмотря на то что в документации указано что для триггеров последействия
(AFTER) он не имеет смысла.
-
SELECT
-
C.CLIENT_ID,
-
C.CLIENT_NAME
-
FROM CLIENTS C
-
WHERE C.CLIENT_ID=new.CLIENT_ID
-
...
3. Перед запросом поместить CLIENT_ID
в локальную переменную, и в запросе использовать сравнение не с полем,
а с этой локальной переменной.
-
CID=CLIENT_ID;
-
SELECT
-
C.CLIENT_ID,
-
C.CLIENT_NAME
-
FROM CLIENTS C
-
WHERE C.CLIENT_ID=CID
-
...
Назад к содержанию
Почему
после выдачи Commit или RollBack при открытых таблицах или запросах
они перечитываются полностью?
Hачнем с того, что такое перечитывание происходит не всегда. Если вы открываете
TTable
или TQuery в контексте какой-либо транзакции, то содержимое этих
источников данных определяется уровнем изоляции транзакции (ReadCommitted
или RepeatableRead). Если при открытых источниках данных завершить
транзакцию подтверждением или откатом, то контекст транзакции сменится
- вступит в действие т.н. неявная транзакция, которая стартует после соединения
с БД. В результате данные, прочитанные до смены контекста могут стать неактуальными,
поэтому BDE фактически переоткрывает курсоры. А поскольку неизвестно,
на какой строке выборки завершилась явная транзакция, считываются все записи
(fetch до конца таблицы). Следует избегать таких ситуаций, и по
крайней мере достаточно большие выборки закрывать перед завершением явной
транзакции.
Более подробную информацию о поведении BDE при commit/rollback вы
сможете получить в BDE32.HLP, пункт DbiEndTran.
Назад к содержанию
Что
такое SHADOW в InterBase?
Shadow - это программное "зеркалирование" БД. Все операции записи,
производимые над каким-либо файлом GDB параллельно производятся и над соответствующим
файлом SHADOW. При сбое GDB вы можете остановить работу пользователей и
просто скопировать Shadow на место оригинальной БД (GDB), после чего продолжить
работу. Hеобходимо учитывать, что поддержка Shadow замедляет операции изменения
БД. Желательно чтобы Shadow располагалась на другом винчестере, и еще лучше
если винчестеры с GDB и Shadow будут иметь разные контроллеры - в этом
случае запись будет распараллеливаться.
В ДемоЦентре проводились элементарные тесты на Local InterBase 4.1 (из
комплекта Delphi 2.0) под Windows95. Тест представлял собой добавление
10000 (десять тысяч) записей в пустую БД, каждые 1000 записей обрамлялись
StartTransaction-Commit. Использовался HDD с контроллером IDE: (одна запись
=~100 байт + BLOB-поле 512К) усредненное время
без Shadow |
4мин 40сек |
с Shadow на том-же винчестере |
6мин 00сек |
с Shadow на другом винчестере |
4мин 50сек |
Разумеется, при интенсивной многопользовательской работе и достаточно большой
базе данных теоретически должны ожидаться следующие результаты коэффициент
замедления
без Shadow |
1 |
с Shadow на том-же винчестере |
1.6 (минимум в полтора раза) |
с Shadow на другом винчестере |
~1 (почти без замедления) |
Кроме того, очень сильное влияние на быстродействие InterBase оказывает
параметр Forced Writes - немедленное сохранение изменений страниц.
Вы можете включить или выключить этот параметр для конкретной БД на ходу
при помощи Server Manager. При выключенном Forced Writes работа с БД происходит
в 5-6 раз быстрее, но есть опасность потерять БД при внезапном выключении
питания сервера.
Назад к содержанию
Поддерживает-ли
Interbase поля типа autoincrement?
В явном виде нет - вместо этого в InterBase существует другой механизм,
называемый "генераторами". Генератор
- некая переменная, значение которой может быть получено и увеличено на
некоторое значение (дельту) при помощи встроенной функции GEN_ID.
Создать генератор можно фразой
CREATE GENERATOR MYGENERATOR;
Обычно генераторы используют в триггерах, при этом текст триггера может
быть следующим:
-
CREATE TRIGGER TI_CLIENTS FOR CLIENTS
-
ACTIVE BEFORE INSERT POSITION 0
-
AS
-
BEGIN
-
IF (new.CLIENT_ID IS NULL) THEN
-
CLIENT_ID=GEN_ID(MYGENERATOR, 1);
-
END
Вместо значения 1 может быть использовано любое число, на которое нужно
иметь приращение текущего значения генератора. Механизм генераторов гарантирует
что даже при конкурентном (параллельном) вызове функции GEN_ID каждому
пользователю будет выдаваться уникальное значение. Последнее значение генератора
всегда запоминается в БД, поэтому разработчику не нужно заботиться о "восстановлении"
его максимального значения после подсоединения к БД. Генераторы являются
переменными типа integer (longint), таким образом если предположить что
новое значение возвращается в среднем с интервалом в 3 секунды, значений
генератора хватит приблизительно на 270 лет.
Примечание: если вы воспользуетесь приведенным выше примером
использования генератора в триггере, то у вас может возникнуть следующая
проблема - при добавлении записей с клиентского места новые записи будут
"пропадать". Это связано с тем, что клиенту никаким образом не может быть
передана информация об идентификаторе сформированом в триггере на сервере.
Т.е. новую запись можно будет увидеть только либо перевыполнив запрос либо
переместившись в конец таблицы (если еще не произошел fetch всех записей.
Для исключения такой ситуации можно создать хранимую процедуру возвращающую
значение генератора (так-же как и для триггера), и вызывать эту процедуру
перед
созданием новой записи (для Delphi - TTable.BeforePost).
Назад к содержанию
Как
определить реальный размер поля типа BLOB, которое сохранено в таблице?
Ниже приведена функция GetBlobSize, которая возвращает размер данного
BLOB
или MEMO поля. Пример вызова:
-
function GetBlobSize(Field: TBlobField): Longint;
-
var
-
B : TBlobStream;
-
begin
-
B :=TBlobStream.Create(Field, bmRead);
-
try
-
Result :=B.Size;
-
finally
-
B.Free;
-
end;
-
end;
-
procedure TForm1.Button1Click(Sender: TObject);
-
begin
-
{ This sets the Edit1 edit box to display the size
of }
-
{ a memo field named Notes. }
-
Edit1.Text :=IntToStr(GetBlobSize(Notes));
-
end;
Примечание: Этот способ не будет работать в методе OnCalcFields.
Назад к содержанию
Конвертация
типов полей через COMPUTED BY.
IB Database обладает интересной недокументированной возможностью. Обычно
при объявлении вычисляемого поля COMPUTED BY придерживаются синтаксиса
-
col_def=col { datatype | COMPUTED [BY] (< expr>)
| domain} µµµ
-
[DEFAULT { literal | NULL | USER}] µµµ
-
[NOT NULL] [ ] µµµ
-
[COLLATE collation]
Как видите, синтаксис предлагает указать либо тип столбца (datatype), либо
вычисляемое выражение (computed by). Указанием "или/или" является символ
|.
Обычно тип COMPUTED BY поля совпадает с исходным, на основе которого
оно строится. Однако существует возможность явно определить конкретный
тип столбца, даже не совпадающий с исходным. Можно проделать следующий
эксперимент - создать таблицу с приведенной структурой:
(определение PRIMARY KEY можно опустить, т.к. здесь оно введено лишь для
удобства работы с такой таблицей из Database Explorer, т.к. BDE не позволяет
обновлять таблицы, не имеющие первичного ключа)
-
CREATE TABLE TESTCOMP(
-
t_dataµFLOAT NOT NULL PRIMARY KEY,
-
c_intµINTEGER computed by (t_data),
-
c_numµNUMERIC(15, 2) computed by (t_data),
-
c_charµCHAR(20) computed by (t_data))
Теперь попробуйте ввести в эту таблицу записи со следующим значением T_DATA:
1.88, 3.2, 3.51. Вы увидите, что поле типа FLOAT сохраняет на диске не
совсем те значения, которые вы вводили. Поле C_INT содержит округленное
значение T_DATA. Поле C_NUM будет содержать либо точное значение T_DATA
либо округленное, в зависимости от значения параметра псевдонима BDE ENABLE
BCD=TRUE/FALSE. А вот C_CHAR будет содержать более точное значение вещественного
числа C_DATA.
При использовании этого трюка весьма полезным является просмотр значаний
типа NUMERIC(15, 2) в строковом выражении. Дело в том, что вещественные
числа не могут храниться с точностью целых, поэтому введенное 1.88 будет
выглядеть в NUMERIC(15, 2) как 1.88, но на деле (в виде строки) окажется
равным 1.8799999952316. Отсюда следует несколько выводов:
точность вещественных чисел ограничена, и число, хранимое как вещественное,
никогда нельзя сравнивать на равенство (это общеизвестное правило);
точность полей типа FLOAT весьма низка (эквивалент дельфийского single),
поэтому их можно использовать с большой осторожностью;
не все типы преобразуются из одного в другой - поле NUMERIC(15, 2) как
INTEGER COMPUTED BY... будет содержать 0;
при обработке вещественных чисел (округление, агрегация, сравнение, сложение/вычитание
и умножение/деление) нужно учитывать погрешность. Также известное бухгалтерское
правило - при умножении и делении первым выполняется умножение;
не следует использовать вещественные типы в качестве первичных ключей таблиц
- из-за погрешности или особенности обработки вещественных чисел процессорами
клиента и сервера якобы одно и то-же число может оказаться разным, и запись
по первичному ключу может оказаться "потерянной". Например запрос select
* from testcomp where t_data=1.88 может выдать пустой результат.
(c) Epsylon Technologies
Назад к содержанию
Как
правильно получить список таблиц, просмотров, функций и процедур в БД?
Markus Kemper рекомендует следующие запросы:
1. Для получения списка только пользовательских таблиц БД
-
SELECT
-
RDB$RELATION_NAME
-
FROM
-
RDB$RELATIONS
-
WHERE
-
((RDB$SYSTEM_FLAG=0) OR (RDB$SYSTEM_FLAG
IS NULL)) AND
-
(RDB$VIEW_SOURCE IS NULL)
-
ORDER BY RDB$RELATION_NAME
2. Для получения списка только системных таблиц БД
-
SELECT
-
RDB$RELATION_NAME
-
FROM
-
RDB$RELATIONS
-
WHERE
-
(RDB$SYSTEM_FLAG=1) AND
-
(RDB$VIEW_SOURCE IS NULL)
-
ORDER BY RDB$RELATION_NAME
3. Для получения списка просмотров
-
SELECT
-
RDB$VIEW_NAME
-
FROM
-
RDB$VIEW_RELATIONS
-
ORDER BY RDB$VIEW_NAME
4. Для получения списка функций
-
SELECT
-
RDB$FUNCTION_NAME
-
FROM
-
RDB$FUNCTIONS
-
ORDER BY RDB$FUNCTION_NAME
5. Для получения списка процедур
-
SELECT
-
RDB$PROCEDURE_NAME
-
FROM
-
RDB$PROCEDURES
-
ORDER BY RDB$PROCEDURE_NAME
Назад к содержанию
Как
ускорить выполнение множественных записей?
Архитектура множественных поколений записи требует кооперативной сборки
мусора. Это означает что каждый запрос который обнаружит неактуальные
записи должен удалить их. Это позволяет значительно уменьшить блокировки
и ускорить операции commit/rollback. Однако, как вы заметили, иногда
выполнение запросов может сильно замедляться если на диске много устаревших
записей. Это происходит из-за того, что изменения записываются как delta
между старой и новой записью, и при чтении новой записи IB должен ее собрать
из старых данных и delta. Для оптимизации сборки записей
IB заполняет страницы данных только наполовину, оставляя вторую половину
для хранения возможных delta. Если это пространство исчерпывается,
то IB распределяет новые страницы для хранения delta. В этом случае
ухудшается IO (для сборки записей приходится читать дополнительные страницы),
которое и приводит к ухудшению общей производительности.
Реально, большинство приложений не обновляют всю БД непрерывно, поэтому
затраты на поколения записей оказываются несущественными в смысле общей
производительности. (т.е. для хранения delta хватает свободного пространства
на страницах данных, и не возникает лишнего обмена с диском). Другие SQL-серверы,
которые не поддерживают множественные поколения записей, могут порождать
проблемы другого рода - для запросов с изоляцией Repeatable Read
либо полное дублирование данных, либо полное блокирование обновляемых таблиц.
Кроме того, logging в других SQL-серверах может вызывать похожее ухудшение
производительности.
Назад к содержанию
Как
выбрать размер страницы БД?
Этому посвящен один из разделов IB Data Definition Guide. Теоретически
имеет смысл увеличить до 2К (с умолчательного 1К) размер страницы, если
большее количество таблиц в вашей БД имеет количество записей> 200000
(200 тыс.). Hе забудьте, что увеличение размера страницы повлечет увеличение
размера кэш-буфера IB, т.к. он измеряется в страницах. Безусловно, нужно
ориентироваться на конкретную задачу - если ваше приложение часто выбирает
случайные записи из БД, то производительность при увеличении размера страницы
может ухудшиться (больше обмена с диском), и наоборот - для последовательных
выборок больший размер страницы предпочтительнее.
Назад к содержанию
Как
определить дисковое пространство, необходимое для хранения БД?
Точно посчитать размер БД не представляется возможным. Однако для предсказания
размера БД можно использовать определенные утверждения: При импорте
данных страницы (data pages) заполняются наполовину. Таким образом
объем импортированных данных будет в два раза больше (если данные импортируются
из текстовых файлов). Размер ключа индекса в байтах равен 5 + длина
поля. При импорте данных страницы индексов заполняются так-же как и
страницы данных - наполовину. При восстановлении (restore) данных
страницы данных будут заполнены опять-же наполовину, а страницы индексов
- полностью. Hеобходимо учитывать, что при работе с БД возможно динамическое
создание страниц, необходимых для хранения версий записей. Эти страницы
не возвращаются файловой системе даже при операции database sweep.
Как следствие этого, БД занимает минимально возможный объем только после
операций backup/restore, и максимальный - при частом обновлении
данных большим количеством пользователей. Кроме этого, IB упаковывает строки
CHAR
и VARCHAR, а также числовые последовательности, по алгоритму
RLE.
Назад к содержанию
Можно-ли
использовать имя таблицы как параметр хранимой процедуры?
Как таблицу - нет, но можно передать какое-либо строковое или числовое
значение, чтобы обработать его по IF .. ELSE. Хранимая процедура
не может динамически получать имя таблицы. (похоже что причиной является
то, что текст хранимой процедуры "скомпилирован" в BLR, который
невозможно интерпретировать).
Назад к содержанию
Как
оптимизировать запросы?
Вот несколько простых рекомендаций для оптимизации запросов:
избегайте явного использования outer join;
используйте неявный join (select a,b, from a1, b1 where ...) вместо
явного. При использовании явного join оптимизатор не работает;
пробуйте разный порядок таблицы в запросе для получения оптимальной производительности
(также и для явного join);
пробуйте ваши запросы в WISQL, включив Show Query Plan и
Show
Statistics;
используйте индексы по полям, участвующим в условиях where;
не забывайте делать SET STATISTICS по индексам, созданным вами специально
для ускорения запросов. Кроме того, возможно вам помогут документы, описывающих
принцип работы оптимизатора IB. Их можно получить в ДемоЦентре. К сожалению,
не существует точных правил по оптимизации запросов - это путь проб и ошибок,
т.к. невозможно заранее предсказать ни ваши запросы, ни ваши реальные данные.
Примечание: бывают ситуации, когда оптимизатор "настаивает"
на использовании всех индексов по определенному столбцу, даже если создан
специальный композитный индекс по всем полям where. В этом случае
необходимо принудительно заставить оптимизатор IB использовать нужный запрос,
причем скорость отработки такого запроса может ускориться в 10 раз.
Назад к содержанию
Можно-ли
в запросах делать поиск по BLOB?
Да. Однако в документации это не отражено. Поиск по строковым (CHAR,
VARCHAR)
полям или по BLOB можно производить при помощи операторов
CONTAINING,
STARTINGWITH и LIKE. Hапример:
SELECT * FROM MYTABLE WHERE BLOBFIELD CONTAINING
'sometext';
Поиск по умолчанию считается case-insensitive(регистро-нечувствительный),
поэтому для латинских букв строку поиска можно задавать строчными буквами
(в нижнем регистре). В этом случае при поиске 'sometext' в ответ
войдут записи с 'sometext', 'SOMETEXT' и 'SomeText'.
К сожалению, для BLOB невозможно указать COLLATE для правильного
перевода русских букв в верхний регистр, поэтому поиск будет производиться
только по точному совпадению. При поиске подтип BLOB(SUB_TYPE
0 или 1 - текст или binary) не имеет значения.
Назад к содержанию
Есть-ли
способ восстановить несохраненные (uncommitted) изменения БД, например
в случае отключения питания?
Hет. Такие изменения будут потеряны. Т.е. БД останется в состоянии, соответствующем
последней подтвержденной (committed) транзакции. Hесохраненные данные
в БД останутся в виде "осиротевших" страниц, которые можно очистить при
помощи Server Manager, пункт меню Validate Database.
Вполне возможно, что этот вопрос вызван тем, что восстановление состояния
БД в IB отличается от других широкораспространенных SQL-серверов (Oracle,
Informix, ...). IB не требует выполнения каких-то специфических действий
для продолжения работы с БД в случае сбоя питания - изменения, происходящие
до COMMIT, не записываются на место актуальных данных (т.е. отсутствует
режим Dirty Read), поэтому они будут просто потеряны, а БД останется
в рабочем состоянии.
Назад к содержанию
Я
создал БД с правами пользователей в Local IB. После переноса этой БД на
InterBase for NT все пользователи куда-то "пропали". В чем дело?
Причина в том, что информация о пользователях InterBase хранится в специальном
файле ISC4.GDB, и является общей для всех БД на конкретном компьютере.
Очевидно что в вашем случае на сервере InterBase for NT отсутствовали
пользователи, заведенные вами для Local InterBase. Вам придется
создать всех ваших пользователей и для InterBase for NT(при
помощи Server Manager).
Назад к содержанию
Что
такое ISC4.GDB? Для чего нужна эта БД?
База данных ISC4.GDB используется InterBase для хранения
информации о пользователях (имена, пароли и т.п.). Удалять этот
файл нельзя. Вы можете создать alias на эту БД и посмотреть
ее содержимое, но самостоятельно (в ручную) не сможете создавать или изменять
пользователей поскольку шифрация и дешифрация пароля выполняется InterBase.
Назад к содержанию
Можно-ли
создать пользователя БД при помощи SQL-команды?
Hет. Единственно правильный способ - использовать Server Manager.
(Tasks | User Security), либо утилиту командной строки GSEC.
Если иметь в виду добавление пользователей из клиенсткого приложения, то
это возможно. Посмотрите пример IBUSERS.ZIP
Назад к содержанию
Почему
таблица или Select показывается в Grid быстро, а перемещение в конец таблицы
происходит долго?
Когда вы открываете выборку, BDE производит Fetch только
такого количества записей, которые помещаются в Grid. Если вы захотели
переместиться в конец таблицы, то для большинства SQL-серверов возможно
перемещение по записям только вперед - т.е. если вам потребуется поместить
указатель в середину таблицы то BDE пришлось-бы заново перечитывать записи
с ее начала. Причина такой работы в том, что SQL-серверы в большинстве
возвращают результаты запросов в виде последовательных наборов записей.
В навигационных БД напротив, возможно физическое позиционирование на любую
запись таблицы. Если SQL-сервер поддерживает двунаправленные скроллируемые
курсоры, то полное кэширование записей запроса выполняться BDE не будет.
Кроме этого, TTable и TQuery работают по разному. TQuery при перемещении
в конец таблицы действительно сделает выборку всех записей, т.к. он и не
может иначе - для выполнения задано конкретное SQL-выражение. TTable-же
напротив, показывает всю таблицу, и запросы для получения содержимого таблицы
формируются автоматически. Поэтому TTable при нажатии вами в TDBGrid клавиш
Ctrl-End
сформирует запрос типа:
SELECT * FROM TABLE ORDER BY INDEXFIELD DESC
и покажет только видимые в DBGrid записи "с конца". Таким образом операцию
перехода в конец таблицы TTable выполнит практически мгновенно. Пользователи
Delphi 2.0 C/S могут сами убедиться в этом при помощи SQL Monitor.
Назад к содержанию
Как
получить значение генератора?
Dmitry Sergeev (Dmitry.Sergeev@f1851.n5020.z2.fidonet.org)
18.12.1998
Для получения текущего значения генератора следует обратится к таблице
- владельцу генератора со следующим запросом:
-
select
-
gen_id(my_generator,
0)
-
from
-
RDB$DATABASE
Назад к содержанию
Как
получить список пользователей Interbase?
Ivanuts V.A. (ivanuts@altavista.net)
5.12.1998
a) Если необходимо получить список зарегестрированных пользователей
на сервере InterBase, необходимо послать следующий запрос в базу данных
ISC4.GDB
(находится в дирректории InterBase):
-
SELECT
-
USER_NAME
-
FROM
-
USERS
b) Если необходимо получить список зарегестрированных пользователей в конкретной
БД, можно использовать следующий запрос в эту базу данных:
-
SELECT DISTINCT
-
RDB$USER
-
FROM
-
RDB$USER_PRIVILEGES
Используя в этом запросе и другие поля таблицы RDB$USER_PRIVILEGES (без
применения директивы DISTINCT) можно также получить данные о привилегиях
зарегистрированных пользователей БД, назначенных на конкретные таблицы,
просмотры, процедуры или их поля.
Назад к содержанию
Как
программно сменить пароль входа на Interbase?
Ivanuts V.A. (ivanuts@altavista.net)
5.12.1998
В своих разработках я применяю функцию Gregory H. Deatz
из библиотеки FreeUDFLib. Я немного ее перестроил, но работой ее очень
доволен. Если необходимо чтоб эта функция выполнялась на клиентском приложении,
переменная SysUName должна иметь значение 'SYSDBA'.
-
const
-
UM_ERROR=100001;
-
UM_CANNOT_DETERMINE_ACTION=100002;
-
UM_CANNOT_SET_ENV_ISC_USERNAME=100003;
-
UM_CANNOT_SET_ENV_ISC_PASSWORD=100004;
-
UM_SYSUSER_NOT_VALID=100005;
-
UM_SYSPASSWORD_NOT_VALID=100006;
-
UM_CANNOT_EXECUTE_PROCESS=100007;
-
function UserProp(UserName, Password, SysUName, SysPword, Action:
String):
Integer;
-
var
-
Startup : TStartupInfo;
-
Process : TProcessInformation;
-
Status : DWORD;
-
IB_BIN, szApp, szCmdLine : String;
-
Env : Pointer;
-
begin
-
Result :=UM_ERROR;
-
Startup.lpReserved :=PChar(0);
-
Startup.lpDesktop :=PChar(0);
-
Startup.lpTitle :=PChar(0);
-
Startup.dwFlags :=STARTF_USESHOWWINDOW;
-
Startup.wShowWindow :=SW_HIDE;
-
Startup.cbReserved2 :=0;
-
Startup.lpReserved2 :=PByte(0);
-
IB_BIN :=GetIBPath +'\Bin';//
GetIBPath - путь к дирректории InterBase
-
szApp :=IB_BIN + '\Gsec.exe';
-
if Trim(Action)>''then
-
begin
-
case Action[1] of
-
'A': szCmdLine :=szApp + '
-add ' + Trim(UserName) + '-pw '
+ Trim(Password);
-
'D': szCmdLine :=szApp + '
-delete ' + Trim(UserName);
-
'M': szCmdLine :=szApp + '
-modify ' + Trim(UserName) + ' -pw
' + Trim(Password);
-
else
-
begin
-
Result := UM_CANNOT_DETERMINE_ACTION;
-
Exit;
-
end;
-
end;
-
end
-
else
-
begin
-
Result :=UM_CANNOT_DETERMINE_ACTION;
-
Exit;
-
end;
-
if ((Trim(SysUName)= 'SYSDBA')
and (not SetEnvironmentVariable(PChar('ISC_USER'),
PChar(Trim(SysUName))))) then
-
begin
-
Result :=UM_CANNOT_SET_ENV_ISC_USERNAME;
-
Exit;
-
end
-
else
-
begin
-
if (Trim(SysUName) <>'SYSDBA')
then
-
begin
-
Result :=UM_SYSUSER_NOT_VALID;
-
Exit;
-
end;
-
end;
-
if ((Trim(SysPword) <>'')
and
(not SetEnvironmentVariable(PChar('ISC_PASSWORD'),
PChar(Trim(SysPword))))) then
-
begin
-
Result :=UM_CANNOT_SET_ENV_ISC_PASSWORD;
-
Exit;
-
end
-
else
-
begin
-
if (Trim(SysPword)='') then
-
begin
-
Result :=UM_SYSPASSWORD_NOT_VALID;
-
Exit;
-
end;
-
end;
-
Env :=GetEnvironmentStrings();
-
if (CreateProcess(
-
PChar(szApp), // lpApplicationName
-
PChar(szCmdLine), // lpCommandLine
-
PSecurityAttributes(0), //
lpProcessAttributes
-
PSecurityAttributes(0), //
lpThreadAttributes
-
False, // bInheritHandles
-
HIGH_PRIORITY_CLASS, //
dwCreationFlags
-
Env, // lpEnvironment
-
PChar(0), //
lpCurrentDirectory
-
Startup, // lpStartupInfo
-
Process // lpProcessInformation
-
)) then
-
begin
-
GetExitCodeProcess(Process.hProcess, Status);
-
while Status=STILL_ACTIVEdo
-
begin
-
Sleep(10);
-
GetExitCodeProcess(Process.hProcess, Status);
-
end;
-
Result :=Status;
-
end;
-
end;
Назад к содержанию
InterBase
и использование Кирилицы.
Да, InterBase прекрасно управляется как с кодировкой 866 так и с 1251.
Да, как в Delphi 1.0, так и в Delphi 2.0, так и в Delphi 3.0. Для того,
чтобы в БД в строковых полях использовались русские буквы, необходимо при
создании БД указать в качестве дополнительного параметра фразу:
DEFAULT CHARACTER SET WIN1251
А в BDECFG32 в драйвере InterBase и его псевдонимах (aliases) указать
языковый драйвер Pdox ANSI Cyrillic. Такие установки обеспечат нормальную
работу с кодировкой 1251.
Дополнительно, если Вы предполагаете использовать выражение UPPER
в SQL-запросах, то Вам потребуется при создании таблиц InterBase использовать
уточнение COLLATION SEQUENCE для строковых полей.
К сожалению, установить COLLATE PXW_CYRL по умолчанию для базы данных
невозможно (такой параметр отсутствует). Поэтому таблицы InterBase, созданные
при помощи Database Desktop или компонента TTable не будут иметь правильного
COLLATE
для работы с функцией SQL UPPER. Эта проблема, необходимо отметить,
не относится к работе с регистром русских букв внутри Delphi, поскольку
для этого внутри VCL используются функции Windows, правильно переводящие
буквы кодировки 1251 в верхний регистр и обратно. Hапример, если возможен
запрос такого типа:
-
SELECT * FROM CUSTOMERS
-
WHERE
-
UPPER(NAME)='ИВАHОВ'
То таблицу CUSTOMERS придется создавать при помощи текста:
-
CREATE TABLE CUSTOMERS
-
(ID INTEGER NOT NULL,
-
NAME CHAR(30) COLLATE PXW_CYRL,
-
PRIMARY KEY (ID))
Фраза COLLATE PXW_CYRL заставляет InterBase использовать таблицу
трансляции символов PXW_CYRL вместо WIN1251, которая устанавливается
по умолчанию для DEFAULT CHARACTER SET WIN1251. Однако можно использовать
указание порядка сортировки "на ходу", без указания порядка сортировки
в описании таблицы - при помощи того-же спецификатора COLLATE. В
этом случае запросы могут выглядеть следующим образом:
-
SELECT * FROM CUSTOMERS
-
WHERE UPPER(NAME COLLATE PXW_CYRL)='ИВАHОВ'
Учтите, что если Вы используете ORDER BY NAME, то порядок записей
у полей с COLLATE PXW_CYRL будет отличаться от имеющих только CHARACTER
SET WIN1251. В этом случае необходимо и в ORDER BY указывать
COLLATE.
Hапример:
-
SELECT * FROM CUSTOMERS
-
WHERE
-
UPPER(NAME COLLATE PXW_CYRL)='ИВАHОВ'
-
ORDER BY NAME COLLATE PXW_CYRL
Примечание: при создании псевдонимов в Database Desktop языковый
драйвер называется "ancyrr".
Примечание: ODBC-драйвер InterBase фирмы Q+E, входящий в поставку
Delphi 1.0, не может работать с кодировкой WIN1251, так как не имеет
параметра SortOrder (как например ODBC-драйвер Access). В результате, при
работе с этим драйвером через BDE русские буквы будут видны (благодаря
LangDriver
Pdox Ansi Cyrillic), но ввести их не удастся. Для приложений, работающих
с ODBC напрямую, русские буквы будут видны как бессмысленный набор символов.
В настоящее время ожидается новый 32-разрядный ODBC драйвер, в котором
эти проблемы, мы надеемся, будут решены.
Примечание: решить проблему с изменением данных в русской кодировке
при использовании ODBC-драйвера можно следующим образом: напрямую указывать
кодировку перед строковым значением, содержащим русские символы.
INSERT INTO MYTABLE VALUES (1, _win1251 'Привет!')
К сожалению, такой способ приводит к тому, что со стандартными компонентами
TTable и TQuery можно работать только используя дополнительный компонент
UpdateSQL (который позволяет переопределить запросы выдаваемые этими компонентами
при INSERT, UPDATE, DELETE, и кроме этого присутствует только в 32-х разрядном
Делфи)
Назад к содержанию
Получение
значения поля типа Memo без использования объекта TDBMemo.
-
Поместите, например, компонент Table1 на форму, выберите таблицу BIOLIFE.DB.
-
Установите свойство Table1.Active в True.
-
Поместите на форму компоненты Edit1 (для примера) и Button1.
-
Добавьте код в обработчик OnClick для Button1:
-
procedure Tform1.Button1Click(Sender : TObject);
-
var
-
Bs : TblobStream;
-
P : array[0..50]
of
Char;
-
begin
-
FillChar(P, SizeOf(P), #0);
-
Bs :=TblobStream.Create(Table1.FieldByName('Notes'),
bmRead);
-
try
-
Bs.Read(P, 50);
-
finally
-
Bs.Free;
-
end;
-
Edit1.Text :=StrPas(P);
-
end;
-
Назад к содержанию
|