Лена |
Отправлено: 19.10.2006, 11:04 |
|
Мастер участка
Группа: Участник
Сообщений: 501
|
Когда пользователь вводит в ключевую колонку значение, которое там уже есть, то возникает исключение из-за дублирования.
Как правильно его обработать в OnBeforePost компонента ClientDataSet.
Сейчас у меня в этом обработчике есть код, который заставляет пользователя заполнить нужные колонки:
CODE |
void __fastcall TDataModule2::ClientDataSetKeyBeforePost(TDataSet *DataSet)
{
if(Form1->DBGrid1->Fields[0]->Value.IsNull() || Form1->DBGrid1->Fields[2]->Value.IsNull()
|| Form1->DBGrid1->Fields[3]->Value.IsNull())
{
ShowMessage("Поля \"Имя\", \"Фамилия\", и \"Отчество\" должны быть заполнены");
Abort();
}
}
|
Подскажите, что еще добавить, чтобы тоже обрабатывался запрет на ввод дублированного значения в колонке первичного ключа?
Присоединить изображение
|
|
AVC |
Отправлено: 19.10.2006, 11:33 |
|
Ветеран
Группа: Модератор
Сообщений: 1583
|
1. PK это святое и незачем его давать на растерзание пользователю. Слишком много головной боли даже при поддерживаемых и правильно настроенных каскадах.
2. Как вы собираетесь проверять на дублирование? Для этого нужно иметь все значения всех запсей на клиенте. За время анализа другая станция может создать запись с таким значением. Тупик.
Ловите ошибку после сохранения и делайте выводы. |
|
Лена |
Отправлено: 19.10.2006, 11:43 |
|
Мастер участка
Группа: Участник
Сообщений: 501
|
>незачем его давать на растерзание пользователю
База создана не мной и требование, чтобы это поле было видимо пользователю. В него вводятся всякие коды, которые не должны дублироваться. От меня требуется только подмена английского сообщения, чтобы в случае дублирования пользователь получил возможность исправить ошибку и работать дальше.
|
|
beginner |
Отправлено: 19.10.2006, 11:51 |
|
Дежурный стрелочник
Группа: Участник
Сообщений: 44
|
Можно использовать Lookup.
Если успешный, значит дублируется значение. |
|
olegenty |
Отправлено: 19.10.2006, 12:01 |
|
Ветеран
Группа: Модератор
Сообщений: 2412
|
тогда отправляй запрос на сервер о том, а не exists ли там уже такой код. однако, поддерживаю AVC, — прочти статью "естественные ключи против искусственных" и введи искусственный ключ, а по своему уникальному полю построй уникальный индекс. и, до кучи, в идеале, проверка должна делаться на серверной, а не клиентской стороне. так спокойнее. пусть сервер тебе сформирует сообщение о том, что что-то не так...
ИМХО — плохо спроектированную базу всё равно надо перепроектировать.
|
|
AVC |
Отправлено: 19.10.2006, 12:16 |
|
Ветеран
Группа: Модератор
Сообщений: 1583
|
QUOTE (Лена @ 19.10.2006, 11:43) | База создана не мной и требование, чтобы это поле было видимо пользователю. |
"Видимое это вам не изменяемое"
QUOTE |
От меня требуется только подмена английского сообщения, чтобы в случае дублирования пользователь получил возможность исправить ошибку и работать дальше.
|
См. пункт 2. Грамотно только ПОСЛЕ сервера. |
|
Лена |
Отправлено: 19.10.2006, 14:49 |
|
Мастер участка
Группа: Участник
Сообщений: 501
|
QUOTE |
"Видимое это вам не изменяемое"
|
Изменяемое, так хочет заказчик.
Это первичный ключ, который можно менять в гриде, но надо отслеживать возможность дублирования и отменять ее. Многопользовательского доступа не будет.
Скажите, а почему не срабатывает обработчик OnPostError?
Почему не работает?:
CODE |
void __fastcall TDataModule2::ADOQueryKeyPostError(TDataSet *DataSet,
EDatabaseError *E, TDataAction &Action)
{
if(E->Message=="Key violation")
{
ShowMessage("Нельзя дублировать");
Abort();
}
}
|
Отредактировано Лена — 19.10.2006, 15:50 |
|
beginner |
Отправлено: 19.10.2006, 15:02 |
|
Дежурный стрелочник
Группа: Участник
Сообщений: 44
|
CODE | void __fastcall TDataModule2::ClientDataSetKeyBeforePost(TDataSet *DataSet)
{
if(Form1->DBGrid1->Fields[0]->Value.IsNull() || Form1->DBGrid1->Fields[2]->Value.IsNull()
|| Form1->DBGrid1->Fields[3]->Value.IsNull())
{
ShowMessage("Поля \"Имя\", \"Фамилия\", и \"Отчество\" должны быть заполнены");
Abort();
}
Variant locvalues[1];
locvalues[0] = Form1->DBGrid1->Fields[Номер_ключ_поля]->AsVariant;
Variant LookupResults = ClientDataSetKey->Lookup("Имя_ключ_поля", VarArrayOf(locvalues, 1), "Имя_Ключевого_поля");
if ! (VarType(LookupResults) == varNull)
{
ShowMessage("Дублирование ключевого поля!");
Abort();
}
} | |
|
Лена |
Отправлено: 19.10.2006, 15:36 |
|
Мастер участка
Группа: Участник
Сообщений: 501
|
Получаю исключение. Сам тип этого поля String.
CODE |
Variant locvalues[1];
locvalues[0] = Form1->DBGrid1->Fields[0]->AsVariant;
Variant LookupResults = ClientDataSetKey->Lookup("code", VarArrayOf(locvalues, 1), "code");
if (!VarType(LookupResults) == varNull)
{
ShowMessage("Дублирование в этом поле запрещено!");
Abort();
}
|
Если написать так:
locvalues[0] = Form1->DBGrid1->Fields[0]->AsString;
тоже самое:
Отредактировано Лена — 19.10.2006, 16:37
Присоединить изображение
|
|
olegenty |
Отправлено: 19.10.2006, 15:48 |
|
Ветеран
Группа: Модератор
Сообщений: 2412
|
ну так присваивать надо было не как AsVariant, а как AsString
Лен, это в любом случае в корне кривое решение. ты прикинь, что у этой записи есть подчинённые записи... и ты их тоже плотно будешь апдейтить? (даже если это за тебя СУБД сделает — нафиг надо-то?)
|
|
beginner |
Отправлено: 19.10.2006, 15:48 |
|
Дежурный стрелочник
Группа: Участник
Сообщений: 44
|
Странно.
Можно, тогда попробовать
locvalues[0] = Variant(Form1->DBGrid1->Fields[Номер_ключ_поля]->AsString);
|
|
Лена |
Отправлено: 19.10.2006, 15:58 |
|
Мастер участка
Группа: Участник
Сообщений: 501
|
CODE | Variant locvalues[1];
locvalues[0] = Variant(Form1->DBGrid1->Fields[0]->AsString);
Variant LookupResults = ClientDataSetKey->Lookup("code", VarArrayOf(locvalues, 1), "code");
if(!VarType(LookupResults) == varNull)
{
ShowMessage("Дублирование запрещено!");
Abort();
}
|
Ошибка та же на этой строке:
Variant LookupResults = ClientDataSetKey->Lookup("code", VarArrayOf(locvalues, 1), "code");
>это в любом случае в корне кривое решение
Покажу результат руководству, а они путсть решают оставить так, или базу изменить. |
|
Лена |
Отправлено: 19.10.2006, 16:07 |
|
Мастер участка
Группа: Участник
Сообщений: 501
|
Variant LookupResults = ClientDataSetKey->Lookup("code", VarArrayOf(OPENARRAY(Variant,(locvalues, 1))), "code");
так тоже получаю исключение AV но уже другое.
А если так:
CODE |
String locvalues;
locvalues = Form1->DBGrid1->Fields[0]->AsString;
Variant LookupResults = ClientDataSetKey->Lookup("code", VarArrayOf(OPENARRAY(Variant,(locvalues))), "code");
if(!VarType(LookupResults) == varNull)
{
ShowMessage("vvvvvvvvvvvvvvvvvvvv!");
Abort();
}
|
Пишет стак оверфлоу.
Отредактировано Лена — 19.10.2006, 17:10 |
|
olegenty |
Отправлено: 19.10.2006, 16:08 |
|
Ветеран
Группа: Модератор
Сообщений: 2412
|
QUOTE |
Покажу результат руководству, а они путсть решают оставить так, или базу изменить.
|
будет весело, если наш директор завод начнёт решать, КАК изменить базу данных, чтобы нечто иметь на выходе. и как он в этом случае предложит сопровождать эту базу...
|
|
beginner |
Отправлено: 19.10.2006, 16:11 |
|
Дежурный стрелочник
Группа: Участник
Сообщений: 44
|
Это моя ошибка.
Нужен
Variant LookupResults = ClientDataSetKey->Lookup("code", VarArrayOf(locvalues, 0), "code");
Если запись имеет подзаписи, тогда нужно еще один невидимый внутренний ключь и через него связывать подзаписи. Или можно определить триггер.
Отредактировано beginner — 19.10.2006, 17:11 |
|
Лена |
Отправлено: 19.10.2006, 16:16 |
|
Мастер участка
Группа: Участник
Сообщений: 501
|
CODE |
Variant locvalues[1];
locvalues[0] = Form1->DBGrid1->Fields[0]->AsVariant;
Variant LookupResults = ClientDataSetKey->Lookup("code", VarArrayOf(locvalues, 0), "code");
if (!VarType(LookupResults) == varNull)
{
ShowMessage("Äóáëèðîâàíèå êëþ÷åâîãî ïîëÿ!");
Abort();
}
|
или так:
CODE |
String locvalues;
locvalues = Form1->DBGrid1->Fields[0]->AsString;
Variant LookupResults = ClientDataSetKey->Lookup("code", VarArrayOf(OPENARRAY(Variant,(locvalues))), "code");
if (!VarType(LookupResults) == varNull)
{
ShowMessage("Äóáëèðîâàíèå êëþ÷åâîãî ïîëÿ!");
Abort();
}
|
Получаю это исключение на рисунке.
Отредактировано Лена — 19.10.2006, 17:38
Присоединить изображение
|
|
beginner |
Отправлено: 19.10.2006, 17:07 |
|
Дежурный стрелочник
Группа: Участник
Сообщений: 44
|
Наверно здесь баг:
if (!(VarType(LookupResults) == varNull))
|
|
olegenty |
Отправлено: 19.10.2006, 17:26 |
|
Ветеран
Группа: Модератор
Сообщений: 2412
|
так проверять Variant нельзя. надо так
CODE |
if (!LookupResults.IsNull() && !LookupResults.IsEmpty())
{
...
}
|
|
|
Admin |
Отправлено: 19.10.2006, 20:47 |
|
Владимир
Группа: Администратор
Сообщений: 1190
|
Может это конечно не по теме, но если у вас будет два
человека с одниковым ФИО ? Не такая уж редкость если на
предприятии больше 300 человек. Каким образом вы их введете
в базу данных и будете отличать ?
Делайте autoinc поле на ключ, при добавлении записи -
триггер или хранимую процедуру на увеличение значения поля.
|
|
Лена |
Отправлено: 23.10.2006, 09:44 |
|
Мастер участка
Группа: Участник
Сообщений: 501
|
CODE |
Variant locvalues[1];
locvalues[0] = Form1->DBGrid1->Fields[0]->AsString;
Variant LookupResults = ClientDataSetKey->Lookup("code", VarArrayOf(locvalues,0), "code");
if ((!LookupResults.IsNull() && !LookupResults.IsEmpty())
)
{
ShowMessage("Дублирование запрещено!");
Abort();
}
|
Ошибка переполнения стека на строке:
Variant LookupResults = ClientDataSetKey->Lookup("code", VarArrayOf(locvalues,0), "code");
Что касается этого поля (code), то это первичный ключ не связанный с другими таблицами. В него вводятся индефикационные номара, например 1234SE и т.д. Эти номера присуствуют только в таблице keys.
CREATE TABLE keys
(
code varchar(32) NOT NULL,
description varchar(50),
enabled bool NOT NULL,
person_id int4,
CONSTRAINT pk_keys PRIMARY KEY (code)
)
WITHOUT OIDS;
ALTER TABLE keys OWNER TO postgres;
|
|
Admin |
Отправлено: 23.10.2006, 11:56 |
|
Владимир
Группа: Администратор
Сообщений: 1190
|
CODE |
void __fastcall TForm1::Button2Click(TObject *Sender)
{
Variant Rez = ADODataSet1->Lookup("Area", Edit1->Text.ToInt(), "Area");
if(!Rez.IsNull())
ShowMessage("Такое значение уже есть !");
else ShowMessage("Такого значения нет !");
}
//---------------------------------------------------------------------------
|
для тектсового поля соответственно
CODE | Variant Rez = ClientDataSetKey->Lookup("code", Form1->DBGrid1->Fields[0]->AsString, "code");
if(!Rez.IsNull())
ShowMessage("Такое значение уже есть !");
else ShowMessage("Такого значения нет !");
}
//--------------------------------------------------------------------------- |
|
|
Лена |
Отправлено: 23.10.2006, 12:12 |
|
Мастер участка
Группа: Участник
Сообщений: 501
|
Спасибо.
Работает в этом варианте:
CODE |
ADOQueryKey->Open();
Variant Rez = ADOQueryKey->Lookup("code", Form1->DBGrid1->Fields[0]->AsString, "code");
if(!Rez.IsNull())
{
ShowMessage("Такое значение уже есть!");
Abort();
}
|
Причем строка ADOQueryKey->Open(); обязательна.
Скажите, почему не работает так: ClientDataSetKey->Lookup... (переполнение стека) и еше нужно ли в конце этого кода писать:
ADOQueryKey->Close(); ?
Отредактировано Лена — 23.10.2006, 13:14 |
|
Admin |
Отправлено: 23.10.2006, 12:19 |
|
Владимир
Группа: Администратор
Сообщений: 1190
|
QUOTE | Скажите, почему не работает так: ClientDataSetKey->Lookup... (переполнение стека) и еше нужно ли в конце этого кода писать:
ADOQueryKey->Close(); ?
|
Это надо смотреть по коду, так ничего не могу сказать.
ADOQueryKey->Close(); лучше явно закрыть, осободить ресурсы,
если он больше не нужен.
|
|
Admin |
Отправлено: 23.10.2006, 12:26 |
|
Владимир
Группа: Администратор
Сообщений: 1190
|
Но возникает вопрос — а что сидит в ADOQueryKey ?
Если там Select * from keys
и если таблица keys будет очень большой —
вам будет возвращены все записи и поля таблицы.
смотрите, может лучше это организовать это по другому,
отдельным запросом, что-то типа
Select code from keys where code = параметр из Form1->DBGrid1->Fields[0]->AsString,
тогда и возвращено будет только одно значение code или NULL.
Но это уже вам виднее.
|
|
beginner |
Отправлено: 23.10.2006, 12:29 |
|
Дежурный стрелочник
Группа: Участник
Сообщений: 44
|
QUOTE (Лена @ 23.10.2006, 13:12) | Спасибо.
Работает в этом варианте:
Скажите, почему не работает так: ClientDataSetKey->Lookup... (переполнение стека) ? |
Согласно help, конструкция с VarArrayOf применяется, когда индекс состоит из нескольких полей. Видимо, ClientDataset lookup с массивом единичного размера работать не может. |
|
beginner |
Отправлено: 23.10.2006, 12:29 |
|
Дежурный стрелочник
Группа: Участник
Сообщений: 44
|
...
Отредактировано beginner — 23.10.2006, 13:33 |
|
Лена |
Отправлено: 23.10.2006, 12:33 |
|
Мастер участка
Группа: Участник
Сообщений: 501
|
>Но возникает вопрос — а что сидит в ADOQueryKey
Там запрос который добовляет еще одну сборную колонку в которой имя-фамилия-отчество-должность из другой таблицы:
select keys.code, keys.description, keys.enabled, keys.person_id, (visitors.lastname || ' ' || visitors.firstname || ' ' || visitors.patronymic || ' ' || visitors.post) as man from keys
join visitors on keys.person_id = visitors.id order by man
Отредактировано Лена — 23.10.2006, 13:34 |
|
Лена |
Отправлено: 23.10.2006, 13:28 |
|
Мастер участка
Группа: Участник
Сообщений: 501
|
Проблема продолжается:
Теперь попытка поменять в соседнем булевом поле значение с "Да" на "Нет" приводит к коду:
ShowMessage("Такое значение уже есть!");
Abort();
да и попытка в любом поле ввести значение которое там есть приводит к этому коду. Как ограничить действие этого кода только на первый столбец грида?
Отредактировано Лена — 23.10.2006, 14:54 |
|
Admin |
Отправлено: 23.10.2006, 14:10 |
|
Владимир
Группа: Администратор
Сообщений: 1190
|
Это нужно смотреть код. Например, без кода непонятно, зачем используется Abort();
|
|
Лена |
Отправлено: 23.10.2006, 14:24 |
|
Мастер участка
Группа: Участник
Сообщений: 501
|
Abort() чтобы пользователь исправил запись. Весь код:
Весь код
CODE |
void __fastcall TDataModule2::ClientDataSetKeyBeforePost(TDataSet *DataSet)
{
//некоторые колонки не могут быть пустыми
if(Form1->DBGrid1->Fields[0]->Value.IsNull() || Form1->DBGrid1->Fields[2]->Value.IsNull()
|| Form1->DBGrid1->Fields[3]->Value.IsNull())
{
ShowMessage("Поля \"Код\", \"Доступ\", и \"Владелец\" должны быть заполнены");
Abort();
}
//первая колонка не должна содержать дубликаты
ADOQueryKey->Open();
//DataModule2->ClientDataSetKey->FieldByName("code")->AsString;
Variant Rez = ADOQueryKey->Lookup("code", Form1->DBGrid1->Fields[0]->AsString, "code");
if(!Rez.IsNull())
{
ShowMessage("Такое значение уже есть !");
Abort();
}
ADOQueryKey->Close();
}
| |
|