Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Базы данных и репортинг > Key violations error ????


Автор: Serg1234 12.10.2006, 08:53
Привет всем.
Есть таблица Paradox с главным индексом по автоинкрементальному полю и вторичными индексами.
Пол года база работала нормально, а теперь пытаюсь вставить новую запись в таблицу и получаю ошибку Key violations error....?  
Это испортилась база и ошибка в коде программы???
Что делать как лечить???
Подскажите пожалуйста.

Автор: php 12.10.2006, 09:05
Как пытаешься вставить новую строку ?

Автор: Serg1234 12.10.2006, 12:19
Код

Table1.Insert
....
Table1.Post

ошибка вылазит на строке Table1.Post

Автор: php 13.10.2006, 13:13
Поробуй 
Код

try Table1.Append();

except ShowMessage('Внутренняя ошибка'); end;


и тоже самое для:
Код

try Table1.Post();

except ShowMessage('Ошибка'); end;

Автор: samurai 16.10.2006, 09:26
ошибки из личного опыта:

1. key violation вылазил, тогда когда пытался редактировать запись с автоинкрементальным ключом,
    возможно для добавления новой нужно использовать не Insert а Append, он подберет следующее значение автоинкремента.
2. если таблицы связанные, то следить за порядком заполнения таблиц и вместо Table1.post использовать
    Table1.CachedUpdates:=true; это позволит вносить изменение привыходе из программы и при закрытии формы
3. у меня было много ошибок с автоинкрементом и я от него избавился, BDE капризная штука smile
4. Если раньше работало и вдруг перестало - значит сломались индексы, т.е. нужно их удалить и заново создать - 
    можно в ручную - DataBase Desktop, можно прямо во время ошибки программно, при условии отсутствия blob полей,
    их желательно в отдельную таблицу

Автор: Akella 19.10.2006, 16:52
скорее всего, что Primary индекс улетел в тёплые края

Автор: Serg1234 20.10.2006, 12:28
Спасибо всем кто откликнулся.
Судя по всему проблемы возникли с автоинкрементальным полем.
Я в DataBase Desktop переопределил автоинкрементальное поле(поле по которому создан Primary индекс) как  integer и сохранил базу. Потом это поле опять сделал автоинкрементальным, что позволило создать индексы заново.
samurai,  а как это сделать программно?

Автор: Aleksej 21.10.2006, 18:17
для этого есть fieldDefs, а потом create table. или через Query. так же можно использовать функции с модуля BDE.
кстати, на table1.append(); тоже ругается?

Автор: Serg1234 22.10.2006, 13:00
На table1.append тоже ругалась, пока я не создал индексы заново.
Теперь вопрос как программно создать индексы по автоинкрементальному полю.
Я создал новую таблицу в которой автоинкрементальное поле(IncremField) представил как Integer, перенес данные из старой таблицы
Код

spisok_table.IndexName:='';spisok_table.CancelRange;spisok_table.First;
 While Not(spisok_table.Eof) do
  Begin
   Table_new.Insert;
   For i:=0 to spisok_table.FieldCount-1 do Table_new.Fields.Fields[i]:=spisok_table.Fields.Fields[i];
   Table_new.Post;
   spisok_table.Next;
  End;
 Table_new.Active:=False;Table_new.FieldDefs.Items[IncremField].DataType:=ftAutoinc;

Осталось поменять тип поля на autoincremen и создать индекс по этому полю.
Но не получается smile 

P.s. Если кто увидел ошибки в коде - напишите пожалуйста. Заранее благодарен.


Автор: Akella 23.10.2006, 15:49
Цитата(Serg1234 @  22.10.2006,  13:00 Найти цитируемый пост)
и создать индекс по этому полю.


Код

  DB.Connected:=False;
  tNew.Close;
  tNew.DatabaseName:=sPathForNewDB;
  //создаем справочник аптек
  if fmSelFilesDB.cbCreateApt.Checked then begin
    tNew.FieldDefs.Clear;
    tNew.IndexDefs.Clear;
    tNew.TableName:='Apteka.db';
    tNew.TableType:=ttDefault;
    tNew.FieldDefs.Add('AID',ftAutoInc);
    tNew.FieldDefs.Add('AName',ftString,20,False);
    tNew.FieldDefs.Add('AAddres',ftString,40,False);
    tNew.FieldDefs.Add('ATel',ftString,40,False);
    tNew.FieldDefs.Add('AMAIL',ftString,30,False);
    tNew.FieldDefs.Add('AFA',ftString,10,False);


    tNew.IndexDefs.Add('','AID',[ixPrimary]);// <--- вот оно


    tNew.IndexDefs.Add('idxAName','AName',[ixCaseInsensitive]);
    tNew.IndexDefs.Add('idxAAddres','AAddres',[ixCaseInsensitive]);
    tNew.IndexDefs.Add('idxATel','ATel',[ixCaseInsensitive]);
    tNew.IndexDefs.Add('idxAMail','AMail',[ixCaseInsensitive]);
    tNew.IndexDefs.Add('idxAFA','AFA',[ixCaseInsensitive]);
    tNew.CreateTable;
  end;//if

Автор: Serg1234 23.10.2006, 19:21
Цитата(Akella @  23.10.2006,  15:49 Найти цитируемый пост)
tNew.FieldDefs.Add('AID',ftAutoInc);


На этапе создания таблицы я не могу полю присвоить тип Autoincrement, потому, что номера Autoincrement поля в новой таблице не будут совпадать с номерами в старой таблице, а по этому номеру связаны таблицы.
На этапе копирования из старой таблицы приходится создавать поле типа Integer и копировать в него значение поля из старой таблицы.

А вот после этого необходимо изменить тип поля на Autoincrement - и с этим основная проблема.

Автор: Akella 24.10.2006, 10:04
Код

iFieldIndex:integer;
...

  tNew.FieldDefs[iFieldIndex].DataType := ftAutoInc;

Автор: Serg1234 24.10.2006, 23:39
Цитата(Akella @  24.10.2006,  10:04 Найти цитируемый пост)
tNew.FieldDefs[iFieldIndex].DataType := ftAutoInc;


Я так пробовал, ничего не происходит. Может после этой строки надо что то обновить?

На форуме нашел пример использования функции DbiDoRestructure
С её помощью получается изменить тип поля(например с Integer в Float, или Int16 to Int32), но сделать поле Autoincr..  - не получается. smile 

Автор: Akella 25.10.2006, 09:12
Цитата(Serg1234 @  24.10.2006,  23:39 Найти цитируемый пост)
Я так пробовал, ничего не происходит. Может после этой строки надо что то обновить?

чесно признАюсь - не знаю как.

Добавлено @ 09:13 
не сталкивался с реструктуризацией Paradox таблицы программно.

Добавлено @ 09:16 
поищи в справке или в рунете по ключевому слову "DataType", мож пример найдёшь.

Добавлено @ 09:20 
Лично я советую тебе воспользоваться TQuery

Цитата

Создание таблиц с помощью SQL 

Если Вы хотите воспользоваться компонентом TQuery, сначала поместите его на форму. После этого настройте свойство DatabaseName на нужный Вам алиас (если базы данных еще не существует, удобней создать ее в WISQL командой File|Create Database..., а затем уже настроить на нее новый алиас). После этого можно ввести SQL-предложение в свойство SQL. Для выполнения запроса, изменяющего структуру, вставляющего или обновляющего данные на сервере, нужно вызвать метод ExecSQL компонента TQuery. Для выполнения запроса, получающего данные с сервера (т.е. запроса, в котором основным является оператор SELECT), нужно вызвать метод Open компонента TQuery. Ёто связано с тем, что BDE при посылке запроса типа SELECT открывает так называемый курсор, с помощью которого осуществляется навигация по выборке данных (подробней об этом см. в уроке, посвященном TQuery). 

Как показывает опыт, проще воспользоваться утилитой WISQL. Для этого в WISQL выберите команду File|Run an ISQL Script... и выберите файл, в котором записан ваш скрипт, создающий базу данных. После нажатия кнопки УOKФ ваш скрипт будет выполнен, и в нижнее окно будет выведен протокол его работы. 

Приведем упрощенный синтаксис SQL-предложения для создания таблицы на SQL-сервере InterBase (более полный синтаксис можно посмотреть в online-справочнике по SQL, поставляемом с локальным InterBase): 

CREATE TABLE table
( [,  |  ...]);

где 

table - имя создаваемой таблицы, 
- описание поля, 
- описание ограничений и/или ключей (квадратные скобки [] означают необязательность, вертикальная черта | означает УилиФ). 

Описание поля состоит из наименования поля и типа поля (или домена - см. урок 9), а также дополнительных ограничений, накладываемых на поле: 


 = col {datatype | COMPUTED BY () | domain}
[DEFAULT {literal | NULL | USER}]
[NOT NULL] []
[COLLATE collation]

Здесь: 

col - имя поля; 
datatype - любой правильный тип SQL-сервера (для InterBase такими типами являются - см. урок 11 - SMALLINT, INTEGER, FLOAT, DOUBLE PRECISION, DECIMAL, NUMERIC, DATE, CHAR, VARCHAR, NCHAR, BLOB), символьные типы могут иметь CHARACTER SET - набор символов, определяющий язык страны. Для русского языка следует задать набор символов WIN1251; 
COMPUTED BY () - определение вычисляемого на уровне сервера поля, где - правильное SQL-выражение, возвращающее единственное значение; 
domain - имя домена (обобщенного типа), определенного в базе данных; 
DEFAULT - конструкция, определяющая значение поля по умолчанию; 
NOT NULL - конструкция, указывающая на то, что поле не может быть пустым; 
COLLATE - предложение, определяющее порядок сортировки для выбранного набора символов (для поля типа BLOB не применяется). Русский набор символов WIN1251 имеет 2 порядка сортировки - WIN1251 и PXW_CYRL. Для правильной сортировки, включающей большие буквы, следует выбрать порядок PXW_CYRL. 
Описание ограничений и/или ключей включает в себя предложения CONSTRAINT или предложения, описывающие уникальные поля, первичные, внешние ключи, а также ограничения CHECK (такие конструкции могут определяться как на уровне поля, так и на уровне таблицы в целом, если они затрагивают несколько полей): 


 = [CONSTRAINT constraint ] 

[an error occurred while processing this directive] 

Здесь 

 = {{PRIMARY KEY | UNIQUE} (col[,col...]) | 
FOREIGN KEY (col [, col ...]) REFERENCES other_table 
| CHECK ()}

 = 
{  { | ()} 
|  [NOT] BETWEEN  AND  
|  [NOT] LIKE  [ESCAPE ] 
|  [NOT] IN ( [,  ...] | 

 = {
col [] |  |  | 
| NULL | USER | RDB$DB_KEY } [COLLATE collation]
 
 = num | "string" | charsetname "string"
 = {
COUNT (* | [ALL]  | DISTINCT )
| SUM ([ALL]  | DISTINCT )
| AVG ([ALL]  | DISTINCT )
| MAX ([ALL]  | DISTINCT )
| MIN ([ALL]  | DISTINCT )
| CAST ( AS <datatype>)
| UPPER ()
| GEN_ID (generator, )
}

 = {= | < | > | <= | >= | !< | !> | <> | !=}

 = выражение SELECT по одному полю, 
которое возвращает в точности одно значение.

Приведенного неполного синтаксиса достаточно для большинства задач, решаемых в различных предметных областях. Проще всего синтаксис SQL можно понять из примеров. Поэтому мы приведем несколько примеров создания таблиц с помощью SQL. 

Пример A: Простая таблица с конструкцией PRIMARY KEY на уровне поля 


CREATE TABLE REGION (
REGION REGION_NAME NOT NULL PRIMARY KEY,
POPULATION INTEGER NOT NULL);

Предполагается, что в базе данных определен домен REGION_NAME, например, следующим образом: 


 
CREATE DOMAIN REGION_NAME 
AS VARCHAR(40) CHARACTER SET WIN1251 COLLATE PXW_CYRL;

Пример B: Таблица с предложением UNIQUE как на уровне поля, так и на уровне таблицы 


CREATE TABLE GOODS (
MODEL SMALLINT NOT NULL UNIQUE,
NAME CHAR(10) NOT NULL, 
ITEMID INTEGER NOT NULL, CONSTRAINT MOD_UNIQUE 
UNIQUE (NAME, ITEMID));

Пример C: Таблица с определением первичного ключа, внешнего ключа и конструкции CHECK, а также символьных массивов 


CREATE TABLE JOB (
JOB_CODE JOBCODE NOT NULL,
JOB_GRADE JOBGRADE NOT NULL,
JOB_REGION REGION_NAME NOT NULL,
JOB_TITLE VARCHAR(25) CHARACTER SET WIN1251 COLLATE PXW_CYRL NOT NULL,
MIN_SALARY SALARY NOT NULL,
MAX_SALARY SALARY NOT NULL,
JOB_REQ BLOB(400,1) CHARACTER SET WIN1251,
LANGUAGE_REQ VARCHAR(15) [5],
PRIMARY KEY (JOB_CODE, JOB_GRADE, JOB_REGION),
FOREIGN KEY (JOB_REGION) REFERENCES REGION (REGION),
CHECK (MIN_SALARY < MAX_SALARY));

Данный пример создает таблицу, содержащую информацию о работах (профессиях). Типы полей основаны на доменах JOBCODE, JOBGRADE, REGION_NAME и SALARY. Определен массив LANGUAGE_REQ, состоящий из 5 элементов типа VARCHAR(15). Кроме того, введено поле JOB_REQ, имеющее тип BLOB с подтипом 1 (текстовый блоб) и размером сегмента 400. Для таблицы определен первичный ключ, состоящий из трех полей JOB_CODE, JOB_GRADE и JOB_REGION. Далее, определен внешний ключ (JOB_REGION), ссылающийся на поле REGION таблицы REGION. И, наконец, включено предложение CHECK, позволяющее производить проверку соотношения для двух полей и вызывать исключительное состояние при нарушении такого соотношения. Пример D: Таблица с вычисляемым полем 


CREATE TABLE SALARY_HISTORY (
EMP_NO EMPNO NOT NULL,
CHANGE_DATE DATE DEFAULT "NOW" NOT NULL,
UPDATER_ID VARCHAR(20) NOT NULL,
OLD_SALARY SALARY NOT NULL,
PERC_CHANGE DOUBLE PRECISION DEFAULT 0 NOT NULL
CHECK (PERC_CHANGE BETWEEN -50 AND 50),
NEW_SALARY COMPUTED BY
(OLD_SALARY + OLD_SALARY * PERC_CHANGE / 100),
PRIMARY KEY (EMP_NO, CHANGE_DATE, UPDATER_ID),
FOREIGN KEY (EMP_NO) REFERENCES EMPLOYEE (EMP_NO));


Прочитал??? А теперь поищи и прочитай про "Alter Table" - реструктуризация таблицы.

Автор: Serg1234 26.10.2006, 09:05
Цитата(Akella @  25.10.2006,  09:12 Найти цитируемый пост)
Лично я советую тебе воспользоваться TQuery


Спасибо за совет, но что то я сомневаюсь, что это поможет решить проблему. В примерах показаны разные способы создания таблицы, а с этим проблем и у TTable нет. Есть проблема смены типа поля с Integer на Autoinc.(Но ведь в DataBase это работает smile )

Поиск Alter Table вывел на такой же вопрос - к сожелению он тоже остался без ответа.  smile 

Автор: Akella 26.10.2006, 10:03
Попробуй такую конструкцию
Код

ALTER TABLE TableName CHANGE LastFieldName NewFieldName INTEGER NOT NULL PRIMARY KEY;


Alter Table и Create Table немного схожи в определении полей

Добавлено @ 10:13 
Цитата(Serg1234 @  26.10.2006,  09:05 Найти цитируемый пост)
Поиск Alter Table вывел на такой же вопрос - к сожелению он тоже остался без ответа. 

поиск на форуме по ключевому слову Alter Table
http://forum.vingrad.ru/topic-47970/unread-1/hl/alter%2Cand%2Ctable/index.html

Добавлено @ 10:14 
Цитата

В BDE есть файл localsql.hlp - там синтаксис SQL описан

Автор: Serg1234 26.10.2006, 23:52
Цитата(Akella @  26.10.2006,  10:03 Найти цитируемый пост)
Цитата(Serg1234 @  26.10.2006,  09:05 )
Поиск Alter Table вывел на такой же вопрос - к сожелению он тоже остался без ответа. 

поиск на форуме по ключевому слову Alter Table
http://forum.vingrad.ru/topic-47970/unread...able/index.html


Я этот вопрос и имел в виду. Но это уже не важно.
Я нашел в чем проблема - проблема в том, что прежде чем изменять тип поля с Integer на Autoinc, необходимо по этому полю сделать Primary индекс. И все заработало (с помощью BDE!).

В низу текст процедуры, может кому еще пригодится.
p.s. Akella  - спасибо за участие smile .


Код

Var Name,NameInc:String;
      i,IncremField=Integer;NameInc
Begin
 //создаем новую(временную) таблицу, копируя в нее структуру старой
 //Инкрементальное поле(ftAutoinc) заменяем целочисленным полем(ftInteger)
 if FileExists('DB\sp_new.db') then DeleteFile('DB\sp_new.db');
 Table_new.TableName:='DB\sp_new.db';Table_new.TableType:=ttParadox;
 For i:=0 to spisok_table.FieldCount-1 do
  With Table_new.FieldDefs do
  Begin
   IF spisok_table.FieldDefList.FieldDefs[i].DataType=ftAutoinc
    Then Begin Add(spisok_table.FieldDefList.FieldDefs[i].Name,ftInteger,spisok_table.FieldDefList.FieldDefs[i].Size,False);
          IncremField:=i;NameInc:=spisok_table.FieldDefList.FieldDefs[i].Name;
         End
    Else Add(spisok_table.FieldDefList.FieldDefs[i].Name,spisok_table.FieldDefList.FieldDefs[i].DataType,
             spisok_table.FieldDefList.FieldDefs[i].Size,False);
  End;
 //переносим информацию из старой таблицы в новую
 spisok_table.IndexName:='';spisok_table.CancelRange;spisok_table.First;
  While Not(spisok_table.Eof) do
  Begin
   Table_new.Insert;
   For i:=0 to spisok_table.FieldCount-1 do Table_new.Fields.Fields[i]:=spisok_table.Fields.Fields[i];
   Table_new.Post;
   spisok_table.Next;
  End;

 //создаем ixPrimary индекс
 Table_new.IndexDefs.Update;
 Table_new.Active:=False;Table_new.Exclusive:=True;Table_new.Open;
 Table_new.AddIndex('', NameInc,[ixPrimary]);
 //Меняем поле целочисленное поле на поле типа ftAutoinc
 Fillchar(MyChangeRec,sizeof(MyChangeRec),0);StrPCopy(MyChangeRec.szName,NameInc);
 MyChangeRec.iType:=fldINT32;
 MyChangeRec.iSubType:=fldstAUTOINC;
 Table_new.Active:=False;Table_new.Exclusive:=True;Table_new.Open;
 // ChangeField- процедура взята из файла bde32.hlp
 ChangeField(Table_new,Table_new.FieldByName(NameInc),MyChangeRec);
 //создаем Secondary индексы
 spisok_table.IndexDefs.Update;Table_new.IndexDefs.Update;
 For i:=1 to spisok_table.IndexDefs.Count do
  Begin
   Table_new.AddIndex(spisok_table.IndexDefs.Items[i-1].Name,spisok_table.IndexDefs.Items[i-1].Fields,spisok_table.IndexDefs.Items[i-1].Options)
  End;
 Table_new.IndexDefs.Update;
 //удаляем старую таблицу и сохраняем новую с именем старой таблицы
 // DeleteTable,CopyTable - процедуры взяты c drkb23
 Name:=spisok_table.TableName;spisok_table.Active:=False;spisok_table.Close;
 DeleteTable('',spisok_table.TableName);
 CopyTable('', Table_new.TableName,'',Name);
End;

Автор: Akella 27.10.2006, 08:56
Serg1234, пометь тогда как решённый

Автор: Serg1234 27.10.2006, 09:54
Вопрос решен

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)