void __fastcall TForm1::DBGrid1TitleClick(TColumn *Column)
{
if(Column->Title->Caption == "FatherName")
{
ADOQuery1->Sort = "";
ADOQuery1->Sort = "FatherName ASC,CardNumberID";
}
else if(Column->Title->Caption == "CardNumberID")
{
ADOQuery1->Sort = "";
ADOQuery1->Sort = "CardNumberID ASC,FatherName";
}
}
DBgrid c сортировкой столбцов
Или есть какой-то другой способ?
Есть еще TClientDataSet.
Э... EhLib?
Если сортировка не по вычисляему столбцу - индексы в руки и вперед, иначе скорее всего вьюсы.
Результат запроса не выводить в DBGrid, а записать в массив массивов строк. После этот массив записывать в обычный StringGrid.
При выборе способа сортировки, производится пересортировка массива в ОЗУ с последующим заполнением StringGrid'а
А если записей дофига и больше - это ж БД и проектировать запросы надо исходя из этого.
Сама БД может и иметь записей "дофига", но результат SQL-запроса будет ограниченным.
Так что, если критерии отбора будут широкими, то проще будет повторить запрос с иной сортировкой. Если же притерии опроса жёсткие, позволяющие отобрать сравнительно небольшое кол-во записей - быстрее будет произвести повторную сортировку уже полученного результата.
SQL запрос даст все записи, только результат выдается порциями, по требованию. А для загрузки в список придется получить сразу и все
[QUOTE=maestro_ufa]Есть DBGrid, куда загружаются данные достаточно ресурсоемкого запроса. Требуется реализовать сортировку грида по клику на заголовок столбца. Гонять каждый раз запрос к серверу с дописанным в конце ORDER BY - не вариант, т.к. будет выполняться достаточно долго. В голову приходит вариант загрузки данных в массив, где их потом сортировать. Но как тогда потом можно данные из отсортированного массива залить в DBGrid?
Оптимальный вариант: TClientDataSet.
Чем оптимален?
[QUOTE=Hydra]SQL запрос даст все записи, только результат выдается порциями, по требованию. А для загрузки в список придется получить сразу и все[/QUOTE]
SQL запрос выдаёт не "все" записи, а только те, которые соответствуют указанным условиям. Но их выдаёт не "порциями", а целиком, потому что его результат отделяется от содержимого таблиц БД.
У него, кстати, есть LocalSort.
[QUOTE=el scorpio]Если же притерии опроса жёсткие, позволяющие отобрать сравнительно небольшое кол-во записей - быстрее будет произвести повторную сортировку уже полученного результата.[/QUOTE]
Согласен. Для этого, кстати, совершенно неообязательно городить геморрой с StringGrid. Есть MemTableEh, например.
[QUOTE=el scorpio]Но их выдаёт не "порциями", а целиком, потому что его результат отделяется от содержимого таблиц БД.[/QUOTE]
Зависит от запроса и плана его выполнения сервером. Если для полного выполнения запроса не требуется получение всех строк на стороне сервера, курсор может выбираться частично, по мере прокрутки.
Как правило, если ORDER BY идет не по индексу, выполнение запроса требует единовременного получения всех строк на стороне сервера.
[QUOTE=Hydra]А если записей дофига и больше - это ж БД и проектировать запросы надо исходя из этого.[/QUOTE]
Подозреваю, что база не нормализована, особенно, если структура один в один скопирована из унаследованной (файловой) реализации.
Тем оптимален, что после загрузки записей в TClientDataset,
если нужна сортировка напр. по полю field1, то для этого достаточно
ClientDataSet1->IndexFieldNames = "field1";
Если по полю field2, тогда
ClientDataSet1->IndexFieldNames = "field2";
При этом можно определить индексы, и тогда индексы будут строиться только один раз.
Плюсь еще, что данные выводятся с помощью TDBGrid.
А то что ты предлагаешь, TStringGrid, два больших минуса
1. Постоянная пересортировка при смене поля.
2. В нормальной программе, числа в столбцах должны быть выравнены справа, даты по-середине. В TStringGrid это нужно будет сделать вручную.
SQL запрос выдаёт не "все" записи, а только те, которые соответствуют указанным условиям. Но их выдаёт не "порциями", а целиком, потому что его результат отделяется от содержимого таблиц БД.[/QUOTE]
Ага, у меня в бд результат запроса весит 3га и что-то я не замечал чтобы сервер все это мгновенно по сети в клиента кидал. Про транзакции слышал?
[QUOTE=Freeman]У него, кстати, есть LocalSort.[/QUOTE]
Так я об этом и говорю.
3 ГИГАБАЙТА :eek:
"Ну и запросы у вас", - сказала БД и зависла
Вообще-то SQL придумали как раз для того, чтобы передавать по сети именно то, что требуется. А если нужно получать таблицу целиком - пользуйте Table.
А "транзакция" - это механизм логирования изменений для их отмены, на тот случай, если произойдёт сбой в работе, либо ещё какая-нибудь причина для прекращения ввода на полпути.
А "транзакция" - это механизм логирования изменений для их отмены, на тот случай, если произойдёт сбой в работе, либо ещё какая-нибудь причина для прекращения ввода на полпути.[/QUOTE]
Не совсем правильно.
Транзакция - это логический блок, объединяющий одну или более операций в базе данных и позволяющий подтвердить или отменить результаты работы всех операций в блоке.
"Основное назначение транзакций в базе данных -- переводить её из одного согласованного состояния в другое. При фиксации изменений в базе данных гарантируется сохранение либо всех изменений, либо ни одного. Более того, выполняются все правила и проверки, обеспечивающие целостность данных." (с) Том Кайт, "Oracle для профессионалов".
Кстати, 3 гига для современной СУБД -- мелочь.
В данном случае я имел в виду не запрос, возвращающий большое число записей, а запрос выполняющий долго на сервере.
Сопоставив всю полученную информацию, я пришел вот к такому решению.
Для отображения данных я пользовался TDBGrigEh из библиотеки EhLib (действительно красивая и удобная таблица), для хранинея использовал структуру TDataSet - TDataSetProvider - TClientDataSet - TDataSource.
Код:
TDataSet* MySourceDs; //Запрашивает данные у сервера SQL-запросом
TDataSetProvide* MyProvider;
TClientDataSet* MyClientDs; //Хранит и индексирует данные локально
TDataSource* MyDataSource;
TDbGridEh* MyDbGrid;
TDataSetProvide* MyProvider;
TClientDataSet* MyClientDs; //Хранит и индексирует данные локально
TDataSource* MyDataSource;
TDbGridEh* MyDbGrid;
Связываются эти объекты следующим образом:
Код:
MyProvider->DataSet=MySourceDs;
MyClientDs->ProviderName=MyProvider;
MyClientDs->Data=MyProvider->Data;
MyDataSource->DataSet=MyClientDs;
MyDbGrid->DataSource=MyDataSource;
MyClientDs->ProviderName=MyProvider;
MyClientDs->Data=MyProvider->Data;
MyDataSource->DataSet=MyClientDs;
MyDbGrid->DataSource=MyDataSource;
Для обеспечения сортировки, необходимо определить индексы:
Код:
TIndexOptions opts;
opts << ixCaseInsensitive;
for (int i=0; i<MyDbGrid->FieldCount; i++)
{
MyClientDs->AddIndex(MyDbGrid->Fields->FieldName+"Index",MyDbGrid->Fields->FieldName,opts,"","",0);
}
opts << ixCaseInsensitive;
for (int i=0; i<MyDbGrid->FieldCount; i++)
{
MyClientDs->AddIndex(MyDbGrid->Fields->FieldName+"Index",MyDbGrid->Fields->FieldName,opts,"","",0);
}
Для того, чтобы нажимался заголовок грида, необходимо выставить в true свойство TitleButton у столбцов:
Код:
for (int i=0; i<MyDbGrid->FieldCount; i++)
{
TColumnTitleEh* l_Column;
l_Column=MyDbGrid->Columns->Items->Title;
l_Column->TitleButton=true;
}
{
TColumnTitleEh* l_Column;
l_Column=MyDbGrid->Columns->Items->Title;
l_Column->TitleButton=true;
}
Теперь ловим событие грида OnSort, и задаем там индекс для сортировки:
Код:
void __fastcall TMyFrm::MyDbGrid_OnSort(TObject *Sender)
{
TColumnEh* l_SortCol=MyDbGrid->SortMarkedColumns->Items[0];
MyClientDs->IndexName=l_SortCol->FieldName+"Index";
}
{
TColumnEh* l_SortCol=MyDbGrid->SortMarkedColumns->Items[0];
MyClientDs->IndexName=l_SortCol->FieldName+"Index";
}
3 ГИГАБАЙТА
"Ну и запросы у вас", - сказала БД и зависла
[/QUOTE]
:)
2maestro_ufa
Что-то как-то слшком навороченно. Может все же запрос не очень красивый.
"Основное назначение транзакций в базе данных -- переводить её из одного согласованного состояния в другое. При фиксации изменений в базе данных гарантируется сохранение либо всех изменений, либо ни одного. Более того, выполняются все правила и проверки, обеспечивающие целостность данных." (с) Том Кайт, "Oracle для профессионалов".
Кстати, 3 гига для современной СУБД -- мелочь.[/QUOTE]
Согласен много гигов данных - это мелочь. А вот подобный результат запроса - абсурд.
А насчёт определения транзакции - я сказал почти тоже самое, только простыми словами.
Кстати, чтобы произвести пересортировку уже полученного набора данных, достаточно задать поля для индексации для содержимого. Естественно, перед открытием нового запроса, индексацию нужно будет обнулить.
Теперь ловим событие грида OnSort, и задаем там индекс для сортировки:[/QUOTE]
В последних версиях EhLib есть дополнительные модули, реализующие сортировку на OnSort автоматом. Модули зависят от используемой БД и должны подключаться вручную. Возможно, и для ClientDataSet есть.
Я как раз и задал поля для индексации TClientDataSet'a. TDataSet никакую индексацию не предоставляет. Если знаете способ проще, то поконкретнее пожалуйста.
В EhLib есть папочка с модулями, реализующими автоматическую сортировку по нажатию на заголовок, забыл только их имена. Нечто вроде BdeUtilsEh, IBUtilsEh. В старых версиях EhLib все было свалено в кучу.
[QUOTE=maestro_ufa]Если знаете способ проще, то поконкретнее пожалуйста.[/QUOTE]
Если модуль существует:
Код:
uses
CdsUtilsEh;
CdsUtilsEh;
Если нет - написать его, глядя на подобные как пример. Это если резко желается что-то универсальное создать.