Здраствуйте. Есть проблема , может кто занимался или есть мысли в каком напрвлении двигаться — подскажите.
Пишется 3-звенная прилага (Delphi6-Delphi6-MSSQL2000). Связь с СП через TSocketConnection, с MSSQL — через BDE.
Необходимо организовать загрузку большого объема данных (много записей). Так как скорость критична, желательно скачивать данные порциями, но плавно (не заметно для клиента) — то ли как-то реагировать на прокрутку, то ли еще что...
Заранее спасибо за подсказки
Здравствуйте, bmv, Вы писали:
bmv>Здраствуйте. Есть проблема , может кто занимался или есть мысли в каком напрвлении двигаться — подскажите. bmv>Пишется 3-звенная прилага (Delphi6-Delphi6-MSSQL2000). Связь с СП через TSocketConnection, с MSSQL — через BDE. bmv>Необходимо организовать загрузку большого объема данных (много записей). Так как скорость критична, желательно скачивать данные порциями, но плавно (не заметно для клиента) — то ли как-то реагировать на прокрутку, то ли еще что... bmv>Заранее спасибо за подсказки
1. Зачем клиенту большой объем данных сразу?
2. "Данные закачиваются порциями" = TClientDataSet.PacketRecords
a. PacketRecords is automatically set to -1, meaning that a single packet should contain all records in the dataset.
b. If PacketRecords is greater than zero, then it specifies the number of records to return in a packet.
c. To retrieve the metadata for a dataset, set PacketRecords to 0. When PacketRecords is zero, the provider returns only information from its dataset that defines the database’s structure, such as table, column, constraint, and domain definitions.
3. "как-то реагировать на прокрутку" (не знаю, где прокрутка, на вскидку) TClientDataSet.DisableControls/TClientDataSet.EnableControls
4. Мастер-детаил при больших объемах данных лучше строить на клиенте т.к. для каждой записи мастера выполняется запрос, упаковываются данные в пакет и пересылаются клиенту. Лучше 2 запросами все вытянуть и связать на клинте.
5. "с MSSQL — через BDE" используй ADO.
P.S. Хорошенько подумай над пунктом 1.
Здравствуйте, bmv, Вы писали:
bmv>Необходимо организовать загрузку большого объема данных (много записей). Так как скорость критична, желательно скачивать данные порциями, но плавно (не заметно для клиента) — то ли как-то реагировать на прокрутку, то ли еще что...
А зачем? И исчо, имей в виду, при такой архитектуре масштабируемость приложения падает, ведь по сути на клиент держит подключение к БД. Оптимально подключился к БД, отсосал данные-отключился. И большой набор данных тоже надо давить, пусть ользователь более конкретные критерии поиска вводит.
LG>5. "с MSSQL — через BDE" используй ADO.
Поддерживаю на все 100
Здравствуйте, LG, Вы писали:
LG>1. Зачем клиенту большой объем данных сразу?
Скажу по другому Есть большой перечень к примеру деталей. Надо чтобы пользователь мог свободно передвигаться по нему вверх и вниз. Задание фильтров и поиск — это уже другое дело, но надо обеспечить пользователю свободу перемещения по списку. Замечу — быстрое перемещение.
LG>2. "Данные закачиваются порциями" = TClientDataSet.PacketRecords
Про это знаю, но к сожалению у меня провайдер не статичный. В любом случае это не до конца решает проблему VVVVV
LG>3. "как-то реагировать на прокрутку" (не знаю, где прокрутка, на вскидку) TClientDataSet.DisableControls/TClientDataSet.EnableControls
Имею ввиду что если искусственно закачивать данные порциями (экранами), то возникает проблема с полосой прокрутки...
Вот если бы что-то по типу как это делает сам MSSQL Enterprise Manager, когда открывает большие таблицы...
LG>5. "с MSSQL — через BDE" используй ADO.
Хорошо было бы ... да нельзя
В любом случае, спасибо за ответ. Может кто еще что посоветует...
Здравствуйте, bmv, Вы писали:
bmv>Здравствуйте, LG, Вы писали:
LG>1. Зачем клиенту большой объем данных сразу? bmv>Скажу по другому Есть большой перечень к примеру деталей. Надо чтобы пользователь мог свободно передвигаться по нему вверх и вниз. Задание фильтров и поиск — это уже другое дело, но надо обеспечить пользователю свободу перемещения по списку. Замечу — быстрое перемещение.
Это маразм. Действительно, какой толк ходить по списку из 10000 записей? намного правильнее при проектировании системы редусмотреть толковую фильтрацию.
LG>5. "с MSSQL — через BDE" используй ADO. bmv> Хорошо было бы ... да нельзя
Здравствуйте, bmv, Вы писали:
LG>>3. "как-то реагировать на прокрутку" (не знаю, где прокрутка, на вскидку) TClientDataSet.DisableControls/TClientDataSet.EnableControls bmv>Имею ввиду что если искусственно закачивать данные порциями (экранами), то возникает проблема с полосой прокрутки... bmv>Вот если бы что-то по типу как это делает сам MSSQL Enterprise Manager, когда открывает большие таблицы...
Вопрос порционной(постраничной) выборки тесно связан с вопросом нумерации строк в запросе (см. FAQ.Нумерация записей в запросе). Другими словами, для того, чтобы выбрать N-ую порцию из результатов запроса, нужно сначала пронумеровать результаты этого запроса.
Отсюда и похожие методы решения
Вариант 1 «Классический».
SELECT TOP 100 * FROM MyTable
WHERE id NOT IN (SELECT TOP 100 id FROM MyTable ORDER BY id) ORDER BY id
Главный недостаток этого метода в в том, что т.к. TOP n записей выбираются уже из конечного результата запроса, то проверка условия WHERE будет выполняться для каждой строки главного запроса. При этом, время выполнения этой проверки будет расти вместе с номером выбираемой порции(страницы). Если, например, таблица содержит 100 записей и необходимо выбирать данные порциями по 10 записей, то
для 2-ой порции нужно будет будет проверять подзапрос из 10 записей
для 3-ей порции нужно будет будет проверять подзапрос из 20 записей
для 4-ой порции нужно будет будет проверять подзапрос из 30 записей и т.д.
Достоинство метода в его универсальности, академичности. Он не требует специфики T-SQL, этот метод можно применить практически на любом SQL-сервере.
Вариант 2 «Эффективный, специфический для T-SQL».
Как и в случае нумерации строк данный метод основан на использовании временной таблицы. Для удобства оформим наш запрос как хранимую процедуру, возвращающую n-ую порцию(страницу), содержащую m записей
CREATE PROCEDURE dbo.get_this_page (@rec_per_page int, @page_num int) AS
SELECT identity(int, 1,1) AS RowNum, MyId AS OrigId INTO #tmp FROM mytable
SELECT b.* FROM #tmp AS a
INNER JOIN mytable AS b on a.OrigId = b.MyId
WHERE a.RowNum BETWEEN (@rec_per_page * @page_num + 1)
AND (@rec_per_page * (@page_num + 1))
DROP TABLE #tmp
Прмечания.
— предложенный вариант процедуры будет блокировать базу tempdb на все время выполнения 1-го запроса. Если время блокировки становиться неприемлимым, то необходимо разбить этот запрос таким образом
CREATE TABLE #temp(RowNum int identity, OrigId int)
INSERT INTO #temp(OrigId) SELECT MyId FROM mytable
— Если, поле MyId было создано признаком «IDENTITY», то это поле в запросе необходимо «завернуть» в функцию «CONVERT», иначе будет сообщение об ошибке.
Glory
Здравствуйте, dimzon, Вы писали:
D>Это маразм. Действительно, какой толк ходить по списку из 10000 записей? намного правильнее при проектировании системы редусмотреть толковую фильтрацию.
Предложи, как это будет выглядеть визуально. Если выдавать окошко "Задайте фильтр", то фильтр-то могут задать и такой, что в него все равно попадет очень много записей — В таблице всего 240000...
LG>>5. "с MSSQL — через BDE" используй ADO. bmv>> Хорошо было бы ... да нельзя D>Почему?
Требование заказчика
Здравствуйте, LG, Вы писали:
LG>Здравствуйте, bmv, Вы писали:
LG>>>3. "как-то реагировать на прокрутку" (не знаю, где прокрутка, на вскидку) TClientDataSet.DisableControls/TClientDataSet.EnableControls bmv>>Имею ввиду что если искусственно закачивать данные порциями (экранами), то возникает проблема с полосой прокрутки... bmv>>Вот если бы что-то по типу как это делает сам MSSQL Enterprise Manager, когда открывает большие таблицы... LG>
LG>Вопрос порционной(постраничной) выборки тесно связан с вопросом нумерации строк в запросе (см. FAQ.Нумерация записей в запросе). Другими словами, для того, чтобы выбрать N-ую порцию из результатов запроса, нужно сначала пронумеровать результаты этого запроса.
LG>Отсюда и похожие методы решения
LG>Вариант 1 «Классический».
LG>SELECT TOP 100 * FROM MyTable
LG>WHERE id NOT IN (SELECT TOP 100 id FROM MyTable ORDER BY id) ORDER BY id
LG>Главный недостаток этого метода в в том, что т.к. TOP n записей выбираются уже из конечного результата запроса, то проверка условия WHERE будет выполняться для каждой строки главного запроса. При этом, время выполнения этой проверки будет расти вместе с номером выбираемой порции(страницы). Если, например, таблица содержит 100 записей и необходимо выбирать данные порциями по 10 записей, то
LG>для 2-ой порции нужно будет будет проверять подзапрос из 10 записей
LG>для 3-ей порции нужно будет будет проверять подзапрос из 20 записей
LG>для 4-ой порции нужно будет будет проверять подзапрос из 30 записей и т.д.
LG>Достоинство метода в его универсальности, академичности. Он не требует специфики T-SQL, этот метод можно применить практически на любом SQL-сервере.
LG>Вариант 2 «Эффективный, специфический для T-SQL».
LG>Как и в случае нумерации строк данный метод основан на использовании временной таблицы. Для удобства оформим наш запрос как хранимую процедуру, возвращающую n-ую порцию(страницу), содержащую m записей
LG>CREATE PROCEDURE dbo.get_this_page (@rec_per_page int, @page_num int) AS
LG>SELECT identity(int, 1,1) AS RowNum, MyId AS OrigId INTO #tmp FROM mytable
LG>SELECT b.* FROM #tmp AS a
LG>INNER JOIN mytable AS b on a.OrigId = b.MyId
LG>WHERE a.RowNum BETWEEN (@rec_per_page * @page_num + 1)
LG>AND (@rec_per_page * (@page_num + 1))
LG>DROP TABLE #tmp
LG>Прмечания.
LG>- предложенный вариант процедуры будет блокировать базу tempdb на все время выполнения 1-го запроса. Если время блокировки становиться неприемлимым, то необходимо разбить этот запрос таким образом
LG>CREATE TABLE #temp(RowNum int identity, OrigId int)
LG>INSERT INTO #temp(OrigId) SELECT MyId FROM mytable
LG>- Если, поле MyId было создано признаком «IDENTITY», то это поле в запросе необходимо «завернуть» в функцию «CONVERT», иначе будет сообщение об ошибке.
LG>Glory
Спасибо за развернутый ответ, все это, конечно, здорово, только все-таки как решить при этом вопрос с прокруткой данных пользователем
Здравствуйте, dimzon, Вы писали:
D>Здравствуйте, LG, Вы писали:
LG>>Здравствуйте, bmv, Вы писали:
D>
LG>>Вариант 1 «Классический».
D><опущено>
D>Можно еще использовать: D>
D>Сначала выбираем на клиента все первичные ключи — объем будет невелик ибо в выборке будут только ключи.
D>потом за каждой страницей(блоком) делаем select * from xxx where id in (<список идентификаторов>)
В таблице 240000 записей, причем каждая является уникальной (как я уже говорил, перечень деталей). Мне не очень охота хранить в оперативной памяти 240000 ключей, даже на сервере приложений.
bmv>В таблице 240000 записей, причем каждая является уникальной (как я уже говорил, перечень деталей).
И что, они все будут удовлетворять условию поиска?
И второй вопрос, что пользователь будет делать с такой необъятной таблицей? Глазами нужную искать? Повторюсь — ЗАДУМАЙТЕСЬ О ХОРОШЕЙ ФИЛЬТРАЦИИ, ДАВАТЬ ПОЛЬЗОВАТЕЛЮ ТАКУЮ ПРОСТЫНЮ ЭТО МАРАЗМ
bmv>Мне не очень охота хранить в оперативной памяти 240000 ключей, даже на сервере приложений.
Гы. Простая математика. Пусть у нас есть искуственный первичный ключ IDENTITY. В памяти один первичный ключ занимает 4 байта. (240000 * 4)/1024=937,5 килобайт < 1Мб.
Почему нельзя это счастье забрать на клиетна, разве один мегабайт это так много?
... << RSDN@Home 1.0 beta 7a >>
Re[7]: Загрузка большого объема данных
От:
Аноним
Дата:
25.06.03 10:53
Оценка:
Здравствуйте, dimzon, Вы писали:
D>Здравствуйте, bmv, Вы писали:
bmv>>В таблице 240000 записей, причем каждая является уникальной (как я уже говорил, перечень деталей). D>И что, они все будут удовлетворять условию поиска? D>И второй вопрос, что пользователь будет делать с такой необъятной таблицей? Глазами нужную искать? Повторюсь — ЗАДУМАЙТЕСЬ О ХОРОШЕЙ ФИЛЬТРАЦИИ, ДАВАТЬ ПОЛЬЗОВАТЕЛЮ ТАКУЮ ПРОСТЫНЮ ЭТО МАРАЗМ
Я с этим согласен, пользователю все это не нужно... Только я не могу понять, как будет в Вашем случае интерфейс пользователя выглядеть. Вот пользователь открывает окно и...
Я вижу два варианта: 1 — уже что-то ему показать (что я и пытаюсь сделать), однако этот вариант предполагает, что он может двигаться по списку, не задавая фильтров.
2 — выскакивает окно с просьбой задать фильтр. Однако здесь он может задать такой фильтр, который нас и не спасет вовсе от большого списка.
Может я что-то не вижу? Какие есть еще варианты?
D>Гы. Простая математика. Пусть у нас есть искуственный первичный ключ IDENTITY. В памяти один первичный ключ занимает 4 байта. (240000 * 4)/1024=937,5 килобайт < 1Мб. D>Почему нельзя это счастье забрать на клиетна, разве один мегабайт это так много?
Подсчет конечно верный, только что нам дает хранение этого счастья? Кроме занимаемого 1 Мб ОП (который отнюдь не лишний), по-моему, ничего. Как правило данные отображаемые пользователю отсортированы вовсе не по колонке IDENTITY. И вообще, зачем хранить полный список, если у отображаемой нами порции данных всегда можно узнать начальный ключ и конечный ключ и, соответственно, загрузить следующую необходимую порцию.
Здравствуйте, <Аноним>, Вы писали:
А>Здравствуйте, dimzon, Вы писали:
D>Здравствуйте, bmv, Вы писали:
bmv>>В таблице 240000 записей, причем каждая является уникальной (как я уже говорил, перечень деталей). D>И что, они все будут удовлетворять условию поиска? D>И второй вопрос, что пользователь будет делать с такой необъятной таблицей? Глазами нужную искать? Повторюсь — ЗАДУМАЙТЕСЬ О ХОРОШЕЙ ФИЛЬТРАЦИИ, ДАВАТЬ ПОЛЬЗОВАТЕЛЮ ТАКУЮ ПРОСТЫНЮ ЭТО МАРАЗМ А>Я с этим согласен, пользователю все это не нужно... Только я не могу понять, как будет в Вашем случае интерфейс пользователя выглядеть. Вот пользователь открывает окно и...
---------------------------------------------------------------
| Здесь поля фильтра и кнопка "Обновить" |
---------------------------------------------------------------
| |
| Здесь злосчастная таблица. При открытии вместо нее надпись: |
| Заполните фильтр и нажмите обновить |
| |
| При отсутствии данных, удовлетворяющих фильтру надпись: |
| "Не найденно ни одной записи, удовлетворяющей условию" |
| |
---------------------------------------------------------------
А>Однако здесь он может задать такой фильтр, который нас и не спасет вовсе от большого списка.
Надо искуственно ограничить размер выборки скажем 500-и строк. Тогда пишем такой запрос:
SET ROWCOUNT 501
SELECT ... И понеслась...
SET ROWCOUNT 0
И смотрим сикоко строк вернулось, если 501 то все равно показываем 500 и где-нить на форме выводим предупреждение: "Показаны первые 500 записей. если надо что-то конкретное ужесточите условия поиска"
D>Гы. Простая математика. Пусть у нас есть искуственный первичный ключ IDENTITY. В памяти один первичный ключ занимает 4 байта. (240000 * 4)/1024=937,5 килобайт < 1Мб. D>Почему нельзя это счастье забрать на клиетна, разве один мегабайт это так много? А>Подсчет конечно верный, только что нам дает хранение этого счастья? Кроме занимаемого 1 Мб ОП (который отнюдь не лишний), по-моему, ничего. Как правило данные отображаемые пользователю отсортированы вовсе не по колонке IDENTITY. И вообще, зачем хранить полный список, если у отображаемой нами порции данных всегда можно узнать начальный ключ и конечный ключ и, соответственно, загрузить следующую необходимую порцию.
Ну во первых можно получить список идентификатров уже отсортированный как надо (select ProductID from dbo.Product order by ProductName), Во вторых это "облегчает" выполнение повторных запросов, ведь фильрация может быть далеко не тривиальной, а так один раз уже отфильтровано и при повторных запросов поиск происходит просто по значению первичного ключа что для SQL расплюнуть.
Здравствуйте, bmv, Вы писали:
bmv>Спасибо за развернутый ответ, все это, конечно, здорово, только все-таки как решить при этом вопрос с прокруткой данных пользователем
Так это, вроде как надо свой класс — Грида переопределять, в нем с полосой прокрутки работать.
Не знаю может для дельфы есть готовые решения?
Мы на С++ свой грид делали.
Суть алгоритма — сразу закачивать строк, превышающих, отобрражаемое в гриде, и сверху и снизу, коэффициент зависит от Вас, какой выберете. Далее по мере приближения к краю списка строк пользователем при прокрутки, покачивать строки со стороны, в которую пользователь продвинулся, а сдругой стороны отгружать. Опять таки следует выбрать коэффициент — количество буферных строк, при котором не осуществлять подкачку, чтобы она не была слишком частой, а то пользователь сместиться на одну строку вперед, а потом назад, а программа каждый раз будет подкачку осуществлять
И не слушайте dimson-а. Заладил фильтраци, фильтрация.
То, что Вы делаете есть более красивое решение, а фильрация это отдельная тема.
Здравствуйте, Vitaton, Вы писали:
V>Здравствуйте, bmv, Вы писали:
bmv>>Спасибо за развернутый ответ, все это, конечно, здорово, только все-таки как решить при этом вопрос с прокруткой данных пользователем
V>Так это, вроде как надо свой класс — Грида переопределять, в нем с полосой прокрутки работать.
А почему бы вместо этого не использовать paging-представление? Для показа большого гомогенных данных это явно напрашивается. Да и в реализации вероятно будет проще, чем мудрить с полосами прокрутки.
Здравствуйте, Vitaton, Вы писали:
V>Здравствуйте, bmv, Вы писали:
V>Так это, вроде как надо свой класс — Грида переопределять, в нем с полосой прокрутки работать. V>Не знаю может для дельфы есть готовые решения?
TClientDataSet вообще-то слинкованный с обычным DataGrid работает
V>И не слушайте dimson-а. Заладил фильтраци, фильтрация. V>То, что Вы делаете есть более красивое решение, а фильрация это отдельная тема.
Это НЕ более красивое решение! Это НЕРАЦИОНАЛЬНОЕ решение, к тому-же слабо масштабируемое! Нафиг пользователю в данном как минимум эту дурацкую простыню из 240000 записей видеть не надо! Проектировать надо систему грамотно вместо того чтобы извращаться при реализации бредовой идеи. А то получается (в данном случае)
"мы сделаем чтобы пользователь мог быстро листать охрененную простыню и визуально искать что ему нужно(при этом сами встанем раком реализуя скорость и сервер посадим тоже)"
вместо того чтобы:
"мы сделаем чтобы пользователь мог легко и просто найти нужную запись введя критерии фильтрации"
Здравствуйте, dimzon, Вы писали:
D>Надо искуственно ограничить размер выборки скажем 500-и строк. Тогда пишем такой запрос:
Так ведь об этом то и речь! Только зачем просить его ужесточить условия поиска если можно в фоновом режиме подгрузить следующие записи если ему так приспичило ходить по большой выборке. И здесь мы как раз получаем проблемы со ScrollBar.
D>Ну во первых можно получить список идентификатров уже отсортированный как надо (select ProductID from dbo.Product order by ProductName), Во вторых это "облегчает" выполнение повторных запросов, ведь фильрация может быть далеко не тривиальной, а так один раз уже отфильтровано и при повторных запросов поиск происходит просто по значению первичного ключа что для SQL расплюнуть.
И чем мне поможет, если у меня будет перечень ИД, отсортированных в каком-то там порядке? В очередном запросе для выборки 500 записей я должен буду перечислить 500 ИД? В любом случае придется повторять условие сортировки по-моему.
MX>А почему бы вместо этого не использовать paging-представление? Для показа большого гомогенных данных это явно напрашивается. Да и в реализации вероятно будет проще, чем мудрить с полосами прокрутки.
А что это такое и с чем его едят?
Здравствуйте, dimzon, Вы писали:
V>>Не знаю может для дельфы есть готовые решения? D>TClientDataSet вообще-то слинкованный с обычным DataGrid работает
И как в делфийских гридах можно устанавливать, какую часть DataSet'а следует просматривать? Речь идет о точной установке с какой по какую запись. Это-то по большому счету меня и интересует.
Здравствуйте, Vitaton, Вы писали:
V>Так это, вроде как надо свой класс — Грида переопределять, в нем с полосой прокрутки работать. V>Не знаю может для дельфы есть готовые решения? V>Мы на С++ свой грид делали. V>Суть алгоритма — сразу закачивать строк, превышающих, отобрражаемое в гриде, и сверху и снизу, коэффициент зависит от Вас, какой выберете. Далее по мере приближения к краю списка строк пользователем при прокрутки, покачивать строки со стороны, в которую пользователь продвинулся, а сдругой стороны отгружать. Опять таки следует выбрать коэффициент — количество буферных строк, при котором не осуществлять подкачку, чтобы она не была слишком частой, а то пользователь сместиться на одну строку вперед, а потом назад, а программа каждый раз будет подкачку осуществлять
Во-во, вот эти решения меня больше всего и интересуют. К сожалению свой грид писать — очень много времени потерять (если, конечно, хороший). Я этого в данный момент себе позволить не могу.
В любом случае придется или сделать как советует dimzon, или кто-то еще что скажет.
Здравствуйте, bmv, Вы писали:
bmv>Здравствуйте, dimzon, Вы писали: bmv> D>>Надо искуственно ограничить размер выборки скажем 500-и строк. Тогда пишем такой запрос: bmv>Так ведь об этом то и речь! Только зачем просить его ужесточить условия поиска если можно в фоновом режиме подгрузить следующие записи если ему так приспичило ходить по большой выборке. И здесь мы как раз получаем проблемы со ScrollBar.
D>>Ну во первых можно получить список идентификатров уже отсортированный как надо (select ProductID from dbo.Product order by ProductName), Во вторых это "облегчает" выполнение повторных запросов, ведь фильрация может быть далеко не тривиальной, а так один раз уже отфильтровано и при повторных запросов поиск происходит просто по значению первичного ключа что для SQL расплюнуть. bmv>И чем мне поможет, если у меня будет перечень ИД, отсортированных в каком-то там порядке? В очередном запросе для выборки 500 записей я должен буду перечислить 500 ИД? В любом случае придется повторять условие сортировки по-моему.
Если условие выборки — сложное, то скорее всего количество возвращённых записей будет невелико.
И наоборот.
В первом случае показываем все записи.
Во втором, для показа очередных 500 записей, просто повторяем запрос, а в условие запроса добавляем "ID > [ID последней показанной записи]".
Здравствуйте, dimzon, Вы писали:
D>Здравствуйте, Vitaton, Вы писали:
V>>Здравствуйте, bmv, Вы писали:
V>>Так это, вроде как надо свой класс — Грида переопределять, в нем с полосой прокрутки работать. V>>Не знаю может для дельфы есть готовые решения? D>TClientDataSet вообще-то слинкованный с обычным DataGrid работает
Ну я не знаю о чем Вы, ну если что-то есть для Дельфы, так подскажите товарищу, который этот пост затеял.
Я же, не супер гуру в дельфе, а на C++ c MFC, я решал задачу кэшированной навигации и все работает тип топ. Да и кроме этого я сам разрабатывал подсистему (библиотеку базовых классов) работы с фильтрами. Так что немного разбирался и с первым и со вторым вопросами.
V>>И не слушайте dimson-а. Заладил фильтраци, фильтрация. V>>То, что Вы делаете есть более красивое решение, а фильрация это отдельная тема. D>Это НЕ более красивое решение! Это НЕРАЦИОНАЛЬНОЕ решение, к тому-же слабо масштабируемое! Нафиг пользователю в данном как минимум эту дурацкую простыню из 240000 записей видеть не надо! Проектировать надо систему грамотно вместо того чтобы извращаться при реализации бредовой идеи. А то получается (в данном случае) D>
D>"мы сделаем чтобы пользователь мог быстро листать охрененную простыню и визуально искать что ему нужно(при этом сами встанем раком реализуя скорость и сервер посадим тоже)"
D>вместо того чтобы: D>[q] D>"мы сделаем чтобы пользователь мог легко и просто найти нужную запись введя критерии фильтрации" D>[/q
Послушайте товарищ, пора бы уже успокоиться с фильтрацией. Никто не против, что д.б. подсистема фильтрации, для удобства пользователя. Но не смешивайте, есть два отдельных понятия: удобная подсистема фильтрации и кэшированная навигация. Если бы Вы попытались понять, что за алгоритмы существуют и предлагаются для решения проблемы кэшированной навигации по наборам данных, то и не упоминали даже о простынях 240000 записей и о слабой масштабируемости.
Решение проблемы с кэшированной навигации является довльно непростой, и конечно, если нет под рукой готовых решений, то стоит подумать браться за этй задачу или нет. Если не браться тогда можно ОГРАНИЧИТСЯ подсистемой фильтрации, что и предлагаете вот уже нексколько раз.
И к грамотности проектирования отсутсвие кэшированной навигации имеет малое отношение. Просто надо понять, стоит ли тех затрат разработка данного алгоритма или нет, того эффекта, который это приносит.
И если уж не понимаете о чем идет речь, то не называйте бредовыми идеи других, тем более, что кэшированную навигацию придумал не я, а это двовольно известная задача и есть ее решения у многих серьезных разработчиков.
Ну конечно с другой стороны, если Вы не способны решить подобную задачу, то легче ее назвать бредовой идеей и продолжать толдычить про фильтрацию, с которой никто и не спорит, что она нужна...
Здравствуйте, bmv, Вы писали:
bmv>Здравствуйте, MiXalych, Вы писали:
MX>>А почему бы вместо этого не использовать paging-представление? Для показа большого гомогенных данных это явно напрашивается. Да и в реализации вероятно будет проще, чем мудрить с полосами прокрутки. bmv>А что это такое и с чем его едят?
Ну, живой пример — этот форум. Погляди на его header, там будет что-то вроде:
<- 1-10/3221 -> //это собственно говоря и есть paging
Здравствуйте, bmv, Вы писали:
bmv>Здравствуйте, dimzon, Вы писали:
D>Надо искуственно ограничить размер выборки скажем 500-и строк. Тогда пишем такой запрос: bmv>Так ведь об этом то и речь! Только зачем просить его ужесточить условия поиска если можно в фоновом режиме подгрузить следующие записи если ему так приспичило ходить по большой выборке. И здесь мы как раз получаем проблемы со ScrollBar.
А зачем гемороится и кодировать поиск если пользователю все равно не нужна такая простыня. Пользователей надо дисциплинировать блин, и лишний раз напоминаю — такой скролинг это лишняя нагрузка не сервер(притом нерациональная), что не есть хорошо. Замечу такой скролинг подразумевает наличие посоянного подключения к серверу БД, что отрицательно сказывается на масштабируемости и в даном случае этого можно избежать.
D>Ну во первых можно получить список идентификатров уже отсортированный как надо (select ProductID from dbo.Product order by ProductName), Во вторых это "облегчает" выполнение повторных запросов, ведь фильрация может быть далеко не тривиальной, а так один раз уже отфильтровано и при повторных запросов поиск происходит просто по значению первичного ключа что для SQL расплюнуть. bmv>И чем мне поможет, если у меня будет перечень ИД, отсортированных в каком-то там порядке? В очередном запросе для выборки 500 записей я должен буду перечислить 500 ИД? В любом случае придется повторять условие сортировки по-моему.
Это мое предложение не касалось первого, это было о том как выбирать данные постранично. По поводу сортировки согласен, если выкачивать идентификаторы то их надо во первых выкачать упорядоченно и при получении каждой страницы повторять условия сортировки.
M>Если условие выборки — сложное, то скорее всего количество возвращённых записей будет невелико. M>И наоборот.
Не факт!
M>В первом случае показываем все записи.
M>Во втором, для показа очередных 500 записей, просто повторяем запрос, а в условие запроса добавляем "ID > [ID последней показанной записи]".
Не канает — теряется сортировка (нам ведь записи нужно не по ID упорядочивать)
Здравствуйте, MiXalych, Вы писали:
MX>Здравствуйте, bmv, Вы писали:
bmv>>Здравствуйте, MiXalych, Вы писали:
MX>>>А почему бы вместо этого не использовать paging-представление? Для показа большого гомогенных данных это явно напрашивается. Да и в реализации вероятно будет проще, чем мудрить с полосами прокрутки. bmv>>А что это такое и с чем его едят? MX>Ну, живой пример — этот форум. Погляди на его header, там будет что-то вроде: MX><- 1-10/3221 -> //это собственно говоря и есть paging
Понял. Это был 1 из вариантов...
Здравствуйте, bmv, Вы писали:
bmv>dimzon — спасибо за длинный диалог
И напоследок — расскажу как можно реализовать именно скролинг по простыне. Просто если вдруг это кому-либо нужно (в данном случае это не нужно!)
Итак, берем TListView (это в дельфи), пихаем в него пустые строки по количеству строк в выборке(для этого ведь данные с сервера качать не надо, достаточно просто кол-во) и пишем свой обработчик на OwnerDraw. Как только строка станет видимой компонент попросит ее нарисовать Вот тут-то и дергаем данные с сервера и отрисовываем. Если дергать с умом (скажем текущая строка + 50 строк выше + 50 строк ниже) и при этом кэшировать результат то поимеем правный скролинг и актуальную полосу прокрутки. И как раз тут прекрасно уляжется моя идея с получением списка идентификаторов с сервера на клиент в отсортированном виде Т.е. добавляя пустые строки в ListView мы каждой строке в Tag записываем идентификатор, а когда нужно отрисовать строку (в событии OwnerDraw) мы берем Tag строки + берем Tag-и 50 строк сверху и 50 строк снизу, получаем данные и пихаем в кэш
Здравствуйте, dimzon, Вы писали ужасные вещи.
Эта функциональность предоставляется вирутальным листвью. Включите ему OwnerData в true, установите Items.Count в нужное количество и поехали. В смысле поехали читать доку.
А добавление 240000 пустых итемов съест непристойно много времени и памяти.
... << RSDN@Home 1.0 beta 7a >>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
По моему, простыня даже из 500 записей — это полный маразм, пожалейте пользователя — ему вовсе не нужна "полная свобода", ему нужно, чтобы он мог как можно более удобнее вполнить определенную задачу.
С 240000 конечно, не сталкивались, но даже на 10000-записей пользователи нас озадачили, что "ходить" по такому объему данных не слишком удобно и муторно.
В итоге мы нашли следующее решение проблемы, которое было достаточно просто в реализации и устраивало пользователей.
Для каждого справочника (а речь, как я понимаю, идет имеенно о справочние, хотя это применимо и для других классов данных), была сделана возможность организовавыть его элементы в группы, причем с какой-угодно иерархией. Аналогично файловой системе "Папки — файлы". И что вы думаете, через 2 дня все эти 10000 элементов пользователями были разложены по нужным папкам в оостветствии с их предпочтениями (в нашем случае это был материальный справочник сырья-компонентов для ликеро-водочной промышленности). А для навигации по всему этому добро было выбрано решение, аналогично диалогу выбора файлов (кончечно, с учетом специфики процесса), где вверху — комбобох с папками, а для выбранной папки в спсике отображаются материалы в этой папке. Так вот, после разложения всех элементов справочника по папкам, оказалось, что наибольшее количество элементов в одной папке — около 120 (по моему, "Травы и корни") и что и загружается мгновенно, и пользователюпонятно, где искать. Конечно, был сделан поиск по различным критериям, в результате выполнения которого выводился список элементов с указанием того, в какой папке они находятся. Однако, поиском пользовались, в основном, на первом этапе, и только те, кто не структурировал сравочник, следовательно, не знал, по какому принципу он был разбит на папки. Потом, примерно через 2 недели, все пользователи въехали и с тех пор все (пользователи и разработчики)избавились от головной боли.
Мораль сей басни такова: надо хорошо продумывать пользовательский интерфейс и грамотно проектировать структуру данных системы, чтобы потом героически не решать созданную, прежде всего, собой, проблему "кеширования навигации".
Здравствуйте, aston, Вы писали:
A>Спасибо за внимание.
Это правильный подход. Точнее, первый шаг на этом, безусловно правильном, пути
В свое время я столкнулся с определенными проблемами при работе с подобными справочниками.
У нас был каталог товаров. (оптово-торговая компания).
Так вот, оказалось, что не существует одинаково удобной для всех каталогизации.
Для бухгалтерии эти товары должны были быть упорядочены по НДС (10% либо 20%).
Для менеджеров по продажам — по "типу", т.е. что-то типа ПродуктыПитания/соусы/кетчупы/.
Для менеджеров по поставкам — по производителю.
Поскольку система поддерживала только группировку "вручную" (а не по фактическим значениям свойств), возникло две проблемы:
1. Стихийно организовалась структура, сочетающая в себе все подряд. Типа ПродуктыПитания/НДС10/Кетчупы/Балтимор/.
2. Легко ошибиться при внесении товара в каталог, и шансы его найти будут минимальны. Изменения свойств не приводят к перепозиционированию объекта — вы бы знали, сколько было стонов, когда на одну из категорий НДС сменили! Сначала искали на "новом" месте, потом привыкли искать на "старом", и когда перенесли...
Мораль: каталогизация должна быть множественной. Я в свое время думал над этим, но потом сменил работу и забил. Остановился я на том, что сие есть частный случай задачи "поиска с нечетко заданными условиями", которая находится на стыке психологии и математики.
... << RSDN@Home 1.0 beta 7a >>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Согласен, такая поблема существует, но, она, по моему мнению, в наследственных генах бухгалтеров, маркетологов и т.д.
В нашем случае она также возникла и тут всплыли следующие варианты решений: или предусматривать какое угодно количество вариантов группировки и каждый пользователь, по своему усмотрению, использует наиболее подходящий для своей конкретной предметной области, либо внедрить единый внутрикорпоративный стандарт каталогиации элементов. Первое решение, на первый взгляд, вроде снимало все проблемы, но существовала одна фундаметальная проблема: при добавлении элемента в такой справочник (его поддерживал отдел снабжения, как первое лицо,которое сталкивается с новым товаром), элемент помещался в папку того варианта группировки, с которым работал отдел снабжения, в остальных вариантах этого товара как бы не существовало до особого действия пользователя, который привязывал товар к другим вариантам группировки. После этого стали возникать случаи, кодга снабженец вроде внес товар в справочник, а технолог его не видит. Поэтому, в конце концов, был принят вариант с единым каталогом. Шума было много, да времени потребовалось около 2 месяцев, чтобы все подразделения привыкли, зато затем... лепота.
З.Ы. За это все и не любят Чубайса, что он принимает непопулярные решения, зато он хоть порядок навел.
Спасибо за внимание.
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, aston, Вы писали:
A>>Спасибо за внимание.
S>Это правильный подход. Точнее, первый шаг на этом, безусловно правильном, пути S>В свое время я столкнулся с определенными проблемами при работе с подобными справочниками. S>У нас был каталог товаров. (оптово-торговая компания). S>Так вот, оказалось, что не существует одинаково удобной для всех каталогизации. S>Для бухгалтерии эти товары должны были быть упорядочены по НДС (10% либо 20%). S>Для менеджеров по продажам — по "типу", т.е. что-то типа ПродуктыПитания/соусы/кетчупы/. S>Для менеджеров по поставкам — по производителю. S>Поскольку система поддерживала только группировку "вручную" (а не по фактическим значениям свойств), возникло две проблемы: S>1. Стихийно организовалась структура, сочетающая в себе все подряд. Типа ПродуктыПитания/НДС10/Кетчупы/Балтимор/. S>2. Легко ошибиться при внесении товара в каталог, и шансы его найти будут минимальны. Изменения свойств не приводят к перепозиционированию объекта — вы бы знали, сколько было стонов, когда на одну из категорий НДС сменили! Сначала искали на "новом" месте, потом привыкли искать на "старом", и когда перенесли... S>Мораль: каталогизация должна быть множественной. Я в свое время думал над этим, но потом сменил работу и забил. Остановился я на том, что сие есть частный случай задачи "поиска с нечетко заданными условиями", которая находится на стыке психологии и математики.
Здравствуйте, aston, Вы писали:
A>Согласен, такая поблема существует, но, она, по моему мнению, в наследственных генах бухгалтеров, маркетологов и т.д.
Да нет. Я сначала тоже так думал, но потом понял, что на практике очень часто процесс поиска принципиально неформализуем. В таких случаях надо решать эту проблему, а не говорить "пусть пользователь пойдет и подумает".
Отступление: помните игрушки для PC 80х? В начале они усердно предлагали пользователю при старте выбрать между CGA/EGA/VGA. Где-то году в 90м или чуть позже до народа доперло, что это можно узнать программным способом, и нефиг парить мозги человеку без тех.образования.
Похожая фигня и в проектировании АСУ. SQL, между прочим, в свое время разрабатывался как язык для конечных пользователей. Типа вот садится Клавдия Иванна за isql и пишет что-то типа "select * from store_orders where submit_date > 'yesterday'". Именно потому его синтаксис так похож на английский язык.
Оказалось, что на самом деле конечные пользователи слишком плохо формализуют свои запросы, и для них проще дать выбор из "показать накладные за сегодня/за вчера/за этот месяц".
Я клоню к тому, что в большинстве случаев каталогизация призвана выполнить функцию деления по какому-то свойству. Путь к элементу иерархии напоминает знаменитую игру: "Это большое?" -> "Это серое?" -> "Это слон"? Такая последовательность вопросов/ответов неявно подразумевает каталогизацию по размеру, а затем по цвету. Стоит отметить, что она не является произвольной — есть некие правила игры, и мы не можем поместить в каталог "Большое/серое" ни мышку ни кита.
Если такие правила существуют, значит их можно выразить в терминах group by. Именно этим занимается Outlook с его Group Bar — вы можете сами вытаскивать категории для каталогизации в нужном вам порядке.
Если бы в примере про каталог товаров мы пользовались интерфейсом такого типа, то бухгалтерия вытащила "в начало" категорию НДС, выписка — тип товара, а поставки — производителя. При этом каждому было бы удобно, и не было бы риска, что товар с НДС 20% попадет в каталог 10%.
Я подумывал о разработке такого интерфейса и пристально смотрел в сторону ExpressGrid для реализации.
К сожалению, большинство таких библиотек приводят к слишком низкому быстродействию, и я на это забил (хотя есть очевидные способы добиться приемлемых результатов).
К сожалению, у такого подхода (если им и ограничиваться) есть свои недостатки:
1. Недогруппировка. Если мы сгруппируем товар в каталоге по признаку НДС (и оставим остальные признаки свободными), то у нас получатся ровно две группы по 90000 и 10000 элементов соответственно. Упс.
2. Перегруппировка. Если мы сгруппируем товар по таймстампу регистрации в каталоге, то получим 100000 папок по 1 элементу (из-за того, что "12:14 03.03.2003" != "12:15 03.03.2003". Если быть более точным, то вообще идея прямой группировки по значениям примитивных типов порочна. Чуть ниже я напишу про это еще абзац.
3. Униформность группировки. В той игре с угадыванием животных при ответе "нет" на вопрос "большое" следующим вопросом мог быть вовсе не цвет. При каталогизации по признакам есть риск получить сильно несбалансированное дерево. Например, если мы искуственно определим признак "новыйТовар" как "таймстамп регистрации в каталоге в пределах недели от текущей даты", то группировка по нему разложит товар на ровно две группы; при этом в одной будет 10-100 позиций, а в другой остальные сто тысяч. Введение группировки на следующем уровне приведет либо к проблеме 1 в "большой" группе, либо к проблеме 2 в "маленькой", либо к той и другой сразу.
Решением проблемы 1 и частичным решением проблемы 2 является подбор "правильного" набора группирующих признаков. "Правильность" набора можно определить как требование иметь на каждом уровне как можно ближе к 10 позиций (либо подгрупп). К сожалению, существующие системы не дают никакого способа заранее оценить эту правильность. То есть первичное взаимодействие с системой — это "стрельба вслепую". Перелет — недолет — попал.
Решение той части проблемы 2, которая вызвана наличием большого количества вариантов значения признака, значительно сложнее. Интуитивное решение — разделить диапазон значений на сравнительно небольшое количество интервалов — соответствует введению искуственных признаков. Именно это делает Internet Explorer при просмотре Navigation History. Он раскладывает визиты на папки Сегодня — Вчера — Эта неделя — Этот месяц... (При этом надо отдельно отметить тот факт, что пустые группы не показываются вообще). Очевидно, что для каждого частного случая мы можем подобрать необходимый набор искуственных признаков.
Вообще говоря, введение искусственных признаков — достаточно эффективный путь решения проблем группировки. Он применяется и для проблемы 1 — например, телефонные книги сгруппированы по первой букве. А многотомные словари группируются еще более неочевидным способом — там интервалы задаются первыми несколькими буквами (напр. "АБЛ-ДАР"), а для их определения используется критерий количества информации в каждом поддиапазоне.
По поводу проблемы 3 я не видел пока никаких решений, кроме введения той самой "файловой системы", при которой критерии определяются пользователем. Примером развитых систем такого типа служат почтовые программы с их наборами правил. Я не уверен в жизнеспособности таких решений. Дело в том, что редкая почтовая программа имеет дело с десятками тысяч сообщений, которые необходимо одновременно использовать. На паре тысяч элементов почти любая система каталогизации будет работать вполне успешно.
Подводя итог:
1. Можно ввести критерий "правильности" системы каталогизации, подсчитывая количество элементов на каждом уровне иерархии
2. "Естественных" признаков не всегда достаточно для построения "правильной" каталогизации. В общем случае трудно предложить автоматический способ генерации искуственных признаков; есть некоторые варианты:
— деление, специфичное для типа данных. Даты естественным образом сгруппированы в иерархический набор интервалов по своим компонентам (век/декада/год/квартал/месяц/неделя/день); строковые данные можно группировать по первым символам. Для числовых данных можно привязываться к "круглости" чисел.
— равномерное деление интервала по значению признака (телефонные книги. На каждую букву отводится по группе, независимо от фактического количества фамилий в ней)
— логарифмическое деление интервала по значению признака (товары от 1 до 10 рублей/от 11 до 100 рублей/от 101 до 1000 рублей...)
— равномерное деление интервала по количеству элементов в нем (словари)
3. Жесткая привязка признака каталогизации к уровню может привести к "неправильности" каталогизации при любом наборе естественных и искуственных признаков
Напоследок попробую придумать "идеальную" систему:
1. Начинаем с достаточно многочисленного множества элементов.
2. Система должна уметь предложить достаточно удачный признак для категоризации верхнего уровня в начале работы (при необходимости).
3. Если пользователь задал другой признак, то система должна уметь автоматически построить по нему искуственный признак в случае необходимости (задали группировку по названию товара — будем группировать по первой букве)
4. При выборе категории для просмотра, система должна перейти к множеству объектов, ограниченному заданным значением признака, и перейти к п.2.
4.1. При наличии вариантов признаков на этом уровне, система должна учитывать выбор, сделанный на предыдущем шаге. Т.е. если мы выбрали группировку по дате, и был введен искусственный признак "год", то на следующем уровне предпочитаемым прижнаком будет "месяц", даже если есть другие кандидаты. При этом система не должна быть жесткой в том смысле, что при отсутствии необходимости группировать (мало элементов в заданном году) никакой группировки и не надо.
5. При возврате на уровень вверх, система должна "вспомнить" историю навигации, чтобы вернуться к шагу 3, а не шагу 2.
Естественно, это только маленький эскиз на кусочке салфетки, и для проектирования реальной системы информации недостаточно. Надо также уметь запоминать предпочтения пользователя с тем, чтобы ему не приходилось слишком часто менять в п.3 решение, предложенное системой в п.2. При этом система должна быть все еще достаточно гибкой, чтобы при резком изменении объемов данных для просмотра не получать слишком "неправильных" способов категоризации.
... << RSDN@Home 1.1 alpha 1 >>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.