Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C++ Builder > Посчитать одинаковые записи


Автор: Лена 16.4.2007, 18:15
При помощи этого кода я могу найти нужную запись:
Variant Rez = ADOQueryKey->Lookup("code", Form1->DBGrid1->Fields[0]->AsString, "code");
if(!Rez.IsNull())...
Если запись найдена, подскажите, как можно посчитать сколько раз она встречается в колонке грида?

Автор: Anikmar 16.4.2007, 18:47
Может быть быстрее отдельным запросом?
SELECT Count(Поле) AS Result FROM Table WHERE условие

Автор: Лена 16.4.2007, 19:24
Мне надо не много по другому. Пользователь вводит запись в колонку грида номер 1 и затем щелчком мыши переходит на другую строку в колонку например номер 3. Я хочу убедиться, что пользователь ввел запись, которой гарантировано, нет в колонке номер 1. Сейчас это реализовано так:
Код

ADOQueryKey->Open();

   Variant Rez = ADOQueryKey->Lookup("code", Form1->DBGrid1->Fields[0]->AsString, "code");
   if(!Rez.IsNull()&& Form1->DBGrid1->SelectedIndex == 0) /* */
             {
             sShowMessage("Такая запись уже есть. Введите другую");
             DataSet->Cancel();
             ADOQueryKey->Close();
             Abort();
             }

   ADOQueryKey->Close();

Этот код прекрасно работает, если пользователь сразу после ввода значения перейдет на другую строку в гриде. Если же пользователь продолжит редактировать эту же запись в соседней колонке, то при попытке post возникнет исключение, потому что в первой колонке записи должны быть разные. Для пояснения рисунок.


Автор: Anikmar 16.4.2007, 19:47
Я для решения таких задач применял 2 метода:
1. Вводил ограничение Unique в саму БД и ловил исключение
2. (Более гуммано с точки зрения сбережения нервов пользователю) выводил редактирование строки в отдельную форму, где все проверки проводил при нажатии ок и по-русски объснял пользователю в каком месте он дурак  smile 

Автор: Лена 17.4.2007, 10:49
В этом поле есть ограничение Unique. Подскажите, как правильно его обработать? Связь: ADOConnection1->ADOQuery->DataSetProvider->ClientDataSet->DataSource->Грид. В моем случае, который представлен на рисунке, происходит исключение. В каком событии правильно обработать эту ситуацию?
Думала просто простым циклом: если одинаковых записей больше двух, сделать отмену редактирования.


Автор: Anikmar 17.4.2007, 11:21
Думаю самым простым в вашей ситуации будет обработчик BeforePost. В нем отдельным запросом проводить проверку и запрещать запись, если поля неподходят. Таким образом вы отсечете ситуацию ДО ее возникновения, а не после, как в случае с исключением.

Автор: Лена 17.4.2007, 11:49
У меня как раз в обработчике ClientDataSetBeforePost, код который я приводила выше в своем втором сообщении. Как его правильно переделать?

Автор: Anikmar 17.4.2007, 12:04
Я с этим компонентом никогда не работал, но общая логика такая: Проверяете записываемые поля на допустимость их значений и в случае невозможности записи выдаете сообщение об ошибке и прерываете запись функцией Abort()

Автор: Vyacheslav 17.4.2007, 12:19
Цитата(Anikmar @  16.4.2007,  19:47 Найти цитируемый пост)
2. (Более гуммано с точки зрения сбережения нервов пользователю) выводил редактирование строки в отдельную форму, где все проверки проводил при нажатии ок и по-русски объснял пользователю в каком месте он дурак  

Отличный подход.
Вообще редактирование в непосредственно в гриде - это неправильно.

Автор: Лена 17.4.2007, 12:21
Цитата(Anikmar @  17.4.2007,  12:04 Найти цитируемый пост)
Проверяете записываемые поля на допустимость их значений 


Сообственно это и есть моя проблема. smile  Как это правильно делается? Наверняка существует стандартный код проверки ограничение Unique в  обработчике BeforePost. Я только смогла придумат код выше, но он не в полной мере подходит...

Автор: Vyacheslav 17.4.2007, 12:35
Цитата(Лена @  17.4.2007,  10:49 Найти цитируемый пост)
В этом поле есть ограничение Unique. Подскажите, как правильно его обработать? Связь: ADOConnection1->ADOQuery->DataSetProvider->ClientDataSet->DataSource->Грид. В моем случае, который представлен на рисунке, происходит исключение. В каком событии правильно обработать эту ситуацию?

Лена. Вся проблема у Вас возникла потому, что  Вы не контролируете  момент начала редактирования и его конца. Под контролем я понимаю наличие в коде команд Edit  и Post  при  устаноленном  AutoEdit в false. Наиболее правильным был бы подход, когда пользователю явно обозначен момент  начала редактирования и его конца. То есть, если он хочет отредактировать конкретную строку, он должен нажать кнопку Редактировать, получить  окно для редактирования, набрать все данные, визуально проверитиь их правильность и завершить редактирование, сохранив изменения или отказавшись от них. В этом случае у Вас есть всегда место, где перед сохранением, Вы можете проверить все данные на валидность
В Вашем случае скорее всего придется написать обработчик
Код

void __fastcall TForm1::ClientDataSet1PostError(TDataSet *DataSet,
      EDatabaseError *E, TDataAction &Action)
{

}


Добавлено @ 12:37
Цитата(Лена @  17.4.2007,  12:21 Найти цитируемый пост)
Как это правильно делается? Наверняка существует стандартный код проверки ограничение Unique в  обработчике BeforePost. Я только смогла придумат код выше, но он не в полной мере подходит...


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

Автор: Лена 17.4.2007, 12:46
Цитата(Vyacheslav @  17.4.2007,  12:35 Найти цитируемый пост)
Вы не контролируете  момент начала редактирования и его конца. Под контролем я понимаю наличие в коде команд Edit  и Post  при этом  уставноленном  AutoEdit в false. Наиболее правильным был бы подход, когда пользователь явно обозначен момент  начала рекдактирования и его конца. То есть, если он хочет отредактировать конкретную строку, он должен нажать кнопку Редактировать, получить  окно для редактирования, набрать все данные, визуально проверитиь их пправильность и завершить редактирование, сохранив изменения или отказавшись от них. В этом случае у Вас есть всегда место, где перед сохранением, Вы можете проверить все данные на валидность


Да, теперь понимаю, что такой подход с окном для редактирования самый оптимальный. Я буду так реализовывать в следующих проектах. Просто этот проект надо уже отдать. Не готова вносить серьезные изменения. Скорость моего кодирования не впечатляет, могу не успеть. smile 
Вроде все работает. Вот только случайно наткнулась на ситуации описанную выше.
Для своего случая попробую, согласно вашего совета, ClientDataSet1PostError.


Автор: Лена 17.4.2007, 13:06
Всем хорош мой любимый Builder, только вот справочная система подкачала.
Жму F1 на PostError, вижу описание, а где же samples?!  smile 
Короче написала так:
Код

void __fastcall TDataModule2::ClientDataSet1PostError(TDataSet *DataSet,
      EDatabaseError *E, TDataAction &Action)
{
   if(E->Message == "Key violation.")
   {
   ShowMessage("Произошла ошибка. Записи в поле \"Ключ\" не должны повторяться");
   Action = daAbort;
   }
   else {
           ShowMessage("Произошла ошибка: " + E->Message);
           Action = daAbort;
          }
}

Но что-то не очень нравиться. Как бы добавить действие "Отменить редактирование", чтобы запись вернулась в исходное состояние? А то пользователь будет щелкать, не понимая в чем проблема, и будет получать ShowMessage. Хочется вернуть проблемную запись в первоначальный вид в этом обработчике. Это возможно?

P.S.
Наверное есть смысл снабдить все мои ClientDataSet-ы  обработчиком ClientDataSet1PostError?
И еще наверное хорошо бы добавить ClientDataSet1DeleteError?

Автор: Vyacheslav 17.4.2007, 13:38
А разве запись не возвращается в исходное состояние после daAbort
А так?
Код

void __fastcall TDataModule2::ClientDataSet1PostError(TDataSet *DataSet,
      EDatabaseError *E, TDataAction &Action)
{
   ShowMessage("Произошла ошибка " + E->Message);
   DataSet->Cancel();
   Action = daAbort;
}



Добавлено @ 13:49
Вообще, насколько я понимаю, у Вас постановка задачи не совсем верна. Откуда пользователю знать, есть или нет в БД такое число. Это же у Вас, как я понимаю, уникальный ключ. Ну так подставляйте его сами по результатам запроса от БД и запретите пользователю редактировать это поле.
Добавте обработчик OnNewRecord?в котором будете присваивать значение
Код

void __fastcall TForm1::ClientDataSet1NewRecord(TDataSet *DataSet)
{
Form1->DBGrid1->Fields[0]->AsInteger =  getNewValue();
}

где  getNewValue() - метод, который будет возвращать значение  запроса
SELECT MAX(Поле) + 1  AS Result FROM Table 



Автор: Лена 17.4.2007, 13:58
Цитата(Vyacheslav @  17.4.2007,  13:38 Найти цитируемый пост)
DataSet->Cancel();


М-да, слона не заметила! Это то что нужно, работает. Спасибо! Меня сбило столку слово daAbort, думала оно все вернет в исходный вид.


Если у меня в других гридах нет проблемы с дублированием, то достаточно ли будет для всех ClientDataSet-ов написать просто:
Код

void __fastcall TDataModule2::ClientDataSet1PostError( TDataSet *DataSet, EDatabaseError *E, TDataAction &Action)
{  
ShowMessage("Произошла ошибка: " + E->Message);  
Action = daAbort;
}
//--------------------------------------------------------------------------- 
void __fastcall TDataModule2::ClientDataSet1DeleteError(  TDataSet *DataSet, EDatabaseError *E, TDataAction &Action)
{  
ShowMessage("Произошла ошибка: " + E->Message); 
 Action = daAbort;
}

Или везде добавить DataSet->Cancel();? Или может еще что добавить в код?  smile 



P.S.
>Это же у Вас, как я понимаю, уникальный ключ.

Это поле в конечном итоге будут вводиться данные из другой программы, сделанной другим человеком. Пользователь не сможет ввести туда данные и эти данные будут уникальными. НО! Прежде чем это будет сделано, моя программа попадет к промежуточному звену людей, которые не знают, как все это будет выглядеть. Эти люди просто будут знакомиться с интерфейсом, и я не хочу их пугать не понятными исключениями, поэтому просто добавляю эту возможность перехвата.  smile 


Автор: Vyacheslav 17.4.2007, 14:05
Цитата(Лена @  17.4.2007,  13:58 Найти цитируемый пост)
Или везде добавить DataSet->Cancel();? Или может еще что добавить в код?   

Лучше добавить  DataSet->Cancel();. Только зачем дублировать обработчики? Вы напишите один, и присвойте его всем  ClientDataSet в инспекторах. Если что-то впоследствии надо будет добвать, то только в одном месте

Автор: Лена 17.4.2007, 15:30
Большое спасибо!  smile 

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