Вопросы и ответы - Delphi - Базы данных - Библиотека программиста
Пользователь

Добро пожаловать,

Регистрация или входРегистрация или вход
Потеряли пароль?Потеряли пароль?

Ник:
Пароль:

Меню сайта




Ваше мнение
Как вы узнали о нашем сайте?

От друга, знакомого
Из печатных источников
Из поисковой машины
По ссылке с другого сайта
Случайно
Не знаю


Результаты
Другие опросы

Всего голосов: 966
Комментарии: 4


Наши партнеры



Статистика




Programming books  Download software  Documentation  Scripts  Content Managment Systems(CMS)  Templates  Icon Sets  Articles  Contacts  Voting  Site Search




Вопросы и ответы - Delphi - Базы данных

                  
 
Access to table disabled because of previous error. Read failure
При добавлении новых записей с помощью метода TTable.AppendRecord в индексированную таблицу FoxPro через какое-то время (то есть при одновременном добавлении большого количества записей) возникает ошибка:

"Access to table disabled because of previous error. Read failure. File <имя_файла.cdx>".

Возможно, причина заключается в том, что операции чтения-записи в файл, содержащий таблицу FoxPro, особенно при использовании кэширования, предоставляемого операционной системой, конфликтуют с содержимым индексного файла (это часто происходит при многопользовательской работе). Дело в том, что ранние версии dBase, FoxPro, Clipper работали с неподдерживаемыми индексами, то есть индексные файлы не обновлялись одновременно с таблицей, и для их синхронизации требовалось выполнять специальные операции. Но соответствующие средства разработки, применявшиеся в то время, обычно не поддерживали никаких аналогов транзакций - записи чаще всего вставлялись по одной.

В случае применения старых версий формата FoxPro следует избегать кэширования при выполнении дисковых операций с файловым сервером, содержащим базу данных FoxPro. Кроме того, следует проверить и, если необходимо, изменить в настройках BDE параметры MINBUFSIZE, MAXBUFSIZE, LOCAL SHARE - возможно, проблема заключается в недостаточной величине буферов BDE для кэширования данных или в одновременном доступе к данным приложений, использующих и не использующих BDE.

Еще одним из способов решения этой проблемы (самым радикальным) является замена FoxPro на какую-нибудь из серверных СУБД. Например, InterBase неплохо справляется с одновременным вводом большого количества записей благодаря некоторым специфическим особенностям архитектуры этого сервера.
BDE: несколько SQL-запросов одним махом
BDE такую возможность не поддерживает, но есть компонент в библиотеке RxLib - TSQLScript (соответственно и в JVCL - TJvSQLScript), поставляются они в исходниках, и переделать их на любой другой доступ проще-простого. Компонет делает парсинг SQL и выполняет несколько SQL запросов.
Byte-поля Paradox
Что за магия при записи в поле Paradox Byte? По этому поводу в документации ничего не сказано.

Есть 2 пути получить доступ к данным в TBytesField.

Просто вызовите метод GetData, передавая ему указатель на буфер, где сам буфер должен иметь размер, достаточный для хранения данных:

Код
procedure SetCheckBoxStates;
var
CBStates: array[1..13] of Byte;
begin
CBStateField.GetData(CBStates);
{ Здесь обрабатываем данные... }
end;


Для записи значений вы должны использовать SetData.

Используйте свойство Value, возвращающее вариантный массив байт (variant array of bytes):

Код
procedure SetCheckBoxStates;
var
CBStates: Variant;
begin
CBStates := CBStateField.Value;
{ Здесь обрабатываем данные... }
end;


Первый метод, вероятно, для вас будет легче, поскольку вы сразу докапываетесь до уровня байт. Запись данных также получится сложнее, поскольку вам нужно будет работать с variant-методами типа VarArrayCreate и др.
Database index out of date error
Некоторое время назад у меня также была масса ошибок типа 'index out of date' и даже искажение данных. После продолжительного исследования я выяснил причину, она оказалось в различных установках Paradox Language в BDE (v1 и V3) на странице Driver и System в утилите конфигурирования BDE. Я не обратил внимание на установки на странице System одной из рабочих станций, и получил искажение данных.
DBFSeek и DBFLocate
Надежней и намного быстрее (если вы ищите отдельные записи) выполнить поиск строки с помощью Seek (если найдена первая запись), или выполнить Locate (индекс не требуется)

например:

Код
Table1.UpdateCursorPos;
if DBFSeek( Table1, xVal1 ) then {_не_ delphi-функция - смотри ниже}
begin
if DBFLocate( Table1, 'CUSTNAME', xVal2 ) then {_не_ delphi-функция - модификация из faq}
begin
//... делаем все, что необходимо
end;
end;


DBFLocate - модифицированная из faq фунция fieldname

DBFSeek - функция, найденная методом проб и ошибок! - значительно лучшая (IMHO) чем setkey...fieldbyname1...fieldbyname2...gotokey, используемые для выражений индексов dBase за первым полем.
DOS DBF файлы – перекодировка между форматами
При использовании DOS DBF файлов можно сделать небольшую программку (или процедурку), которая произведет перекодировку между форматами. что-то типа:

Код
function update_dos(s:string):string;
var c:STRING;
I:INTEGeR;
l:byte;
dd:char;
begin
i:=1;
c:='';
while i< length(s)+1 do
begin
l:=ord(s[i]);
inc(i);
if (l>=128) and (l<=192)then l:=l+64 else
if (l>=224) and (l<240) then l:=l+16 else
if l=241 then l:=184 else
if l=240 then l:=168;
dd:=chr(l);
c:=c+dd;
end;
update_dos:=c;
end;

function update_win(s:string):string;
var c:STRING;
I:INTEGeR;
l:byte;
dd:char;
begin
i:=1;
c:='';
while i< length(s)+1 do
begin
l:=ord(s[i]);
inc(i);
if (l>=192) and (l<240)then l:=l-64 else
if (l>=240) and (l<256) then l:=l-16 else
if l=184 then l:=241 else
if l=168 then l:=240;
dd:=chr(l);
c:=c+dd;
end;
update_win:=c;
end;
ENoResultSet Error creating cursor handle
Почему не работает Query.Open(Query.ExecSQL)?

Что значит "ENoResultSet Error creating cursor handle"?

1.Query.Open возвращает результат запроса в виде курсора(Cursor).

Когда мы выполняем запрос «select * from table1» мы получаем

Набор данных (Cursor). Можете представит курсор как виртуальную таблицу, со строками и столбцами, определенными в запросе. В этом случае надо использовать Query.Open или Query.Active:=true;

2.Query.ExecSQL выполняет инструкции запроса и курсор не создается.

Если в запросах используются инструкции не создающие набор данных (курсор) – СREATE TABLE, INSERT, DELETE, UPDATE , SELECT INTO и т.д. то нужно вызывать метод ExecSQL.

Local SQL и временная таблица
Local SQL не поддерживает вложенные запросы, но после того как я заработал клок седых волос, я нашел в высшей степени простое решение: использование временной таблицы.

Пример:

Код
with GeneralQuery do
begin
SQL.Clear;
SQL.Add(.... внутренний SQL);
SQL.Open;
DbiMakePermanent(handle, 'temp.db',true);
SQL.Clear;
SQL.Add(SELECT ... FROM 'temp.db'....);
SQL.Open;
end;


Единственное: необходимо убедиться в том, что имя таблицы не вступает в конфликт с именами нескольких работающих копий таблицы. И, разумеется, данная технология не даст "живой" набор!


Memo too large
В BDE есть крутая ошибка, достаточно известная всем, кроме Borland'a. Поскольку они ее еще с 1й Delphi не исправили. Этот баг проявляется как Access Violation в программе при обращении к таблице IB, которая содержит более одного поля типа VARCHAR (или CHAR) размером > 255. Причем, первое поле меньшего, а второе большего размера. Если поменять местами поля или сделать их одного размера, то все нормально.

Эффект имеет место только с IB, вроде.

Multiple records found, but only one was expected
При выполнении некоторых живых запросов, возвращающих единственную запись, BDE ругается 'multiple records found, but only one was expected'.

Запросы вида
Код
SELECT c, b, a, q FROM T WHERE b = :b

где ключ c, но BDE посчитала ключом a. Интересный запрос, да? Такое впечатление, что, поскольку ключом в исходной таблице являлась третья колонка, то Дельфы посчитали ключом третью колонку.

Перестановкой SELECT a, b, c, q... все исправилось. Я решил теперь использовать в таких (live) запросах только SELECT *.
ORACLE - как получить текущую дату?
Код
// make the SQL dependent on type of DBMS

if AppLibrary.Database.DriverName = 'ORACLE' then
SQL.Add('and entry_date < SYSDATE')
else
SQL.Add('and entry_date < "TODAY"');
end;
Stream Read Error
В моем автономном приложении при чтении/записи из моей базы данных с помощью BDE проблем не возникает. Когда я выгружаю .EXE на наш сетевой том NetWare 3.11, я получаю случайные сообщения об ошибке "Stream Read Error" (ошибка чтения потока). В сети у меня имеется BDE, но пользователи имеют на своих жестких дисках собственные файлы IDAPI.CFG. Может мне кто-нибудь прояснит ситуацию?

В программе конфигурирования Database Engine Configuration, на закладке 'system', попробуйте изменить значение по умолчанию для MAXFILEHANDLES с 48 на 12. Не знаю почему, но это решило мои проблемы, у меня исчезли ошибки 'Stream read error' и различные GPF-ы.
TDBGrid со свойствами Col и Row
Код
{
Код улучшенного TDBGrid, имеющего свойства Col,
Row и Canvas и метод CellRect. Это чрезвычайно
полезно в случае, если вы, к примеру, хотите
получить выпадающий список на месте редактируемой
пользователем ячейки.
}

unit VUBComps;

interface

uses

SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, Dialogs, Grids, DBGrids, DB, Menus;

type

TDBGridVUB = class(TDBGrid)
private
{ Private declarations }
protected
{ Protected declarations }
public
property Canvas;
function CellRect(ACol, ARow: Longint): TRect;
property Col;
property Row;

procedure Register;

implementation

procedure Register;
begin

RegisterComponents('VUBudget', [TDBGridVUB]);
end;

function TDBGridVUB.CellRect(ACol, ARow: Longint): TRect;
begin

Result := inherited CellRect(ACol, ARow);
end;

end.
TDBGrid – сохранение конфигурации
Нижеописанный код создает, сохраняет и загружает конфигурационный файл и изменяет размеры столбцов таблицы DBGRID

Код
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, Grids, DBGrids, Db, DBTables, StdCtrls, IniFiles;
...

procedure TMainForm.NewIni(const NomeIni: string);
var
F: System.Text;
i: Byte;
begin
System.Assign(F, NomeIni);
System.ReWrite(F);
System.WriteLn(F, '[Campi_Ordine]');
for i:=1 to Table1.FieldCount do
System.WriteLn(F, 'Campo',i,'=',Table1.Fields[i-1].FieldName);
System.WriteLn(F, '');
System.WriteLn(F, '[Campi_Size]');
for i:=1 to Table1.FieldCount do
System.WriteLn(F, 'Campo',i,'=',Table1.Fields[i-1].DisplayWidth);
System.Close(F);
end;

procedure TMainForm.SaveIni(const FN: string);
var
Ini: TIniFile;
i: Integer;
S : string;
begin
NewIni(FN);
Ini := TIniFile.Create(FN);
with Ini do begin
for i:=1 to Table1.FieldCount do
begin
S:= Table1.Fields[i-1].FieldName;
WriteString('Campi_Ordine', 'Campo'+IntToStr(i),
Table1.Fields[i-1].FieldName);
WriteInteger('Campi_Size', 'Campo'+IntToStr(i),
Table1.Fields[i-1].DisplayWidth);
end;
end;
Ini.Free;
end;

procedure TMainForm.LoadIni(const FN: string);
var
Ini: TIniFile;
i: Integer;
j: Longint;
S: string;

function MyReadInteger(const Section, Ident: string): Longint;
begin
result := Ini.ReadInteger(Section, Ident, -1);
if result=-1 then
raise Exception.Create('Errore nel file di configurazione.');
end;

function MyReadString(const Section, Ident: string): string;
begin
result := Ini.ReadString(Section, Ident, '');
if result='' then
raise Exception.Create('Errore nel file di configurazione.');
end;

begin
Ini := TIniFile.Create(FN);
try
with Ini do
begin
for i:=1 to Table1.FieldCount do
begin
S:= MyReadString('Campi_Ordine', 'Campo'+IntToStr(i));
j:= MyReadInteger('Campi_Size', 'Campo'+IntToStr(i));
Table1.FieldByName(S).index := i-1;
Table1.FieldByName(S).DisplayWidth := j;
end;
end;
finally
Ini.Free;
end;
end;
TDBGrid.CutToClipboard
Внутри TDBGrid "зашит" защищенный (protected) элемент управления типа TInPlaceEdit, потомок TCustomMaskEdit. Данный элемент управляется комбинацией клавиш [Shift]+[Ins] и [Shift]+[Del]. Но для нас не существует способа оперировать элементом, поскольку он является защищенным членом.

Да, но вы можете сделать это обманным путем. Попробуйте так:

Код
procedure TForm1.Paste1Click(Sender: TObject);
begin
SendMessage(GetFocus, WM_PASTE, 0, 0);
end;

procedure TForm1.Copy1Click(Sender: TObject);
begin
SendMessage(GetFocus, WM_COPY, 0, 0);
end;


Эти методы привязаны к вашим пунктам меню. Они посылают сообщение окну с текущим фокусом. Если это элемент управления TInPlaceEdit, то мы добились того, чего хотели.
TDBGrid.DefaultDrawDataCell
TDBGrid имеет недокументированный в электронной справке метод DefaultDrawDataCell.

Вот пример его использования:

Код
procedure TForm1.DBGrid1DrawDataCell(Sender: TObject;
const Rect: TRect; Field: TField; State: TGridDrawState);
begin
with Sender as TDBGrid do
begin
Canvas.Font.Color := clRed;
DefaultDrawDataCell(Rect, Field, State);
end;
end;
Буфер обмена и ячейки TDBGrid
Внутренний (in-place) редактор является защищенным свойством TCustomGrid, поэтому тут придется немного поколдовать. Вы можете сделать приблизительно так:

Код
type
TMyCustomGrid = class(TCustomGrid)
public
property InplaceEditor;
end;

...

if ActiveControl is TCustomGrid then
TMyCustomGrid(ActiveControl).InplaceEditor.CopyToClipboard;
Быстрая обработка CSV файла
Классы Tstrings/TStringlist имеют свойство commatext, которое автоматически разделяет строки, содержащие разделители, на отдельные части. Пример показывает как считать CSV файл. В Конечном итоге, автоматически разделённые строки содержатся в TStringlist.

Код
var
ts: tstringlist;
S: string;
Tf: Textfile;
begin
Ts := Tstringlist.create;
Assignfile(tf, 'filename');
Reset(tf);
while not eof(tf) do
begin
Readln(tf,S);
Ts.CommaText := S;
//ProcessLine;
end;
closefile(tf);
ts.free;
end;


Так же операцию можно производить в обратном порядке.

Свойство Commatext поддерживает разделители как в виде запятых, так и двойных кавычек: 1,2,3,4 и "1","2","3","4"

Например, строка вида "1","2,3","4" будет разделена на три элемента, которые заключены в кавычки (средняя запятая будет проигнорирована). Чтобы включить кавычку в конечный результ, нужно поставить две кавычки подряд: "1",""2" (результат будет 1 и "2 ).
Быстрое копирование таблиц
Код
procedure QuickCopyTable(T: TTable;DestTblName:string;Overwrite: boolean);
// только для не SQL-ых, т.е не промышленных БД (dBase, Paradox ..)
var DBType: DBIName;
WasOpen:boolean;
NumCopied:word;
begin
WasOpen:=T.Active;
if not WasOpen then T.Open;
Check(DbiGetProp(hDBIObj(T.Handle),drvDRIVERTYPE,@DBType,SizeOf(DBINAME),
NumCopied));
Check(DbiCopyTable(T.DBHandle, Overwrite, PChar(T.TableName),DBType, PChar(DestTblName)));
T.Active:=WasOpen;
end;
Быстрое копирование таблиц вместе со всеми файлами
Код
// Только для не SQL-ых, т.е не промышленных БД (dBase, Paradox ..)
// Путь нужно задавать только АНГЛИЙСКИМИ буквами
procedure QuickCopyTable(T: TTable; DestTblName: string; Overwrite: boolean);
var
DBType: DBIName;
WasOpen: boolean;
NumCopied: word;
begin
WasOpen := T.Active;
if not WasOpen then
T.Open;
Check(DbiGetProp(hDBIObj(T.Handle),drvDRIVERTYPE, @DBType,SizeOf(DBINAME), NumCopied));
Check(DbiCopyTable(T.DBHandle, Overwrite, PChar(T.TableName),DBType, PChar(DestTblName)));
T.Active := WasOpen;
end;


Печать страницы
Печать страницы




Внимание! Если у вас не получилось найти нужную информацию, используйте рубрикатор или воспользуйтесь поиском


.



книги по программированию исходники компоненты шаблоны сайтов C++ PHP Delphi скачать