"Виртуальная библиотека Delphi" - читать интересную книгу автора

Базы данных — компоненты и VCL.

1. Какие визуальные компоненты для работы с данными входят в  Delphi?

Различные версии Delphi содержат следующие наборы компонентов:

Компоненты Краткое описание Версии Delphi
TDBGrid  Представление данных в виде таблицы (очень сложный и наиболее популярный компонент) 1 2 3
TDBEdit  Редактирования одного поля 1 2 3
TDBNavigator  Как видно из названия, компонент позволяет перемещаться по таблице 1 2 3
TDBLabel  Статическое отображение содержимого поля 1 2 3
TDBMemo Редактирования текста в поле типа BLOB 1 2 3
TDBImage Отображение картинок из BLOB-а 1 2 3
TDBRadioGroup, TDBCheckBox Дополнительные средства отображения данных 1 2 3
TDBComboBox, TDBListBox Упрощают ввод данных, предлагая несколько заранее определенных вариантов 1 2 3
TDBLookupListBox, TDBLookupComboBox То же самое, но возможные варианты выбираются из другой таблички 1 2 3
TDBCtrlGrid Вариант представления записей с произвольным расположением полей 2 3
TDBChart  Компонент для построение графиков и диаграмм на основании данных, хранящихся в таблице 3
TDecisionGrid, TDecisionChart Компоненты для поддержки принятия решений 3

2. Использование псевдонимов в запросе SQL.

Я делаю запрос по двум таблицам разных форматов, находящихся по разным псевдонимам.

SELECT DB1.Column1, DB2.Column2 FROM :Alias1:DB1, :Alias2:DB2

но в результате получаю ошибку 'неизвестный тип поля "Alias1:DB1"'

На самом деле вы получаете ошибку Unknown Keyword, следовательно всего-лишь нужно заключить псевдоним и имя таблицы в двойные кавычки.

SELECT D1.Column1, D2.Column2 FROM ":Alias1:DB1" D1, ":Alias2:DB2" D2

Вообще экспериментировать с SQL-запросами проще следующим образом — создайте запрос QBE, настройте его так, как вам нужно, а затем оттранслируйте его в SQL. В результате вы получите правильный текст нужного вам SQL-запроса. Владельцы Delphi Client/Server могут использовать также и Visual Query Builder. Однако, не все QBE-запросы могут быть оттранслированы в SQL.


3. Ошибка в SQL запросе.

У меня есть TQuery и TDataSource. В свойстве SQL для TQuery я пишу

SELECT * FROM dbo.AnyTable

база данных на MS SQL Server. Когда я устанавливаю Active в True, то получаю ошибку:'Token not found. Token :dbo. line number:1'. Что не так?

Если свойство RequestLive=True, то имя таблицы нужно брать в кавычки:

SELECT * FROM "dbo.table"

Если свойство RequestLive=False, кавычек не требуется:

SELECT * FROM dbo.table


4. Проблемы при работе с MS Access через TQuery.

Я безуспешно пытался использовать данные из Microsoft Access иначе, нежели просто с помощью TTable.  Используя TQuery я могу только читать результат, но не могу редактировать. После "login screen" возникает сообщение типа 'Passthrough SQL connection must be shared'.

Измените в настройке псевдонима (alias) пункт 'SQLPASSTHRU MODE' на 'SHARED AUTOCOMMIT'.


5. Как создать таблицу при помощи SQL (или почему не работает TQuery.Open)?

TQuery.Open возвращает результат в виде курсора, в связи с этим он работает только для тех выражений, которые возвращают курсор. CREATE TABLE возвращает только результат операции - поэтому для выполнения этого выражения необходимо использовать TQuery.ExecSQL. Но и это может не сработать, если конкретный драйвер БД не поддерживает операцию создания таблиц - для получения характеристик драйвера используйте функции BDE (DbiOpenDriverList, DbiGetDriverDesc). 

6. Возврат значения select max() и подобных SQL-выражений.

Я хочу выполнить SQL-выражение и получить результат в свою переменную, что-то типа

SELECT MAV(FieldA) FROM TableB INTO :VariableC;

Вам не нужно использовать оператор INTO для программного доступа к результату — его можно получить используя свойства Fields или FieldByName соответствующего компонента TQuery.

TQuery1.Add(' SELECT MAX(FiledA) FROM TableB ');

TQuery.Open;

...

VariableC := TQuery1.Fields[0];

Или, если результат нужно визуально отобразить, достаточно подключить к используемому TQuery компоненты TDataSource и TDBText.


7. Автоматический подсчет сумм при помощи TQuery.

Меня интересует возможность подсчета суммы по таблицам, которые уже находятся на форме.

Есть очень простой способ — предположим, что у вас есть на форме Query1, DataSource1, DBGrid1. Добавьте на эту же форму компоненты Query2, DataSource2, DBText1. Установите property Query2.DataSource=DataSource1. В Query2.SQL напишите

SELECT SUM(FieldName) FROM TableName

где TableName — имя той же таблицы что и у Query1, а FieldName — имя столбца по которому производится подсуммирование. Далее свяжите между собой Query2, DataSource2 и DBText1.

При изменении Query1 (если конечно Query1.RequestLive=True) Query2 будет автоматически перевыполняться. Это решение хоть и простое, но неэкономичное — особенно при большом количестве записей в исходной таблице. Более того, запрос Query2 должен иметь WHERE идентичный Query1.

Для подсчета сумм правильнее использовать событие TQuery.OnCalcFields. Хорошим примером является X:\DELPHI\DEMOS\DB\MASTAPP\MASTAPP.DPR. 


8. Использование кавычек в параметризированном запросе.

Мой запрос получает параметр. Проблема в том, что строка параметра содержит " (двойную кавычку), которая приводит к Runtime Error.

Вам необходимо использовать динамический SQL-запрос, иначе при указании например

WHERE TABLE.FIELD = 'let"ter'

вы получите ошибку.


9. Как создать отдельный компонент TTable?

Легко и просто — точно также как и обычный компонент. При этом в качестве параметра конструктору можно передавать значение nil.  

var 

  MyTable: TTable; 

begin 

  MyTable := TTable.Create(nil); 

  try 

    MyTable.DatabaseName := 'MyDB'; 

    MyTable.TableName := 'MyTable.db'; 

    Mytable.IndexName := 'MyIndex'; 

    MyTable.Open; 

    { делать то, что надо } 

  finally 

    MyTable.Free; 

  end; 

end;


10. Как узнать, какая ячейка при просмотре TDBGrid текущая?

Здесь процедура для сохранения текущего номера строки и колонки. Следующий код в методе MyDBGridDrawDataCell обновляет переменные Col и Row (которые не должны быть локальными для этого метода) каждый раз, когда таблица перерисовывается. Используя этот код, вы можете считать, что Col и Row указывают на текущую колонку и строку соответственно.  

var 

  Col, Row: Integer; 

procedure TForm1.MyDBGridDrawDataCell(Sender: TObject; const Rect: TRect; 

  Field: TField; State: TGridDrawState); 

var 

  RowHeight: Integer; 

begin 

  if gdFocused in State then 

  begin 

    RowHeight := Rect.Bottom - Rect.Top; 

    Row := (Rect.Top div RowHeight) - 1; 

    Col := Field.Index; 

  end; 

end; 


11. Как выделить цветом текущую строку в TDBGrid?

Для TDBGrid в свойстве Options установите dgRowSelect в True.


12. Как изменить цвет ячейки в TDBGrid?

Введите следующий код в обработчике события OnDrawDataCell:  

procedure TForm1.DBGrid1DrawDataCell(Sender: TObject; const Rect: TRect; 

  Field: TField; State: TGridDrawState); 

begin 

  if gdFocused in State then 

  with (Sender as TDBGrid).Canvas do 

  begin 

    Brush.Color := clRed; 

    FillRect(Rect); 

    TextOut(Rect.Left, Rect.Top, Field.AsString); 

  end; 

end;

Установите свойство DefaultDrawing в True. Здесь перерисовывается только выделенная ячейка. Если установить DefaultDrawing в False, то вы должны самостоятельно перерисовать все ячейки аналогично примеру.


13. Как узнать, что пользователь перешел на другую запись, например, в TDBGrid?

Переход на новую запись — это событие, которое относится не к визуальному компоненту, а к источнику данных. Соответствующее событие называется OnDataChange и имеется у компонента TDataSource.


14. Как устанавливать собственный цвет или шрифт для столбца TDBGrid?

Выключите property DefaultDrawing, и обрабатывайте событие OnDrawDataCell:  

procedure TForm1.DBGrid1DrawDataCell(Sender: TObject; const Rect: TRect; 

  Field: TField; State: TGridDrawState); 

begin 

  if Field.FieldName = 'Name' then DBGrid1.Canvas.Font.Style := [fsBold]; 

  DBGrid1.DefaultDrawDataCell(Rect, Field, State); 

end;    

Это приведет к тому, что содержимое столбца 'Name' будет показываться жирным шрифтом.

В Delphi 2.0 вы можете использовать редактор столбцов для той же самой цели.


15. Почему указатель ползунка в TDBGrid не показывает текущее положение в таблице?

Дело в том, что TDBGrid предполагает многопользовательский доступ к таблице. В этом случае другие пользователи этой же таблицы могут добавлять или удалять записи, в результате информация о количестве записей на текущий момент становится неопределенной.

Конечно, в однопользовательском варианте количество записей всегда известно, но поскольку TDBGrid работает через промежуточный источник данных DataSource, ему неизвестен конкретный способ доступа к данным — навигационный или SQL. Например, для SQL существует только один способ узнать количество записей — выполнить специальный запрос с их подсчетом, а на это может потребоваться значительное время.

По всем этим причинам TDBGrid является универсальным средством для просмотра таблиц, которое работает во всех случаях и с любыми источниками данных.


16. Как установить фокус на определенное поле в TDBGrid?

Используйте код:

DBGrid1.SelectedField := Table1SomeField;

DBGrid1.SetFocus;


17. Как создать обработчик события OnClick для TDBGrid?

Как и всякий TControl (иерархия наследования TControl#8594;TWinControl#8594;TCustomControl#8594;TCustomGrid#8594;TCustomDBGrid#8594;TDBGrid) у TDBGrid есть событие OnClick, но оно protected. Так что можно либо создать новый класс, производный от TDBGrid, в котором объявить это свойство как published, либо использовать другой вариант. Например, вы можете использовать событие OnColEnter.

18. Как создать маску для TDBEdit?


Маска относится к полю в таблице (компонент TField) а не к самому TDBEdit. Дважды щелкните мышкой на TTable и в FieldEditor'е добавьте все нужные вам поля. Когда поле выбрано в списке, его свойства показаны в Object Inspector, включая маску ввода. Связывание TDBEdit и любых других компонентов с этим TTable будет вызывать наложение маски на соответствующее поле.


19. Хотелось бы иметь для OLE объектов, сохраненных в базе данных, компонент вроде TDBImage.

В стандартном наборе такого компонента действительно нет. Возможно, кто-нибудь скоро напишет что-нибудь в этом роде. В принципе, можно обойтись и без данного компонента. Например, есть табличка .db с BLOB полем для OLE объекта. При движении по записям можно OLE сохранять в базе, уничтожать, создавать новый, считывать из базы.

Чтение/запись OLE:

• создать поток, связанный с BLOB полем

• для OLE контейнера выполнить чтение/запись с потоком (SaveToStream и LoadFromStream)

Естественно, OLE объект должен быть Embedded.


20. Что нужно сделать, чтобы при открытии запароленной таблицы не появлялся диалог запроса пароля?

Просто дайте этот пароль объекту Session перед открытием таблицы:

Session.AddPassword('PASSWORD');

После закрытия таблицы, пароль можно удалить RemovePassword('PASSWORD'), можно удалить все пароли: RemoveAllPasswords.

Если ваш компонент доступа к данным (TTable или TQuery) связан с сессий, отличной от той, которая выставляется по умолчанию, то добавлять пароль нужно именно у этого компонента TSession.


21. Как определить реальный размер поля типа BLOB, которое сохранено в таблице?

Ниже приведена функция GetBlobSize, которая возвращает размер данного BLOB или MEMO поля.  

function GetBlobSize(Field: TBlobField): Longint; 

begin 

  with TBlobStream.Create(Field, bmRead) do 

  try 

    Result := Seek(0, 2); 

  finally 

    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; 


22. Как осуществить поиск по неиндексированному полю в таблице?

Вы можете добавить следующую функцию в ваш модуль и вызвать, например:

Locate(Table1, Table1LName, 'Beman');

Table1 — компонент TTable, Table1LName — TField, который вы добавили с помощью Fields Editor и 'Beman' - имя, которое вы хотите найти.


23. Как узнать, что изменилась текущая запись?

Событие TDataSource.OnDataChange когда State=dsBrowse.


24. Как считать даты для вычисляемых полей?

При вычислении дат важно удостовериться в том, что все используемые значения подходят по типу. В документации не совсем явно отражен тот факт, что на самом деле тип TDataTime эквивалентен типу Double, который можно использовать далее.

В примере, D1 и D2 (поля в Table1) могут быть типа или Date, или TDateTime, а D3 — поле типа Integer.  

procedure TForm1.Table1CalcFields(DataSet: TDataset); 

var 

  T1, T2: TDateTime; 

begin 

  Table1D1.AsDateTime := Date+2; 

{ or Table1D1.Value := Date+2; } 

  Table1D2.AsDateTime := Date-2; 

  T1 := Table1D1.AsDateTime; 

  T2 := Table1D2.AsDateTime; 

  Table1D3.AsInteger := Trunc(Double(T1) - Double(T2)); 

end;