|
|
Регистрация | << Правила форума >> | FAQ | Пользователи | Календарь | Поиск | Сообщения за сегодня | Все разделы прочитаны |
|
Опции темы | Поиск в этой теме | Опции просмотра |
#1
|
||||
|
||||
Хитрое соединение данных двух таблиц
Приветствую вас, форумчане!
В процессе разработки очередной БД у меня возник вопрос, для меня как для новичка достаточно сложный, на уровне нерешаемого. Речь идёт об одной возможности, которую очень хотелось бы реализовать, и состоит она в "хитром" соединении данных двух таблиц, находящихся в отношении "Мастер-Деталь". Есть главная таблица "Компоненты". К ней как детальная подключена таблица "Параметры" (т.е. каждому компоненту может соответствовать произвольный список параметров и конкретных значений этих параметров). Для пущей ясности, как это выглядит, скрины прилагаются. И вот вопрос - как бы сделать так, чтоб в одном гриде отобразить объединённые данные из этих двух таблиц. А именно: в новом "суммирующем" гриде к существующим полям мастер-грида добавить новые поля с заголовками в виде имён параметров (а значения параметров из детальной таблицы станут значениями в добавленных полях "суммирующего" грида). Т.е. если посмотреть визуально, то детальный грид как бы разворачивается на 90 градусов и приклеивается к правой части мастер грида (см. прилагаемый скрин). Повторюсь, возможность очень желательная, но как это сделать (и можно ли вообще), я слабо представляю. Т.к. число записей в детальной таблице переменное, надо или динамически создавать поля для датасета, который будет хранить совмещённые данные, а потом ещё и для грида, отображающего результирующее безобразие, или использовать какой-то хитрый StringGrid. Почему "хитрый" - потому что непременным условием задачи является возможность сортировки данных по столбцам, и я пока не знаю, существует ли такая возможность даже в продвинутых гридах (например, от TMS). Кроме того (и этот момент очень существенен!), мне нужна числовая сортировка по полям значений параметров, а не текстовая, каковая будет при использовании StringGrid-а, так что вариант с "суммирующим" датасетом и гридом предпочтительнее. Результирующий датасет (если это будет датасет) может быть Read-Only, тут главное - это возможность числовой сортировки по "приклеенным" столбцам. Последний раз редактировалось Guaho, 17.02.2019 в 14:20. |
#2
|
|||
|
|||
Вообще, такое можно сделать.
Вот только поддерживает ли это твой сервер или компонент. Называется это Pivot Table, когда ты "разворачиваешь" таблицу. Можно все написать руками, но работать будет не быстро (в зависимости от размера данных). По сути, тебе потребуется TMemTable или что-то подобное. Сначала получаем список всех возможных параметров (из detail). Потом в TMemTable создаем соотв. поля + поля master'а. Далее идем по датасетам и заполняем получившийся TMemTable. Ну, и как ты понимаешь, такая таблица будет не редактируемой. В смысле, если ты захочешь сохранить изменения, то тебе потребуется написать соотв. код для переноса изменений из этой таблицы в БД (обычно это делается через OnAfterPost). |
Этот пользователь сказал Спасибо lmikle за это полезное сообщение: | ||
Guaho (20.02.2019)
|
#3
|
|||
|
|||
смотря какой компонент какого разработчика использует автор, у DevExpress есть демка я думаю она подойдёт для реализации задумки автора, скрины в приложении, справа вверху основная инфа, жмём на плюсег слева от основной инфы, появляется доп инфа разделенная на закладки
|
Этот пользователь сказал Спасибо brookhut за это полезное сообщение: | ||
Guaho (20.02.2019)
|
#4
|
||||
|
||||
Спасибо всем откликнувшимся!
Использую механизм Absolute Database, а для визуализации - библиотеку EhLib. И вот в ней как раз есть и TMemTable, и PivotGrid (я об этом компоненте и подумал, размышляя о задаче, и даже пытался понять, что он делает, но с первого раза не вкурил). А MemTable, как оказалось, поддерживает режим "stand-alone" (в котором работает как ни с чем не связанный массив данных). Для моей задачи как раз то что надо. Ну и инфу по PivotGrid придётся покурить тщательнее ))) З.Ы. Вариант, чтоб что-то раскрывалось по нажатию "плюсиков", мне не подходит. Это, кстати, в EhLib-е есть. А тут нужно в итоге получить самый простой (с виду) грид с совмещёнными полями и данными, чтоб никаких лишних кликов, а только сортировка с целью сравнения значений параметров. |
#5
|
|||
|
|||
Насколько я понял, необходимо таблицу заполнить так:
-мастер-данные выводятся как есть -детализирующие-данные разворачиваются в ту же строку справа. Для этого на клиенте создаём sql-запрос вручную. Нам потребуется два TQuery. Первый, назовём его QueryParams, который покажет нам сколько дополнительных столбцов нам потребуется. А во втором, назовём его QueryRes, мы построим sql-запрос, выполним его и отобразим полученные данные в DbGrid. Допустим у нас есть три таблицы c колонками: TbMaster (TbMaster_id, Name); TbDetail (TbDetail_id, Name); TbMstDetail (TbMstDetail_id, TbMaster_id, TbDetail_id, Value). И нам требуется вывести в строку значения параметров Value. Другими словами, две таблицы связаны с помощью третьей связью многие ко многим, в которой и содержатся значения параметра. Текст первого запроса QueryParams очищаем. QueryParams.SQL.Clear Вводим в него sql-запрос наподобии следующего: Код:
select distinct md.TbDetail_id from TbMaster m left join TbMstDetail md on m.TbMaster_id = md.TbMaster_id Мы получим перечень наших дополнительных столбцов, которые используются в нашей выборке по таблице TbMaster. Количество столбцов возьмём из QueryParams.RecordCount. Кроме того, получим id наших параметров. Теперь сформируем наш второй запрос QueryRes. В нём нам нужно получить sql-запрос наподобии следующего (например, для трёх параметров c id: 10, 11, 12): Код:
select m.num, m.name, m.description ,md1.Value prm1 ,md2.Value prm2 ,md3.Value prm3 from TbMaster m left join TbMstDetail md1 on m.TbMaster_id = md1.TbMaster_id and md1.TbDetail_id = 10 left join TbMstDetail md2 on m.TbMaster_id = md2.TbMaster_id and md2.TbDetail_id = 11 left join TbMstDetail md3 on m.TbMaster_id = md3.TbMaster_id and md3.TbDetail_id = 12 order by m.Name Добавляем в него "обязательную часть", т.е. то что было первоначально на картинке "мастер" в первом посте: Код:
QueryRes.Sql.Add('select m.num, m.name, m.description'); Код:
QueryRes.Sql.Add(' , md'+IntToStr(i) +'.Value prm'+IntToStr(i)); Код:
' ,md1.Value prm1' Код:
QueryRes.Sql.Add(' from TbMaster m'); Код:
QueryRes.Sql.Add(' left join TbMstDetail md'+IntToStr(i) +' on m.TbMaster_id = md'+IntToStr(i)+'.TbMaster_id and md'+IntToStr(i)+'.TbDetail_id = '+IntToStr(n1)); Код:
' left join TbMstDetail md1 on m.TbMaster_id = md1.TbMaster_id and md1.TbDetail_id = 10' Код:
QueryRes.Sql.Add(' order by m.Name'); Переименовываем (или добавляем) столбцы в DbGrid. Последний раз редактировалось DenSarych, 27.02.2019 в 08:23. |
Этот пользователь сказал Спасибо DenSarych за это полезное сообщение: | ||
Guaho (01.03.2019)
|
#6
|
||||
|
||||
Спасибо! Благодарю за столь подробное разъяснение!
Метод с виду подходящий, хотя и несколько сложноватый, как для меня, но тем не менее смысл понятен. Однако мне представляется более оптимальным другой путь, более простой, с использованием компонента TMemTableEh из библиотеки EhLib. Указанный компонент может работать как ни с чем не связанный набор данных. Этот внутренний датасет нужно сначала создать, определив его поля, причём определить их можно в Run Time, что мне и нужно. А далее с ним можно работать как душе угодно (мне будут нужны только функции сортировки и поиска). И вот в чём состоит тогда упрощённый метод: 1. Пробегаемся по всем записям мастер-датасета, из которых нужно составить сводную таблицу, и для каждой записи просматриваем список параметров, а из него формируем (занося в отдельную таблицу) все просмотренные параметры (если какой-то уже есть, его пропускаем). В итоге получаем датасет со списком имён всех параметров, которые должны быть показаны в сводной таблице. 2. Создаём внутренний датасет TMemTableEh, динамически создавая сначала поля мастер-таблицы, а затем поля имён параметров (из только что созданного датасета со списком имён всех параметров). 3. Заполняем датасет данными: перебираем все записи в мастере, формируя "мастерную" часть данных, а внутри этого цикла - перебираем детальные данные (т.е. параметры, привязанные к текущей мастер-записи), и из них формируем список значений для "детальной" части совмещённого датасета. Время выполнения этой операции тут некритично, т.к. эта функция не будет основной, и, если надо, пользователь подождёт 1 - 2 секунды (хотя уверен, что всё выполнится намного быстрее, да и число записей не предполагается слишком большим). Для визуализации надо будет динамически создавать столбцы в DBGridEh; если у меня это не получится, можно нагородить кучу полей с запасом. Последний раз редактировалось Guaho, 01.03.2019 в 21:35. |
#7
|
||||
|
||||
Мне самому трудно поверить, что таки получилось сделать эту функцию, реализация которой ещё недавно казалась почти невозможной, а в итоге оказалась ещё проще, чем можно было представить.
В общем, попробовал я работать с компонентом TMemTable от EhLib. Компонент оказался ужасно глючным: создание полей даже в Disign-time часто приводило к крэшу (с потерей части ранее записанных данных), в итоге на простейшую операцию ушла куча времени. Кроме того, часть созданных строковых полей оказалась "прОклятой" - туда не удавалась записать более 20 символов, хотя длина поля была указана = 250, а пересоздание поля не помогало. Динамически создать поле в датасете можно было без проблем (руководствуясь кодом из примеров), только подключить его к гриду - ну никак (может, в реальности ни фига и не создавалось). Ну и наконец, оказалось, что поля типа "Float" этот датасет не знает, только Integer или Variant. Я попробовал второе, в итоге получил сортировку по числовым полям как по текстовым... Полная ерунда в общем! И тут меня осенило: а чего я зацепился за этот компонент? А что если поля датасета создать статически? Ведь в практической работе, для этой специфики, число параметров одного компонента редко превышает 10, ну 15 - потолок. И вот я сделал 20 статических полей для параметров (с железным запасом, а если вдруг что не влезет - отображаться не будет) в стандартном DBQuery (точнее, ABSDBQuery). Все эти поля - типа Float. Процедура сортировки у меня своя, отработанная уже. Остальное оказалось делом техники - заполнение заголовков (и их хинтов) грида, заполнение данными и значениями параметров. В прикреплённом изображении - вид того, что получилось (включена сортировка по одному из столбцов параметров + виден хинт на заголовок одного из столбцов). Последний раз редактировалось Guaho, 05.03.2019 в 21:38. |