Изменение запроса в зависимости от выбранных строк в нескольких DbGrid (в SQL получить значения из нескольких DataSet)

Здравствуйте.Есть таблицы и компоненты:Tbl_authors – Таблица авторов. С ней связаны компоненты: - ADOTable_authors, - DataSource_authors, - DBGrid_authors. Tbl_books – Таблица книг. С ней связаны компоненты: - ADOTable_books, - DataSource_books, - DBGrid_books.Tbl_books-authors – Таблица книг по авторам. С ней связаны компоненты: - ADOQuery_booksauthors - DataSource_booksauthors, - DBGrid_booksauthors.Требуется отобразить в DBGrid_booksauthors только те записи, которые соответствуют выбранным строкам в DBGrid_authors и DBGrid_books. Иными словами, требуется отобразить написанные конкретным автором книги выбранного наименования. И отображать результат в DBGrid_booksauthors при выборе строк в DBGrid_authors и DBGrid_books (при перемещении по этим гридам).Для этого, как я понимаю, надо в SQL-запросе обработать параметры из нескольких датасетов. Например, так (параметр :Id_Author соответствует полю Id_Author в таблице Tbl_authors, параметр :Id_Book соответствует полю Id_Book в таблице Tbl_books):
SELECT Tbl_books.Book_Name AS [Наименование книги], Tbl_authors.Author_Name AS [Фамилия автора]
FROM Tbl_books, Tbl_authors, Tbl_BooksAuthors
WHERE Tbl_BooksAuthors.Id_Author = :Id_Author AND
      Tbl_BooksAuthors.Id_Book = :Id_Book AND
      Tbl_books.Id_Book = Tbl_BooksAuthors.Id_Book AND
      Tbl_authors.Id_Author = Tbl_BooksAuthors.Id_Author
Как с одного датасета получать в запросе значение - я разобрался (решение написано на форуме в этой теме: Как отобразить значения в таблице, соответствующие коду в другой таблице...).Но вот как получить параметры из нескольких датасетов - не понимаю.Подскажите, куда копать?Заранее спасибо за ответ(ы).
14 ответов

отобразить написанные конкретным автором книги выбранного наименования.
Не понятая фраза. А что, есть много книг одного выбранного наименования какого-либо автора? Уточнить бы что имелось в виду.Если же предположить, что надо выбирать одну книгу, затем ее автора, то зачем тогда огород городить с БД - и так все ясно кто какую книгу написал.Видимо, большую ценность будет иметь задача, в которой по указанному автору показать все его книги. Или, наоборот, по книге найти автора. Но, т.к. это разные задачи, то и решения для каждой нужно будет реализовать по разному.
Но вот как получить параметры из нескольких датасетов - не понимаю.
Данные получают не из датасетов, а из базы данных SQL запросом. Для объединения данных из двух и более таблиц в SQL есть механизм JOIN. Про него и надо читать.


Не понятая фраза. А что, есть много книг одного выбранного наименования какого-либо автора? Уточнить бы что имелось в виду.
Пример такой. Есть автор - Андрей Лаппа. Он написал книгу "Йога. Традиция Единения".Но эта книга в одном и том же издательстве София печаталась: - в 2007 году и имеет ISBN 5-9550-0561-7, 978-5-91250-099-2 (см., например, тут), - в 2013 году и имеет ISBN 978-5-399-00518-8 (см., например, тут).Так же книга печаталась в издательстве Экслибрис: - в 2003 году и имеет ISBN 5-901620-40-2 (см., например, тут).И ещё книга печаталась в издательстве Janus Books: - в 1999 году и имеет ISBN 5-220-00214-7 (см., например, тут).Формально, так как ISBN разный, то книги обязаны считаться разными. Но фактически - это переиздание одной и той же книги и текст один и тот же. Ну может быть, для разных ISBN основное семейство используемых гарнитур шрифтов, формат издания, тип бумаги, переплёт и прочие элементы оформления и печати будут отличаться, но не текст.Соответственно, в базе авторов (а не в конкретной таблице авторов) будет информация об авторе Андрее Лаппа. В базе книг (а не в конкретной таблице книг) будет информация о книгах: - Наименование книги: "Йога. Традиция Единения". - ISBN книги:... - Издательство: ... - Год издания: ... База авторов имеет несколько связанных таблиц, в которых хранится информация об авторах. Аналогично и база книг имеет несколько таблиц, в которых хранится информация о книгах.Еще бывает вариант, когда одна и та же книга печатается в разных издательствах в одном и том же году - текст от этого не меняется но формально, опять же, книги совершенно разные. Пример привести не могу - не помню книгу - но встречал, что одна и та же книга в разных издательствах издавалась чуть ли не в одном и том же месяце (в книге указывается день, когда она подписывается в печать). Формально, ISBN у таких книг опять же будет различный и книги должны считаться разными (но текст в этих ситуациях так же будет один и тот же).Вот и требуется отслеживать такие ситуации.
Видимо, большую ценность будет иметь задача, в которой по указанному автору показать все его книги. Или, наоборот, по книге найти автора. Но, т.к. это разные задачи, то и решения для каждой нужно будет реализовать по разному.
Эту задачу я уже смог решить, решение тут: Как отобразить значения в таблице, соответствующие коду в другой таблице
Данные получают не из датасетов, а из базы данных SQL запросом
Может быть, не совсем корректно выразился. В этом сообщении написано про связь применительно к одному датасету (то есть, как получить параметры из одного датасета), но сейчас меня интересует вопрос - как организовать связующие переменные из нескольких датасетов:
В этом запросе :CustNo - связывающая переменная, которой должно быть присвоено значение из некоторого источника. Delphi позволяет использовать поле TQuery.DataSource чтобы указать другой DataSet, который предоставит эту информацию автоматически. Другими словами, вместо того, чтобы использовать свойство Params и "вручную" присваивать значения переменной, эти значения переменной могут быть просто взяты автоматически из другой таблицы. Кроме того, Delphi всегда сначала пытается выполнить параметризованный запрос используя свойство DataSource, и только потом (если не было найдено какое-то значение параметра) будет пытаться получить значение переменной из свойства Params. При получении данных из DataSource считается, что после двоеточия стоит имя поля из DataSource. При изменении текущей записи в главном DataSet запрос будет автоматически пересчитываться.
и если я не заблуждаюсь, то формальный перевод слова "DataSet" как раз и означает "набор данных". База данных - это то же набор данных. Или я не правильно понимаю эти термины?
При составлении темы рекомендуется не перечислять связанные компоненты на форме (это мало о чем говорит), а давать структуру таблиц базы данных (поля таблиц с их типами).
Есть три таблицы:Tbl_authors – Таблица авторов Поля: - Id_Author (первичный ключ). Тип Integer. - Author_Name. Тип String.Tbl_books – Таблица книг - Id_Book (первичный ключ). Тип Integer. - Book_Name. Тип String.Tbl_books-authors – Таблица книг по авторам - Id (первичный ключ). Тип Integer . - Id_Book. Тип Integer. - Id_Author. Тип Integer.Например, такие данные: Значения в Tbl_authors
Id_Author Author_Name
1 Автор1
2 Автор2
3 Автор3
4 Автор4
5 Автор5
Значения в Tbl_books
Id_Book Book_Name
1 книга1 автор1
2 книга2 автор1,2
3 книга3 автор3
4 книга4 автор2
Значения в Tbl_books-authors
Id Id_Book Id_Author
1 1 1
2 2 1
3 2 2
4 3 3
5 4 2
Для объединения данных из двух и более таблиц в SQL есть механизм JOIN
У меня не объединение таблиц, а выборка некоторого подмножества данных из общей совокупности. Меня полностью устраивает приведённый в первом сообщении SQL-запрос, но вот сейчас я могу его выполнить, только нажимая на кнопку, - то есть, вынуждая пользователя совершить лишнее действие.


Значения в Tbl_books
кто вам придумал такую таблицу? это же ужас
Меня полностью устраивает приведённый в первом сообщении SQL-запрос
а вы почитайте про JOINпо задаче - вы умеете клик по DBGrid отлавливать?


У меня не объединение таблиц, а выборка некоторого подмножества данных из общей совокупности
Чтобы понимать друг друга необходимо пользоваться терминологией, которую используют большинство разработчиков баз данных. Речь у меня шла не об объединении таблиц, а об объединении данных двух и более таблиц. А это очень разные вещи. "Выборка некоторого подмножества данных из общей совокупности" - это и есть объединение данных из разных таблиц, которое делает сервер СУБД на основании полученного текста SQL запроса. Результирующая выборка в виде некоторого набора данных поступает в клиентскую программу и которым управляет датасет.Приведенный в первом посте запрос содержит то самое объединение данных. Такое объединение называют неявным и которое в серьезных программах уже мало используется. Вместо этого применяют явное объединение на основе предложения JOIN. Хотя, конечно, никто не будет ставить к стенке за использование неявного объединения. В большом количестве случаев и то и другое объединения выбирают из БД один и тот же набор данных. Но JOIN более удобен и появился в синтаксисе SQL совсем не случайно. --- Помимо отлавливания клика по DBGrid, которое предложил коллега, в датасете, связанным с DBGrid, есть событие AfterScroll, которое реагирует на любое перемещение по DBGrid, чем бы это перемещение не было вызвано. --- Значения для параметров в запросе легко взять из тех же датасетов. К примеру:
  ADOQuery_booksauthors.SQL.Text := 'select ...'; //сам запрос
    //присвоение значений параметрам  
  ADOQuery_booksauthors.Parameters[0].Values := Tbl_authors['Id_Author'];
  ADOQuery_booksauthors.Parameters[1].Values := Tbl_books['Id_Book']
    //или, что одно и то же, но для большего понимания на примере одного из параметров
  // ADOQuery_booksauthors.Parameters.ParamByName('Id_Author').Values := Tbl_authors['Id_Author'];
  ADOQuery_booksauthors.Open; //и открываем полученный набор данных


кто вам придумал такую таблицу? это же ужас
сам придумал А в ней что такое плохое, чтобы изменить? Старался нормализовать таблицы, устранить избыточность информации и обеспечить целостность данных - привести к третьей нормальной форме. Подскажите, что надо изменить? Или что конкретно неправильно сделано?
клик по DBGrid отлавливать
Это, как я понимаю, событие OnCellClick, в частности, в нем можно получить номер записи и поле по которому кликнули:
procedure TForm1.DBGridCellClick(Column: TColumn);
var
  S: String;
begin
  S := 'FieldName=' + Column.FieldName + ' RecNo=' + IntToStr(Column.Grid.DataSource.DataSet.RecNo);
  ShowMessage(S);
end;
Такая идея была - но её отложил Так как существует возможность попасть в DBGrid, используя клавиатуру и перемещаться по строкам с использованием клавиатуры Можно отработать и эти варианты, но получается заморочено и возникает некоторая избыточность кода...
в датасете, связанным с DBGrid, есть событие AfterScroll, которое реагирует на любое перемещение по DBGrid, чем бы это перемещение не было вызвано
Это на первый взгляд - то, что и хотелось найти. Спасибо за подсказку.
Приведенный в первом посте запрос содержит то самое объединение данных. Такое объединение называют неявным и которое в серьезных программах уже мало используется. Вместо этого применяют явное объединение на основе предложения JOIN
Скандербег, напишите, пожалуйста, как запрос должен быть написан с использованием JOIN? У меня не получается это сделать На всякий случай, привожу код исходного запроса:
SELECT Tbl_books.Book_Name AS [Наименование книги], Tbl_authors.Author_Name AS [Фамилия автора]
FROM Tbl_books, Tbl_authors, Tbl_BooksAuthors
WHERE Tbl_BooksAuthors.Id_Author = :Id_Author AND
      Tbl_BooksAuthors.Id_Book = :Id_Book AND
      Tbl_books.Id_Book = Tbl_BooksAuthors.Id_Book AND
      Tbl_authors.Id_Author = Tbl_BooksAuthors.Id_Author


SELECT Tbl_books.Book_Name AS [Наименование книги], Tbl_authors.Author_Name AS [Фамилия автора]
FROM Tbl_BooksAuthors JOIN Tbl_books ON Tbl_books.Id_Book = Tbl_BooksAuthors.Id_Book
     JOIN Tbl_authors ON Tbl_authors.Id_Author = Tbl_BooksAuthors.Id_Author 
WHERE Tbl_BooksAuthors.Id_Author = :Id_Author AND
      Tbl_BooksAuthors.Id_Book = :Id_Book
Но это лишь пример так называемого внутреннего объединения (INNER JOIN, где слово INNER можно опускать), а будет он работать на ваших реальных данных - надо проверять (такой БД для проверки у меня нет).


Подскажите, что надо изменить?
нельзя хранить 2 значения в 1 ячейке
Так как существует возможность попасть в DBGrid, используя клавиатуру и перемещаться по строкам с использованием клавиатуры
это потому что не надо использовать DBGrid)


Скандербег, вставил текст в поле Memo и из него записываю в ADOQuery2.SQL.Text. При выполнении запроса выдаётся ошибка (см. рис 1 во вложении) - "Ошибка синтаксиса в предложении FROM."запрос отрабатывается так:
  ADOQuery2.Close;
  ADOQuery2.SQL.Clear;
  ADOQuery2.SQL.Text := Memo1.Text;
  ADOQuery2.Parameters.ParamByName('Id_Author').Value :=
      ADOTable_Tbl_authors.FieldByName('Id_Author').AsInteger;
  ADOQuery2.Parameters.ParamByName('Id_Book').Value :=
      ADOTable_Tbl_books.FieldByName('Id_Book').AsInteger;
  ADOQuery1.ExecSQL;
  ADOQuery2.Open;
Ошибка возникает при выполнении:
ADOQuery2.Open;
Что надо исправить?


нельзя хранить 2 значения в 1 ячейке
не понял, а какие два значения хранятся в одной ячейке?
это потому что не надо использовать DBGrid
Что тогда лучше использовать вместо DBGrid для организации табличного вывода?


Что тогда лучше использовать вместо DBGrid для организации табличного вывода?
stringgrid или ListView тут смотря для чего и какие функции нужны


нельзя хранить 2 значения в 1 ячейке
Если это замечание по поводу значений в таблице Tbl_books в колонке "Book_Name"
Id_Book Book_Name
1 книга1 автор1
2 книга2 автор1,2
3 книга3 автор3
4 книга4 автор2
так это тестовые значения, что бы мне легче было ориентироваться. В идеале будут правильные наименования книг.


ADOQuery1.ExecSQL; * ADOQuery2.Open;
это что такое? зачем так делать? если у тебя SELECT - делай просто Open
так это тестовые значения,что бы мне легче было ориентироваться
нельзя так делать ид_книги | ид_авторадаже если у книги несколько авторов, все равно надо хранить по одному потом запросом надо склеить авторов в строку и все


делай просто Open
Убрал ADOQuery1.ExecSQL - всё равно "Ошибка синтаксиса в предложении FROM."
нельзя так делать ид_книги | ид_автора
Я правильно понимаю, что это замечание по таблице: Tbl_books-authors – Таблица книг по авторам - Id (первичный ключ). Тип Integer . - Id_Book. Тип Integer. - Id_Author. Тип Integer.


по синтаксису - проверял свой запрос в студии?