Модераторы: Rodman

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Использование гетов при поиске информации, Как лучше реализовать? 
V
    Опции темы
Rodeon
  Дата 7.12.2016, 02:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 112
Регистрация: 28.8.2008
Где: Россия, Ухта

Репутация: нет
Всего: нет



Здравствуйте.
Не каждая контора может позволить себе систему электронного документооборота и контроля исполнения поручений, поэтому для своего удобства делаю небольшую прогу по отслеживанию приказов, распоряжений и т.д.
Ситуация следующая:
база данных на MySQL
В первой таблице хранятся тэги, которые подгружаются в CheckListBox, где выбираются (как например: 2016, подписанные, актуальные, 2015 и т.д.)

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

Для поиска нужного документа, выбираю необходимые теги из CheckListBox, запрос формирую подобным образом:
Код

procedure TMainForm.sButton1Click(Sender: TObject);
var
  I, checked : Integer;
  Addon: Boolean;
begin
  checked:= 0;
  Addon:= False;
  for i := 0 to sCheckListBox1.Count - 1 do if sCheckListBox1.Checked[i] then Inc(checked);
  Rabotniki_IBQuery.SQL.Clear;
  Rabotniki_IBQuery.SQL.Add('select * from INFORMATION');
  If checked>0 then
  Begin
    for i := 0 to sCheckListBox1.Items.Count - 1 do
    begin
      if sCheckListBox1.Checked[i]=true then
      Case Addon of
      False: Begin
               Rabotniki_IBQuery.SQL.Add(' WHERE TEGS LIKE ''%'+sCheckListBox1.Items.Strings[i]+'%''');
               Addon:=True;
             End;
      True: Rabotniki_IBQuery.SQL.Add(' OR TEGS LIKE ''%'+sCheckListBox1.Items.Strings[i]+'%''');
      End;
    End;
  End;
  Rabotniki_IBQuery.Open;
end;


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


В шапке опечатка, поздно заметил.  smile 

Это сообщение отредактировал(а) Rodeon - 7.12.2016, 02:06
PM MAIL   Вверх
Garmahis
Дата 7.12.2016, 09:15 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 254
Регистрация: 23.12.2004

Репутация: нет
Всего: 4



Правильнее сделать третью таблицу для связи тегов с документами. В таблице достаточно 2 полей: IDDocument и IDTeg. При таком подходе будет гораздо проще делать выборку по тегам.
PM   Вверх
Rodeon
Дата 7.12.2016, 09:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 112
Регистрация: 28.8.2008
Где: Россия, Ухта

Репутация: нет
Всего: нет



Хм... немного не могу понять как оно будет работать?!
У меня и так отдельная таблица с тэгами, в ней всего 1 поле - Char, в каждую строку занесен один тэг. При выводе приказов все поля подгружаются в sCheckListBox, и путем выбора нужных тегов в списке остаются необходимые приказы. Т.е. выбрал например тэги: "2016", "назначения", "ТБ"
выведет все приказы (другая таблица), у которых в строке Tags (длиной 250) встречаются все вышеуказанные слова.

Правильно ли я вас понял, для каждого документа свои тэги будут или как? не пойму предложенной вами реализации.
PM MAIL   Вверх
Vas
Дата 7.12.2016, 21:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 825
Регистрация: 29.6.2005
Где: Stavropol region

Репутация: 23
Всего: 28



Документы
ИД
Название


Теги
Ид
Название

Таблица для связи многие ко многим
ИД_Документа
Ид_Тега


--------------------
И опыт, сын ошибок трудных, И гений, парадоксов друг, И случай, бог изобретатель. ... (А.С. Пушкин)
PM MAIL   Вверх
Rodeon
Дата 8.12.2016, 11:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 112
Регистрация: 28.8.2008
Где: Россия, Ухта

Репутация: нет
Всего: нет



Дошло наконец, что вы хотели сказать.
Все сделал, проверил, запрос заботает в таком виде:
Код

SELECT DISTINCT name FROM document_tag
LEFT JOIN document ON document.id = document_tag.id_document
LEFT JOIN tags on tags.id = document_tag.id_tag


Только это полдела, так как он выводить все приказы, в которых есть хотя бы 1 тег.
У меня в таблице:
Цитата(Vas @  7.12.2016,  21:04 Найти цитируемый пост)
Таблица для связи многие ко многим
ИД_Документа
Ид_Тега 

каждому документу присваивается несколько тегов:
ИД_Документа     Ид_Тега
1                              1
1                               2
1                               6
2                               1
2                                6
2                               4
и т.д.

Осталось разобраться как сюда прикруть выборку тегов, т.е. выводить только те приказы, которые выбраны в sCheckListBox. Может не через sCheckListBox, но хотелось бы визуализировать выбор.

Добавлено @ 12:05
Все, так вобще хорошо:
в конце добавляет ID тегов, и выводит только те приказы у которых они есть:
Код

SELECT DISTINCT name,document.id,Tags.id FROM document_tag
LEFT JOIN document ON document.id = document_tag.id_document
LEFT JOIN tags on tags.id = document_tag.id_tag where tags.ID in (№раз,№два и т.д. через запятую)

Таким образом избавился от цикла, все делается в 1 запрос. Всем спасибо.

Это сообщение отредактировал(а) Rodeon - 8.12.2016, 12:07
PM MAIL   Вверх
Vas
Дата 8.12.2016, 13:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 825
Регистрация: 29.6.2005
Где: Stavropol region

Репутация: 23
Всего: 28



Цитата(Rodeon @  8.12.2016,  11:51 Найти цитируемый пост)
Таким образом избавился от цикла, все делается в 1 запрос. Всем спасибо.

 
Все верно! 

Это сообщение отредактировал(а) Vas - 8.12.2016, 13:27


--------------------
И опыт, сын ошибок трудных, И гений, парадоксов друг, И случай, бог изобретатель. ... (А.С. Пушкин)
PM MAIL   Вверх
Rodeon
Дата 9.12.2016, 01:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 112
Регистрация: 28.8.2008
Где: Россия, Ухта

Репутация: нет
Всего: нет



В виду того, что не нашел бесплатный компонент DbCheckListBox, пришлось по списку выбранных в sCheckListBox-е пунктов пробегать циклом.

Код

procedure TMainForm.sButton1Click(Sender: TObject);
var
  i: Integer;
  s: String;
begin
  i := 0;
  s:='';
  while i < sCheckListBox1.Items.Count do
  Begin
    if (sCheckListBox1.Checked[i]) AND (S<>'') then s:=s+', '+sCheckListBox1.Items.Strings[i];
    if (sCheckListBox1.Checked[i]) AND (S='') then s:=sCheckListBox1.Items.Strings[i];
    Inc(i);
  End;
  Rabotniki_IBQuery.SQL.Clear;
  Rabotniki_IBQuery.SQL.Add('SELECT DISTINCT name,document.id,Tags.id FROM document_tag');
  Rabotniki_IBQuery.SQL.Add(' LEFT JOIN document ON document.id = document_tag.id_document');
  If s<>'' then Rabotniki_IBQuery.SQL.Add(' LEFT JOIN tags on tags.id = document_tag.id_tag where tags.ID in ('+s+')')
  Else Rabotniki_IBQuery.SQL.Add(' LEFT JOIN tags on tags.id = document_tag.id_tag');
  Rabotniki_IBQuery.Open;
end;


Ну и чтобы галочку поставить, что вопрос решенный.  smile 

Это сообщение отредактировал(а) Rodeon - 9.12.2016, 01:17
PM MAIL   Вверх
Garmahis
Дата 9.12.2016, 09:56 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 254
Регистрация: 23.12.2004

Репутация: нет
Всего: 4



Правильнее работать через Object.
Грузить в sCheckListBox1 записи в цикле по таблице тегов селектишь id и teg и пихаешь вот так:
Код

sCheckListBox1 .Items.AddObject(Rabotniki_IBQuery.FieldByName('Tag').AsString, Pointer(Rabotniki_IBQuery.FieldByName('ID').AsInteger));

Соотвественно id тегов получаешь примерно так:
Код

for i := 0 to sCheckListBox1 .Items.Count - 1 do
  if sCheckListBox1.Checked[i] then
    s := s + IntToStr(Integer(sCheckListBox1.Items.Objects[i]))  + ', ';

PM   Вверх
mailworker6
Дата 10.12.2016, 22:37 (ссылка)    |    (голосов: 0) Загрузка ... Загрузка ... Быстрая цитата Цитата


Unregistered











http://tuvaonline.ru/banja_ydovolctvie_i_polza.dhtml

если вас интересует турецкая баня в Москве или хамам,
то вам тем более нужно в «Таёжные бани», где вашему желанию непременно найдётся реализация.
Интересной особенностью турецких бань всегда была было повышенное внимание к внутренней архитектуре и убранству,
которое должно сразу настроить посетителя на то, что он вступает в храм,
храм омовения и чистоты не только тела.

Этот ответ добавлен с нового Винграда - http://vingrad.com
  Вверх
mailworker6
Дата 10.12.2016, 22:37 (ссылка)    |    (голосов: 0) Загрузка ... Загрузка ... Быстрая цитата Цитата


Unregistered











http://tuvaonline.ru/banja_ydovolctvie_i_polza.dhtml

если вас интересует турецкая баня в Москве или хамам,
то вам тем более нужно в «Таёжные бани», где вашему желанию непременно найдётся реализация.
Интересной особенностью турецких бань всегда была было повышенное внимание к внутренней архитектуре и убранству,
которое должно сразу настроить посетителя на то, что он вступает в храм,
храм омовения и чистоты не только тела.

Этот ответ добавлен с нового Винграда - http://vingrad.com
  Вверх
mailworker6
Дата 10.12.2016, 22:37 (ссылка)    |    (голосов: 0) Загрузка ... Загрузка ... Быстрая цитата Цитата


Unregistered











http://tuvaonline.ru/banja_ydovolctvie_i_polza.dhtml

если вас интересует турецкая баня в Москве или хамам,
то вам тем более нужно в «Таёжные бани», где вашему желанию непременно найдётся реализация.
Интересной особенностью турецких бань всегда была было повышенное внимание к внутренней архитектуре и убранству,
которое должно сразу настроить посетителя на то, что он вступает в храм,
храм омовения и чистоты не только тела.

Этот ответ добавлен с нового Винграда - http://vingrad.com
  Вверх
mailworker6
Дата 10.12.2016, 22:37 (ссылка)    |    (голосов: 0) Загрузка ... Загрузка ... Быстрая цитата Цитата


Unregistered











http://tuvaonline.ru/banja_ydovolctvie_i_polza.dhtml

если вас интересует турецкая баня в Москве или хамам,
то вам тем более нужно в «Таёжные бани», где вашему желанию непременно найдётся реализация.
Интересной особенностью турецких бань всегда была было повышенное внимание к внутренней архитектуре и убранству,
которое должно сразу настроить посетителя на то, что он вступает в храм,
храм омовения и чистоты не только тела.

Этот ответ добавлен с нового Винграда - http://vingrad.com
  Вверх
mailworker6
Дата 10.12.2016, 22:37 (ссылка)    |    (голосов: 0) Загрузка ... Загрузка ... Быстрая цитата Цитата


Unregistered











http://tuvaonline.ru/banja_ydovolctvie_i_polza.dhtml

если вас интересует турецкая баня в Москве или хамам,
то вам тем более нужно в «Таёжные бани», где вашему желанию непременно найдётся реализация.
Интересной особенностью турецких бань всегда была было повышенное внимание к внутренней архитектуре и убранству,
которое должно сразу настроить посетителя на то, что он вступает в храм,
храм омовения и чистоты не только тела.

Этот ответ добавлен с нового Винграда - http://vingrad.com
  Вверх
Rodeon
Дата 11.12.2016, 04:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 112
Регистрация: 28.8.2008
Где: Россия, Ухта

Репутация: нет
Всего: нет



Цитата(Garmahis @ 9.12.2016,  09:56)
Правильнее работать через Object.
Грузить в sCheckListBox1 записи в цикле по таблице тегов селектишь id и teg и пихаешь вот так:
Код

sCheckListBox1 .Items.AddObject(Rabotniki_IBQuery.FieldByName('Tag').AsString, Pointer(Rabotniki_IBQuery.FieldByName('ID').AsInteger));

Соотвественно id тегов получаешь примерно так:
Код

for i := 0 to sCheckListBox1 .Items.Count - 1 do
  if sCheckListBox1.Checked[i] then
    s := s + IntToStr(Integer(sCheckListBox1.Items.Objects[i]))  + ', ';

Огромное спасибо. Только попробовал предложенный вами код, все очень и очень стало проще. Теперь все подставляемые значения хранятся в 1 таблице, у каждого свой уникальный (!!!) ID. При заполнении разных Combobox-ов и CheckBox-ов просто игнорирую null строки. Конечный SQL запрос для вывода в общую базу только вырос.

Чтобы наглядно было, мало ли кому понадобится (сделал маленькую базу на 3 колонки (ID, Type, Status):
Код

procedure TPrikazForm.FormCreate(Sender: TObject);
begin
  MainForm.IBQuery1.Active:=False;  {Активируем}
  MainForm.IBQuery1.SQL.Clear; {Очищаем строку запроса}
  MainForm.IBQuery1.SQL.Text:='Select * From PRIKAZ_TYPE'; {Выбираем всю таблицу}
  MainForm.IBQuery1.Active:=true;  {Активируем}
  If MainForm.IBQuery1.RecordCount>0 then  {Проверяем колличество записей}
  Begin
    sComboBox1.items.clear; {Очищаем комбобоксы}
    sComboBox2.items.clear;
    while not MainForm.IBQuery1.Eof do {В цикле заполняем sComboBox-ы}
    begin {Так как значений в поле "Type" у меня 3 штуки, а значений в поле "Status" у меня 5, причем специально сделал вразнобой, внес проверку на Null, все выбираемые параметры внес в 1 таблицу, так как все подгружается 1 циклом, вместо обращения к нескольким таблицам}
      If MainForm.IBQuery1.FieldByName('Type').Value<>Null then sComboBox1.Items.AddObject(MainForm.IBQuery1.FieldByName('Type').AsString, Pointer(MainForm.IBQuery1.FieldByName('ID').AsInteger));  {я наверное не правильно назову, но создаем в комбобоксе индексированные записи}
      If MainForm.IBQuery1.FieldByName('Status').Value<>Null then sComboBox2.Items.AddObject(MainForm.IBQuery1.FieldByName('Status').AsString, Pointer(MainForm.IBQuery1.FieldByName('ID').AsInteger));
      MainForm.IBQuery1.Next;
    End;
    sComboBox1.ItemIndex:=0; 
    sComboBox2.ItemIndex:=0;
  End;
end;


В итоге, после выбора в sComboBox-ах нужных строк, индексы в таблице, соответствующие именно выбранным строковым значениям получаем следующим образом, как пример:
Код

procedure TPrikazForm.sBitBtn3Click(Sender: TObject);
begin
  SMemo1.Lines.Add(IntToStr(Integer(sComboBox2.Items.Objects[sComboBox2.ItemIndex]))); {т.е. в Мемо вносится именно ID соответствующее в таблице, в то время как sComboBox2.ItemIndex будет иным.}
end;


PM MAIL   Вверх
Rodeon
Дата 11.12.2016, 10:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 112
Регистрация: 28.8.2008
Где: Россия, Ухта

Репутация: нет
Всего: нет



В таблице "PRIKAZ" основной ключ ID, но у него нет автоинкремента.
Чтобы не делать функцию получения максимального значения ключа, добавляем в саму таблицу для поля/ключа ID триггер:
Код

SET SQL DIALECT 3;
CREATE GENERATOR GEN_PRIKAZ_ID;
SET TERM ^ ;
CREATE OR ALTER TRIGGER PRIKAZ_BI FOR PRIKAZ
ACTIVE BEFORE INSERT POSITION 0
as
begin
  if (new.id is null) then
    new.id = gen_id(gen_prikaz_id,1);
end
^
SET TERM ; ^


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

Далее загружаю данные из дочернего окна PrikazForm:
Код

procedure TPrikazForm.sBitBtn3Click(Sender: TObject);
begin
  with MainForm.IBQuery1 do
  begin
    Active:=false;
    Sql.Clear;
    if not Transaction.InTransaction then Transaction.StartTransaction;
    ParamCheck := True;
    SQL.Add('INSERT INTO PRIKAZ (NOMER_PRIKAZ, DATA_PRIKAZ, STATUS_PRIKAZ, TIP_PRIKAZ, NAME_PRIKAZ, FILE_PRIKAZ, TAG, KOMMENT_PRIKAZ) VALUES (:NOMER_PRIKAZ, :DATA_PRIKAZ, :STATUS_PRIKAZ, :TIP_PRIKAZ, :NAME_PRIKAZ, :FILE_PRIKAZ, :TAG, :KOMMENT_PRIKAZ)');
    Params[0].AsString := sEdit1.Text;
    Params[1].AsDate := sDateEdit1.Date;
    Params[2].AsString := IntToStr(Integer(sComboBox2.Items.Objects[sComboBox2.ItemIndex]));
    Params[3].AsString := IntToStr(Integer(sComboBox1.Items.Objects[sComboBox1.ItemIndex]));
    Params[4].AsString := sMemo1.Lines.Text;
    Params[5].AsString := sEdit2.Text;
    Params[6].AsString := sCheckListBox1.Items.Text;
    Params[7].AsString := sMemo2.Lines.Text;
    try
      ExecSql;
      except
        on E: Exception Do
        Begin
        {Что-то пошло не так :-(}
        End;
      end;
    Close;
    if Transaction.InTransaction then Transaction.Commit;
  END;
end;


Все работает, проверил. Может знатоки посоветуют где можно улучшить, как ранее советовал Garmahis!?
PM MAIL   Вверх
Google
  Дата 21.11.2017, 02:09 (ссылка)  





  Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Delphi: Базы данных и репортинг"
Vit
Петрович

Запрещено:

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делиться вскрытыми компонентами


Обязательно указание:

1. Базы данных (Paradox, Oracle и т.п.)

2. Способа доступа (ADO, BDE и т.д.)


  • Литературу по Дельфи обсуждаем здесь
  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы по реализации алгоритмов рассматриваются здесь
  • 90% ответов на свои вопросы можно найти в DRKB (Delphi Russian Knowledge Base) - крупнейшем в рунете сборнике материалов по Дельфи
  • Вопросы по SQL и вопросы по базам данных не связанные с Дельфи задавать здесь

FAQ раздела лежит здесь!


Если Вам помогли и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, Vit, Петрович.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Delphi: Базы данных и репортинг | Следующая тема »


 




[ Время генерации скрипта: 0.0991 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.