1 Востаннє редагувалося Romanvolja (30.10.2024 19:37:01)

Тема: SQL запити та вивід даних на форму

type
  TfrMain = class(TForm)
    edMainID: TEdit;
    edTitle: TEdit;
    edContinent: TEdit;
    edCountry: TEdit;
    dbGrMain: TDBGrid;
    adoQrMain: TADOQuery;

...

Виконую запит на вибірку даних

    adoQrMain.SQL.Clear;
    adoQrMain.SQL.Add('SELECT AMSMain.ID, AMSMain.Title, MSContinent.Continent, MSCountry.Country '+
    'FROM AMSMain, MSContinent, MSCountry '+
    'WHERE AMSMain.Continent=MSContinent.ID AND AMSMain.Country=MSCountry.ID' );
    adoQrMain.Active:=True;

тут все чудово


далі виводжу дані на форму і тут щось не так, бо перші два Edit-ти - добре, а другі не працюють

procedure TfrMain.dbGrMainCellClick(Column: TColumn);
begin
  try
    edMainID.Text:=IntToStr(adoQrMain.FieldByName('ID').AsInteger);
    edTitle.Text:=adoQrMain.FieldByName('Title').AsString;
    edContinent.Text:=adoQrMain.FieldByName('Continent').AsString;
    edCountry.Text:=adoQrMain.FieldByName('Country').AsString;
  except
  on e:Exception do
  end;
end;

Підкажіть будь-ласка де я зробив помилку, буду щиро вдячний.

2

Re: SQL запити та вивід даних на форму

чат
безкоштовна версія
ґпт написав:

Ваш код виглядає майже правильно, і логіка здається зрозумілою. Проблема, швидше за все, полягає у зв'язку полів `edContinent` та `edCountry` з відповідними даними в `adoQrMain`. Якщо `edMainID` та `edTitle` працюють, а `edContinent` і `edCountry` — ні, ймовірно, поля `Continent` та `Country` в `adoQrMain` не містять значень або не оновлюються при виборі рядка в `dbGrMain`.

Перевірте такі моменти:

1. **Поля в `adoQrMain`**: Переконайтеся, що запит виконується правильно і таблиця `adoQrMain` дійсно містить дані в полях `Continent` та `Country`. Спробуйте перевірити результат запиту через відладник або додати рядок `ShowMessage` для виводу цих значень у вашій процедурі `dbGrMainCellClick`.

   ```pascal
   ShowMessage(adoQrMain.FieldByName('Continent').AsString);
   ShowMessage(adoQrMain.FieldByName('Country').AsString);
   ```

2. **Поле `dbGrMain`**: Переконайтеся, що клацання по `dbGrMain` активує відповідний запис в `adoQrMain`, а також що `adoQrMain` дійсно пов'язаний з `dbGrMain` (наприклад, перевірте, чи властивість `DataSource` `dbGrMain` вказує на джерело даних з `adoQrMain`).

3. **Перевірка імен полів**: Переконайтеся, що імена полів у запиті `SQL` точно збігаються з тими, що ви використовуєте в `FieldByName`.

3

Re: SQL запити та вивід даних на форму

Cкоріше за все проблема з об'єднанням таблиць, спробуйте переробити запит з використанням JOIN.

  try
  //...
  except
  on e:Exception do
  end;

Не робіть так. Якщо у програмі є помилка, то її треба виправляти, а не ігнорувати.

Подякували: Romanvolja, leofun012

4 Востаннє редагувалося Romanvolja (05.11.2024 01:54:18)

Re: SQL запити та вивід даних на форму

frz написав:
чат
безкоштовна версія
ґпт написав:

... Проблема, швидше за все, полягає у зв'язку полів

Дякую за допомогу. Так і було як ви сказали - невідповідність назв. Вже виправив.

frz написав:
чат
безкоштовна версія
ґпт написав:

...  код виглядає майже правильно

Дякую за зауваження код переробив вийшло приблизно так.



procedure TfrMain.SelectTableAMSMain;
begin
  adoQrMain.SQL.Clear;
  adoQrMain.SQL.Add('SELECT '+
    'AMSMain.ID,                   AMSMain.Title,               MSContinent.Continent, '+
    'AMSMain.IHave,                AMSMain.Desirable,           AMSMain.CountryDisappeared, '+
    'MSCountry.Country,            MSPeriod.Period,             AMSMain.Subject, '+
....

    'FROM ((((( ((((( ((((( ((AMSMain  '+
    'LEFT JOIN MSContinent ON AMSMain.Continent=MSContinent.ID) '+
    'LEFT JOIN MSCountry ON AMSMain.Country=MSCountry.ID) '+
    'LEFT JOIN MSPeriod ON AMSMain.Period=MSPeriod.ID) '+
    'LEFT JOIN MSSeries ON AMSMain.Series=MSSeries.ID) '+
    'LEFT JOIN MSRuler ON AMSMain.Ruler=MSRuler.ID) '+
.....

    'LEFT JOIN MSMetal ON AMSMain.Metal=MSMetal.ID) '+
    'LEFT JOIN MSSample ON AMSMain.Sample=MSSample.ID '+
    'ORDER BY AMSMain.Title ASC ');
  adoQrMain.Active:=True;
end;
Torbins написав:

Cкоріше за все проблема з об'єднанням таблиць, спробуйте переробити запит з використанням JOIN.

  try
  //...
  except
  on e:Exception do
  end;

Не робіть так. Якщо у програмі є помилка, то її треба виправляти, а не ігнорувати.

Дякую JOIN для мого задуму підходить краще.
Бо мені потрібно було з лівої таблиці вивести всі рядки, а WHERE викидав які були незаповнені і це виявилося проблемою при об'єднанні декількох таблиць.

try постараюся більше його не використовувати.

5

Re: SQL запити та вивід даних на форму

Romanvolja написав:

try постараюся більше його не використовувати.

try/except - це хороша фіча, але тільки якщо секція except не є порожньою і містить якісь дії. Наприклад в даному випадку замість того щоб показувати помилки користувачу, ми записуємо їх в логи:

try
  //...
except
  on e: Exception do
    Log.Add(e);
end;

Ясна річ, що у вас десь має бути об'єкт Log, який вміє записувати інформацію про Exception в файл.

Подякували: Romanvolja1

6

Re: SQL запити та вивід даних на форму

Добрий вечір!
Мене знову занесло кудись не туди.
Щось в мене не виходить додавати в базу дані з допомогою TADOQuery

Роблю вибірку з БД так:

procedure TfrEditCountry.selectTableCountry;
begin
  with frEditBase.adoQrEdit do
    begin
      SQL.Clear;//Очищаємо запит
      SQL.Add('SELECT MSCountry.ID, MSCountry.Country, MSCountry.CountryEnglish,'+
              ' MSContinent.Continent, MSCountry.CountryDisappeared'+
              ' FROM MSCountry'+
              ' LEFT JOIN MSContinent ON MSCountry.Continent=MSContinent.ID'+
              ' ORDER BY Country ASC');
              //Створюємо новий запит
      Active:=True;//Активуємо запит 
    end;
end;

Тут все чудово.

А щоби додати дані в БД я написав так:

procedure TfrEditCountry.spBtAddDatabaseCountryClick(Sender: TObject);
begin
  if (edCountry.Text<>'') or (cmBxContinent.Text<>'') then
    begin
      with frEditBase.adoQrEdit do
        begin
          Insert;//Створюємо новий рядок в таблиці
          FieldByName('Country').AsString:=Trim(edCountry.Text);//заповнюємо поле Країни в таблиці
          FieldByName('CountryEnglish').AsString:=Trim(edCountryEnglish.Text);//заповнюємо поле Країни(англійською)
          FieldByName('Continent').AsString:=Trim(cmBxContinent.Text);//заповнюємо поле Континенти
          FieldByName('CountryDisappeared').AsBoolean:=ckBxCountryDisappeared.Checked;
          Post;//Зберігаємо дані
          Refresh;//Оновлюємо дані
        end;
    end;
  clearAllTextComponent;//Очищаємо поля на формі
end;

додаток компілюється, але при роботі в самому додатку
ShowMessage виводить помилку "Недостатньо відомостей ключового поля для оновлення",
та при цьому дані заносяться аж в обидві таблиці
хоча в таблицю MSContinent(континенти) я не планував заносити дані.
Допоможіть виявити мою помилку буду щиро вдячний.

7

Re: SQL запити та вивід даних на форму

Romanvolja написав:
//...
          FieldByName('Continent').AsString:=Trim(cmBxContinent.Text);//заповнюємо поле Континенти
//...

при цьому дані заносяться аж в обидві таблиці
хоча в таблицю MSContinent(континенти) я не планував заносити дані.

У вас в запиті є лише одне поле з назвою Continent і це MSContinent.Continent. В даному випадку ADO байдуже, що в таблиці MSCountry також є поле з такою назвою, бо серед відображуваних селектом полів воно не згадується. Якщо вам усе ж потрібно відредагувати поле MSCountry.Continent, то вам потрібен інший запит, або взагалі окремий TADOTable для MSCountry.

Romanvolja написав:

додаток компілюється, але при роботі в самому додатку
ShowMessage виводить помилку "Недостатньо відомостей ключового поля для оновлення"

Схоже АДО намагається автоматично згенерувати запит оновлення даних на основі селекта, і у нього не виходить розібратись з джойном двох таблиць. Це хороший приклад того, що відбувається, коли в якійсь бібліотеці надто складна і закручена логіка роботи. Уся ця логіка перетворюється на магію, і поведінка бібліотеки стає непередбачуваною. Для прикладу в бібліотеці FireDAC просто зробили окремий компонент TFDUpdateSQL який дозволяє явно задати запит для оновлення даних. АДО, за задумом його творців, мав би справлятися автоматично, тому в ньому є лише мінімальні засоби виправлення помилок. Можливо вам допоможе щось таке:

frEditBase.adoQrEdit.Properties['Unique Table'].Value := 'MSCountry';

Але загалом раджу просто кинути на форму окремий TADOTable для виконання оновлень даних.

Подякували: leofun01, Romanvolja2

8 Востаннє редагувалося Romanvolja (21.11.2024 15:09:36)

Re: SQL запити та вивід даних на форму

Добрий день!
Я думав, що проблема в тому що в обидвох таблицях поле має однакову назву тому в першій таблиці перейменував поле Continent в ID_Continent та переробив запит

Прихований текст
procedure TfrEditCountry.selectTableCountry;
begin
  with adoQrCountry do
    begin
      SQL.Clear;//Очищаємо запит
      SQL.Add('SELECT MSCountry.ID, MSCountry.Country, MSCountry.CountryEnglish,'+
              ' MSContinent.Continent, MSCountry.CountryDisappeared, MSCountry.ID_Continent'+
              ' FROM MSCountry'+
              ' LEFT JOIN MSContinent ON MSCountry.ID_Continent=MSContinent.ID'+
              ' ORDER BY Country ASC');
              //Створюємо новий запит
      Active:=True;//Активуємо запит
    end;
end

;

Думав, що це виправить помилку, але на жаль не допомогло. Тут або я щось не так зробив, або в JOIN-ні цього непередбачено.

Але дякуючи пану Torbins, що підказав мені один із варіантів по якому я пішов, і все запрацювало.

Прихований текст
procedure TfrEditCountry.spBtAddDatabaseCountryClick(Sender: TObject);
var
  sErrorAddDatabase: string;
  sNameContinent: string;  
  sIDCountry: string;
  iIDContinent: integer;
begin
  if (edCountry.Text='') or (cmBxContinent.Text='') then
    begin
      sErrorAddDatabase:=edCountry.Hint+', '+cmBxContinent.Hint;
      ShowMessage(sErrorAddDatabase);
    end;
  sNameContinent:=QuotedStr(Trim(cmBxContinent.Text));
  with frEditContinent.adoQrContinent do
    begin
      SQL.Clear;//Очищаємо запит
      SQL.Add('SELECT MSContinent.ID, MSContinent.Continent'+
              ' FROM MSContinent'+
              ' WHERE MSContinent.Continent='+sNameContinent);
      Active:=True;//Активуємо запит
    end;
  iIDContinent:=frEditContinent.adoQrContinent.FieldByName('ID').AsInteger;
  if (edCountry.Text<>'') and (cmBxContinent.Text<>'') then
    begin
      with adoQrCountry do
        begin
          SQL.Clear;//Очищаємо запит
          SQL.Add('SELECT * FROM MSCountry ORDER BY MSCountry.ID ASC');
          Active:=True;//Активуємо запит
          Insert;//Додаємо рядок в таблицю
          FieldByName('Country').AsString:=Trim(edCountry.Text);//заповнюємо поле Країни в таблиці
          FieldByName('CountryEnglish').AsString:=Trim(edCountryEnglish.Text);//заповнюємо поле Країни(англійською)
          FieldByName('ID_Continent').AsInteger:=iIDContinent;//заповнюємо поле Континенти
          FieldByName('CountryDisappeared').AsBoolean:=ckBxCountryDisappeared.Checked;
          Post;//Зберігаємо дані
          Refresh;//Оновлюємо дані
        end;    
      adoQrCountry.Last;//Переходимо на останній(доданий)рядок
      sIDCountry:=IntToStr(adoQrCountry.FieldByName('ID').AsInteger);//Беремо ID
      selectTableCountry;//Виводимо запит з таблиці, заповнюємо BDGrig
      adoQrCountry.Locate('ID',sIDCountry, []);//Переходимо на доданий рядок
      clearAllTextComponent;//Очищаємо поля на формі
    end;
end;

Сподіваюся, що не сильно з кодом напартачив.
Але, якщо щось не так, то щиро буду вдячний за критику.