Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Базы";
Текущий архив: 2002.08.12;
Скачать: [xml.tar.bz2];

Вниз

Фильтры в виде дерева   Найти похожие ветки 

 
sokoloff   (2002-07-22 13:07) [0]

Встала задача организовать фильтры к базе в виде дерева, причем с наследованием, т.е. например дерево выглядит так:

\__
|
|- Москва(city like "Moscow") -
| |
| |- Ивановы(name like "Ivanov")
| |
| |- Петровы(name like "Petrov")
|
|- Питер
. . .


При выборе узла "Москва" фильтр должен выглядеть как-
SELECT * FROM MAIN WHERE CITY LIKE "Moscow",
а при выборе "Ивановы" как-
SELECT * FROM MAIN WHERE CITY LIKE "Moscow" and NAME LIKE "Ivanov", т.е. добавляются все значения выше лежащих узлов.
(SQL запросы не реальные, приведены только для примера)

Как это сделать я вобщем представляю, даже вчерне работает.

Но разговаривал с другом и он сказал, что читал статью описывающую мой слычай, но не помнит где, говорит то-ли на мастаке, то-ли на королевстве, а может и еще где.
Интересно прочитать эту статью, может какие грабли обойду. Поиск через гугл ничего похожего не дал.
Может у кого осталась ссылка на эту статью, или хоть сайт точно назовете, а может друг чего и перепутал, тогда не буду ковырять инет впустую.


 
kaif   (2002-07-22 14:39) [1]

Откуда такая любовь к деревьям?



 
kalliopiy   (2002-07-22 15:38) [2]

Если это тебе еще интересно, то можно было бы обсудить, но только хочется узнать побольше из того, что тебе требуется на выходе. Ну, например, насколько глубокое твое дерево, и при каких условиях, вообще, ты собираешься фильтровать таблицу ( т.е. я так понимаю, что пользователь из TreeView выбирает узелок, а тут бац и табличка отфильтровалась... Илил еще как?).
Поподробнее, плз.


 
Sokoloff   (2002-07-22 16:06) [3]


> Откуда такая любовь к деревьям?

От пользователей+начальство. Вообще IMHO требование логично.
Прога - морда к базе данных, есть возможность создавать и сохранять фильтры, в первой версии был просто комбобокс с названиями фильтров, но количество последних растет, управлять ими стало неудобно. Попросили сделать иеерархию фильтров, чтоб проще с ними работать. Теперь интерфейс программы становиться похожим на почтового клиента, слева-вверху дерево фильтров (почтовые ящики), справа-вверху dbtable с краткими сведениями(спсок писем в ящике), внизу по всей ширине подробные сведения о выбранной записи(текст письма), для пользователей привычно, а это есть гуд.


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

Жосткого ограничения нет, сколько на создают - столько и будет

> и при каких условиях, вообще, ты собираешься фильтровать
> таблицу ( т.е. я так понимаю, что пользователь из TreeView
> выбирает узелок, а тут бац и табличка отфильтровалась...

Да, именно так.

Фильтры хочу хранить в самой базе, необходимо добавление/удаление/перемещение фильтра, изменение его значения.
Главное, что фильтры должны наследоваться, т.е. если родительский узел хранит "city like "Moscow" and firmtype like "ЗАО"", а дочерний по которому кликнули "name like "Pupkin"" то запрс к базе буде типа
"SELECT * from main where city like "Moscow" and firmtype like "ЗАО" and name like "Pupkin"".
Соответственно если у родительского узла есть свой родитель, то к этому запросу должно прибавляться значение дедушки.
Значение может переопределяться детьми.
Отец - "city like "Moscow" and firmtype like "ЗАО""
Сын - "firmtype like "ООО"",
запрос - "SELECT * from main where city like "Moscow" and firmtype like "ООО"".







 
kalliopiy   (2002-07-22 16:35) [4]

Короче, я чего-то понял, чего-то - нет.

Откуда у тебя это дерево берется, вообще? Из таблицы? Или все забито в программе?

Ситуация такая - проще всего процесс фильтрации автоматизировать используя рекурсию. Пишешь, например, функцию NodeFilter с параметром Node - выбранный узел дерева, например. Она возвращает результат (string): NodeFilter(Node.parent)+<фильтр для узла Node>. Обычная рекурсия. Но для этого нужно, конечно, некоторые штучки предварительно проделать. Т.е. при построении дерева определить что это за узел (кто он - city или ЗАО или ...), а потом уже эту информацию использовать в своей рекурсивной функции.

Честно говоря, сомневаюсь, что я сам бы во всем этом разобрался по своим словам. Но если надо - спрашивай дальше.


 
kalliopiy   (2002-07-22 16:38) [5]

Короче, я чего-то понял, чего-то - нет.

Откуда у тебя это дерево берется, вообще? Из таблицы? Или все забито в программе?

Ситуация такая - проще всего процесс фильтрации автоматизировать используя рекурсию. Пишешь, например, функцию NodeFilter с параметром Node - выбранный узел дерева, например. Она возвращает результат (string): NodeFilter(Node.parent)+<фильтр для узла Node>. Обычная рекурсия. Но для этого нужно, конечно, некоторые штучки предварительно проделать. Т.е. при построении дерева определить что это за узел (кто он - city или ЗАО или ...), а потом уже эту информацию использовать в своей рекурсивной функции.

Честно говоря, сомневаюсь, что я сам бы во всем этом разобрался по своим словам. Но если надо - спрашивай дальше.


 
kaif   (2002-07-22 18:27) [6]

Собственно о деревьях. Я не против отображения в виде дерева. Но хранение разнородных объектов (городов и чуваков) в одной таблице в виде дерева имхо не есть гут. Разумеется, если начальство хочет добавлять новые сущности, например, превратить чувака в узел и добавить к нему дочерние пункты нового типа (его номера телефонов) или создать в каждом городе новую дачернюю сущность, например, "Бани", а в них чуваков заводить, то для простоты придется все запихать в дерево (в 1 таблицу). Однако потом я не представляю запросов типа
SELECT всех чуваков WHERE которые в Москве, но не в Бане №5.
По-моему, лучше всего все это хранить в нормальных отдельных таблицах, а отображать в дереве. А начальство много чего хочет.
Однако вряд ли оно хочет потом в конце концов зайти в тупик и все ломать, когда данные уже введены.
Вначале в 1С деревья тоже всем страшно нравятся (по наивности). А потом, когда у каждого объекта одни и те же дочерние папки приходится заводить и так по 100 раз - некоторые пользователи уже готовы повеситься.
Хотя - дело хозяйское.


 
sokoloff   (2002-07-22 18:41) [7]


> Короче, я чего-то понял, чего-то - нет.

:)

> Откуда у тебя это дерево берется, вообще? Из таблицы? Или
> все забито в программе?


В базе есть таблица FLT с полями:
ID - ключ,
PARENT - родитель,
NAME - то что отображается в дереве (TTreeNode.Text)
FLT_QUERY - собственно кусочек SQL текста.
Эта таблица отображается в виде дерева, по класической схеме, здесь я ничего изобретать не стал.

Есть одина первая запись с ID=0, PARENT=0 - это будет коренем дерева ее удаление запрещено. Далее пользователи могут сами добавлять записи в эту таблицу, добавленная запись получает PARENT равный ID записи на которой стоит пользователь, т.е. становиться ребенком активного узла.
При удалении узла(и записи из таблицы соответственно) удаляются все дочерние узлы.
При перемещении узла в таблице меняется значение PARENT на ID приемника.


> Ситуация такая - проще всего процесс фильтрации автоматизировать
> используя рекурсию. Пишешь, например, функцию NodeFilter
> с параметром Node - выбранный узел дерева, например. Она
> возвращает результат (string): NodeFilter(Node.parent)+<фильтр
> для узла Node>. Обычная рекурсия. Но для этого нужно, конечно,
> некоторые штучки предварительно проделать. Т.е. при построении
> дерева определить что это за узел (кто он - city или ЗАО
> или ...),

все узлы одинаковы, все могут содержать фильтр/фильтры по любым полям.

В базе у меня есть процедура (взято из статьи на ib.demo)

CREATE PROCEDURE GETPARENTS (
ID INTEGER)
RETURNS (
DID INTEGER,
OID INTEGER,
NAME VARCHAR(125),
QUERY VARCHAR(1024))
AS
begin
/* Procedure Text */
WHILE (ID > 0) DO
BEGIN
SELECT O.id, O.PARENT, O.NAME, O.flt_query
FROM FLT O
WHERE O.id = :ID
INTO :DID, :OID, :NAME, :QUERY;
ID=OID;
SUSPEND;
END
end

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

> Честно говоря, сомневаюсь, что я сам бы во всем этом разобрался
> по своим словам. Но если надо - спрашивай дальше

Вообще у меня более менее работает, осталось сделать рекурсивное удаление узлов, но это не сложно. Вопрос был по поводу статьи, как я понимаю никто такой статьи не видел, т.е. мой друг чой-то перепутал. Но если у кого есть хорошие мысли/предложения/наработки с удовольствием приннимаются :)


 
kaif   (2002-07-22 19:05) [8]

Я кажется врубился. Речь идет о том, чтобы, выбирая в дереве пункты, управлять на ходу текстом SQL-запроса? А результат запроса высвечивать справа от дерева?


 
sokoloff   (2002-07-22 19:07) [9]


> Собственно о деревьях. Я не против отображения в виде дерева.
> Но хранение разнородных объектов (городов и чуваков) в одной
> таблице в виде дерева имхо не есть гут.

Неееееееееееее... :)
Данные храняться в нормализованых таблицах, где для чуваков и городов свои поля(на самом деле там связанные таблицы, но для простоты примеров считаем что в таблице MAIN есть текстовые поля CITY, и NAME), в таблице фильтров храняться только тексты SQL запросов, т.е. стринговые переменные со значениями как я писал раньше, программа идет от корня к выбранному узлу и складывает общий запрос из кусочков.
А каждый узел в дереве фильтров может стать веткой если вводиться уточняющий фильтр.
Например, были фильтры по городам - "city like "Москва"", "city like "Питер"", каждый из них пока лист дерева. В базу добавились записи(в другую, нормализованную таблицу), количество записей возвращаемых фильтром Москва стало большым, в узел Москва добавляем новые фильтры скажем по именам людей "name like "А%"","name like "Б%"" и.т.д., узел москва стал веткой, а у него появились дети-листья А,Б,В...
Видно я криво объясняю.


...
> А потом, когда у каждого объекта одни и те же дочерние папки
> приходится заводить и так по 100 раз - некоторые пользователи
> уже готовы повеситься.

Тут не известно как меньше писать придется, если расматривать предыдущий пример с Москвой и А,Б,В, то в моем варианте надо один раз написать "city like "Москва"" и дальше только "name like "БУКВА%"", а с обычными фильтрами постоянно писать "CITY LIKE "Москва"...", тож радости мало :), но и тут, и тут есть свои недостатки.


 
sokoloff   (2002-07-22 19:09) [10]


> Я кажется врубился. Речь идет о том, чтобы, выбирая в дереве
> пункты, управлять на ходу текстом SQL-запроса? А результат
> запроса высвечивать справа от дерева?

Да!


 
kaif   (2002-07-22 19:28) [11]

Идея мне нравится. А в чем видится основная проблема? Я чувствую, что тут почти все можно автоматизировать и шикарная вещь получится. Сам придумал?


 
sokoloff   (2002-07-22 19:35) [12]


> Идея мне нравится. А в чем видится основная проблема? Я
> чувствую, что тут почти все можно автоматизировать и шикарная
> вещь получится. Сам придумал?

Сам.

Вот подскажите, я с помощью процедуры GETPARENTS получаю из таблицы фильтров все записи от корня до выделенного узла, т.е. вывод может быть таким:

SELECT DID, QUERY FROM GETPARENTS ID_выбранного_узла

DID | QUERY |
----+------------------------------------------+
0 | city like "Москва" and name like "Pupkin"|
1 | name like "A%" and user like "Ivanov" |

и уже на стороне клиента формирую запрос - where city like "Москва" and name like "A% and user like "Ivanov". Некоторые части запроса переопределяются, плюс необходимо выделять поля котрые были переопределены (name), которые определены в родительских узлах (city) и которые определены только в выделенном узле (user).
Можно написать встроенную процедуру для вывода таких данных(я не очень силен во встроенных процедурах IB)?
И вообще где лучше парсерить данные в моем случае на сервере или на клиенте?
С одной стороны я перекачиваю лишние данные, с другой стороны
логика довольно навароченная и боюсь нагрузка на сервер будет довольно большой.


 
kaif   (2002-07-22 20:59) [13]

Я пишу приложение, которое в очень большой степени занимается автоматическим формированием SQL-запросов и нахожу, что все, что можно делать для этого на клиенте, следует делать именно на клиенте. Так как это и отлаживать проще и изменять в дальнейшем. На сервере нужно создать минимальную структуру, которая все это будет обслуживать и в которой можно быть уверенным, что менять ее никогда не придется.


 
fnatali   (2002-07-23 06:35) [14]

Не знаю, та статья или нет, которая тебя интересует http://sdm.viptop.ru/articles/sqltrees.html
И ещё на королевстве Делфи была какая-то статья, но точно не могу дать ссылку...


 
sokoloff   (2002-07-23 10:19) [15]


> Не знаю, та статья или нет, которая тебя интересует http://sdm.viptop.ru/articles/sqltrees.html

Эту статью я читал, но это просто о деревьях, а друг говорил именно о деревьях-фильтрах.

> И ещё на королевстве Делфи была какая-то статья, но точно
> не могу дать ссылку...

Посмотрим.

В связи с некоторым интересом возник вопрос, надо делать компоненту дерево-фильтров, будет она интересна народу?



 
Arhelon   (2002-07-23 10:57) [16]

Не знаю устроит Тя или нет но я в таких случаях юзаю не дерево (TTreeView) а список (TListView) по разным причинам (если интересно потом опишу)
И получается довольно красиво (на мой взгляд) софтина один в один "Проводник" мона создовать разные объекты (папки и разного рода элементы) Темболе что у Тя есть структура
ID - ключ,
PARENT - родитель,
Нет никаких траблов на onClick повесить select * from table where parent= id(Выбранного элемента) а там отображай как хотишь.
Есть небольшой трабл с адресной строкой и бутонами вперёд назад но ежли хотишь красивости то енто не трабл (просто аккуратно и всё) правда я есчё тип добавляю чёб объекты разными могли быть.
Вот вообчем-то и всё.


 
Arhelon   (2002-07-23 10:59) [17]

По поводу "надо делать компоненту дерево-фильтров" оно ужо есть :)
Правда в связи со скоропостижной смертью (на выходных) моего винта
не могу кинуть ссылку на ентот компонент.


 
sokoloff   (2002-07-23 11:20) [18]


> Не знаю устроит Тя или нет но я в таких случаях юзаю не
> дерево (TTreeView) а список (TListView) ...

Это IMHO никакой разницы, меняется только отображение, как в проводнике или как в оутлуке, суть остается одна.


> По поводу "надо делать компоненту дерево-фильтров" оно ужо
> есть :)
> Правда в связи со скоропостижной смертью (на выходных) моего
> винта
> не могу кинуть ссылку на ентот компонент.

Это было именно FltersTree? DBTree у меня и так есть.
Компонента была самописная или сторонняя?


 
Arhelon   (2002-07-23 12:13) [19]

Оно было сторонее TDBTreeView называлось :)
Если Тя интересует то завтре могу положить исходник моей реализации листа. Правда там нет твоих вильтров но (имхо) не суть важно.
З.Ы. Завтре потому как дома оно у мня а на работе винт здох


 
Arhelon   (2002-07-23 12:22) [20]

А по поводу
>Это IMHO никакой разницы, меняется только отображение, как в
>проводнике или как в оутлуке, суть остается одна.
Енто не совсем так потому как в дереве (для красоты (ну принято так)) нуна отображать все ветки сразу а енто есть процедура не очень быстрая как и вставка в ветки, а листе енто не надо и работает оно поутому быстро.
(Я когда начинал дельфить ещё на вторых писал базу на основе дерева, но потом отказался от оного напрочь, отчасти из-за отказа мастдай от них в проводнике(по умолчанию))
т.е. моно реализовать все стандартные фичи так что юзер не отличит твой софт от проводника а посему не нужно отдельное обучение юзера на твою прогу.
З.Ы. Енто всё конечно глубоко моё личное мнение


 
dimis   (2002-07-23 12:52) [21]


у класса TtreeNode есть свойство Data
в нем можно хранить sql запрос

TtreeData=class
sql:string;
constructor Create(aSQL:string);
end;
constructor TtreeData.Create;
begin
sql:=ASQL;
end;

var
Node:TTreeNode;
begin
....
Node:=TtreeView1.Items.AddObject("Москва",TTreeData.Create("city like ""Москва""");
Node:=TtreeView1.Items.AddChildObject(Node,"Ивановы",TTreeData.Create("namе like ""Ивановы""");
...
end;

Составляем запрос
procedure TMainForm.TTreeView1Change(Sender: TObject; Node: TTreeNode);
var
n:TTreeNode;
begin
Query1.Close;
Query1.Sql.Clear;
Query1.SQl.Add(select * from table1 where");
n:=Node;
Query1.SQl.Add(TTreeData(n.Data).sql);
while n<>nil do
begin
Query1.SQl.Add("and "+TTreeData(n.Data).sql);
n;=n.Parent
end;
Query1.Open;
end;


 
Proton   (2002-07-23 13:58) [22]

у меня задачка была когдато
требовалось оргпнизовать поиск
галочку напроив фамилии поставл параметр учтитывается нет - не учитыватся
так же с именем
и еще 20 полей
понимаеш ?
так вот пришлось генерировать запрос на основе этого
если надо поделюсь сыром - пиши на мыло


 
sokoloff   (2002-07-23 15:01) [23]

2 Arhelon

TDBTreeView это чей компонент, кто автор?
Я использую RADBTreeView ( http://ralib.hotbox.ru), но уже нашел глюк с удалением узлов, уже исправил, но боюсь что могут быть еще. Поэтому если компонента проверена в деле и работает стабильно, то с удавольствием посмотрю.

2 Proton

насколько я понял это не совсем то, это типа "Дерева аналитических признаков" http://www.delphikingdom.com/helloworld/dbtreeview.htm#02
Вещь интересная, но не подходит для фильтрации по текстовому полю, а у меня обязательно должны быть такие фильтры.




 
kaif   (2002-07-23 15:35) [24]

Решение о том, писать компонент или нет, не должно зависеть от его нужности публике, а только от нужности твоей задаче. Если у тебя множество окон (хотя бы 2 окна), где это нужно задействовать, советую написать компонент. Если же одно окно - тогда, может, не стоит пока. В любом случае - сам решай. Потребность в компоненте у публики зависит от его(компонента) изящества, но не наоборот.



Страницы: 1 вся ветка

Форум: "Базы";
Текущий архив: 2002.08.12;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.54 MB
Время: 0.006 c
8-35387
stup
2002-04-03 17:48
2002.08.12
Помогите сделать хороший интерфейс при использ. DelphiX


3-35200
sokoloff
2002-07-22 13:07
2002.08.12
Фильтры в виде дерева


4-35503
Yuri Btr
2002-05-31 15:01
2002.08.12
Убить приложение зная только его исполняемый файл


14-35453
Oleg_Gashev
2002-07-16 22:34
2002.08.12
Как нас учат -2


4-35522
MIRALATTE
2002-06-07 11:31
2002.08.12
Как можно добавить иконку в системный трей и обрабатывать событ.





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский