Валентин Озеров - Советы по Delphi. Версия 1.4.3 от 1.1.2001
Delphi 1
Все это я делал раньше. Я не могу вам все это показать на развернутом примере, но я дам вам идею как сделать это. Вы должны иметь таблицу, осуществляющую взаимоотношение между людьми. Если на Peter работают Jane и Simon, вы должны иметь таблицу (RELATION) с этими двумя записями.
Master
Slave ------- имена полей
Peter Jane
Peter Simon
Если George и Elisa работают на Jane, то таблица становится такой:
Master Slave ------- имена полей
Peter Jane
Peter Simon
Jane George
Jane Elisa
и так далее.
Если в таблице RELATION необходимо создать дерево, начинающееся на Peter, то нужно добавить к дереву главный узел (запись), где Master = Peter. Затем каждая дочерняя запись располагается ниже записи Master = Peter. После добавления дочерней записи вы сразу увидите, если ребенок имеет собственного ребенка. Ребенок становится теперь, вероятно, отцом, поэтому вы должны позиционировать таблицу RELATION к первой записи, где Master = child, и так далее, рекурсивно. Такой способ гарантирует построение правильного дерева.
Пример:
AddFather('Peter')
AddChild('Peter',1)
Procedure AddFather(Name: String)
Begin
Tree.Add(Name);
End;
Procedure AddChildr(Name: String, Index:Integer)
Begin
Relation.FindKey([Name])while RelationMaster.AsString = Name do
Begin
Tree.AddChild(Index,RelationSlave.AsString);
AddChild(RelationSlave.AsString,Tree.ItemsCount);
Relation.Next;
End;
End;
По-моему, ошибок нет.
DBGrid и Memo-поля
Delphi 1
В обработчик события GetText TMemoField поместите следующую строку:
Text := GrabMemoAsString(TMemoField(Sender));
и поместите следующую функцию так, чтобы к ней можно было свободно обратиться:
function GrabMemoAsString(TheField : TMemoField): String;
begin
if TheField.IsNull then Result := '' else with TBlobStream.Create(TheField, bmRead) do begin
if Size >= 255 then begin
Read(Result[1], 255);
Result[0] := #255;
end else begin
Read(Result[1], Size);
Result[0] := Chr(Size);
end;
Free;
while Pos(#10, Result) > 0 do Result[Pos(#10, Result)] := ' ';
while Pos(#13, Result) > 0 do Result[Pos(#13, Result] := ' ';
end;
end;
Убывающий индекс
Delphi 1
Я нашел простой способ получения убывающего индекса. В Delphi это получается очень легко и красиво:
Table1.AddIndex('NewIndex', 'CustNo;CustName', [ixDescending]);
Как работать из Delphi напрямую с MS ADO (Microsoft Active Data Objects)?
Nomadic отвечает:
Итак, хочу поделиться некоторыми достижениями… так на всякий случай. Если у вас вдруг потребуется сделать в своей программке доступ к базе данных, а BDE использовать будет неохота (или невозможно) – то есть довольно приятный вариант: использовать ActiveX Data Objects. Однако с их использованием есть некоторые проблемы, и одна из них это как передавать Optional параметры, которые вроде как можно не указывать. Однако, если вы работаете с ADO по-человечески, а не через тормозной IDispatch.Invoke то это превращается в головную боль. Вот как от нее избавляться:
var
OptionalParam: OleVariant;
VarData: PVarData;
begin
OptionalParam := DISP_E_PARAMNOTFOUND;
VarData := @OptionalParam;
VarData^.VType := varError;
после этого переменную OptionalParam можно передавать вместо неиспользуемого аргумента.
Далее, самый приятный способ получения Result sets:
Там есть масса вариантов, но как выяснилось оптимальным является следующий вариант, который позволяет получить любой желаемый вид курсора (как клиентский, так и серверный)
var
MyConn: _Connection;
MyComm: _Command;
MyRecSet: _Recordset;
prm1: _Parameter;
begin
MyConn := CoConnection.Create;
MyConn.ConnectionString := 'DSN=pubs;uid=sa;pwd=;';
MyConn.Open('', '', '', –1);
MyCommand := CoCommand.Create;
MyCommand.ActiveConnection := MyConn;
MyCommand.CommandText := 'SELECT * FROM blahblah WHERE BlahID=?'
Prm1 := MyCommand.CreateParameter('Id', adInteger.adParamInput, –1, <value>);
MyCommand.AppendParameter(Prm1);
MyRecSet := CoRecordSet.Create;
MyRecSet.Open(MyCommand, OptionalParam, adOpenDynamic, adLockReadOnly, adCmdText);
…теперь можно фетчить записи. Работает шустро и классно. Меня радует. Особенно радуют серверные курсоры.
Проверялось на Delphi 3.02 + ADO 1.5 + MS SQL 6.5 sp4. Пашет как зверь.
Из вкусностей ADO – их легко можно использовать во всяких многопоточных приложениях, где BDE порой сбоит, если, конечно, ODBC драйвер грамотно сделан…
Ну и еще можно использовать для доступа к данным всяких там «нестандартных» баз типа MS Index Server или MS Active Directory Services.
В Delphi (как минимум в 4 версии) существует «константа» EmptyParam, которую можно подставлять в качестве пустого параметра.
Как засунуть в качестве паpаметpа хpанимой пpоцедуpы стpоку длиной более 255 символов? И вообще, как использовать паpаметpы SP, если они BLOB?
Nomadic отвечает:
«Засунуть» длинную строку можно было и раньше, если написать редактируемый запрос, и воспользоваться операциями Insert/Edit.
Однако это не относится к хранимым процедурам.
В Delphi 3.0 появился новый тип параметра (TBlobField вроде) и соответственно его поддержка в BDE.
Если просто взять BDE 4.01 и выше, то работать все-равно не будет – нужна соотв. версия VCL (из Delphi 3.0 или выше).
Дублирование набора записей
Delphi 1
Вы можете воспользоваться вторым объектом TTable, подключенным к той же таблице, или можете вызвать метод объект TTable DisableControls, сделать изменения, и вызвать EnableControls. Для сохранения той же позиции вы можете попробовать воспользоваться закладкой. Например, так:
procedure TMyForm.MakeChanges;
var
aBookmark: TBookmark;
begin
Table1.DisableControls;
aBookmark := Table.GetBookmark;
try
{ваш код}
finally
Table1.GotoBookmark(aBookmark);
Table1.FreeBookmark(aBookmark);
Table1.EnableControls;
end;
end;
Как программно изменить LangDriver для таблиц dBase и Paradox?
Nomadic отвечает:
Откpываешь help и смотpишь:
……
var list:tstrings;
……
BEGIN
…….
List.Add ( 'LANGDRIVER=db866ru0 ');
……
Session.ModifyDriver( 'DBASE', List );
……
END;
Это действие я пpовожy пеpед откpытием таблицы
Ivan Sboev
(2:5049/36.15)
Это о «русификации» таблицы. В таблицах dBase и Paradox имеется байт, который определяет CodePage содержимого таблицы. Раньше он не использовался и был зарезервирован. Тебе нужно его правильно установить. Это делается через DBD Restructure table. Если хочешь программно, можешь воспользоваться следующей процедурой:
uses DbiTypes, DbiProcs, DbiErrs, DB, WinProcs, SysUtils;
procedure ChangeLangDriver( DatabaseName, TableName, LDName: string );
var
TblExt: string;
Database: TDatabase;
TblDesc: CRTblDesc;
OptDesc: FLDDesc;
OptData: array [0..250] of Char;
Cur: hDBICur;
Rec: CFGDesc;
begin
if (TableName='') or (LDName='') then raise Exception.Create('Unknown TableName or LDName');
Database:=Session.OpenDatabase(DatabaseName);
try
if Database.IsSQLBased then raise Exception.Create('Function ChangeLangDriver working only with dBase or Paradox tables');
FillChar(OptDesc, SizeOf(OptDesc), #0);
FillChar(TblDesc, SizeOf(TblDesc), #0);
StrCopy(OptDesc.szName, 'LANGDRIVER');
OptDesc.iLen := Length(LDName) + 1;
with TblDesc do
begin
StrPCopy(szTblName, TableName);
TblExt := UpperCase(ExtractFileExt(TableName));
if TblExt = 'DBF' then StrCopy(szTblType, szDbase)
else if TblExt = '.DB' then StrCopy(szTblType, szParadox)
else
begin
AnsiToOEM(StrPCopy(OptData, DatabaseName), OptData);
if DbiOpenCfgInfoList(nil, dbiREADONLY, cfgPersistent, StrPCopy(OptData, 'DATABASES' + StrPas(OptData) + 'DB INFO')Cur) <> DBIERR_NONE then raise Exception.Create('Unknown table type');
try
while DbiGetNextRecord(Cur, dbiNOLOCK, @Rec, nil) <> DBIERR_EOF do if StrComp(Rec.szNodeName, 'DEFAULT DRIVER') = 0 then
begin