![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Два почти одинаковых запроса выполняются по разному
И. Павел © (2010-08-10 09:13:00)Здравствуйте.
Я работаю с Access в Delphi 7 через ADO, JET.
Первоначальный запрос (SELECT с несколькими LEFT JOIN) выполняется нормально и возвращает 3 записи. Если заменить прямую подстановку двух чисел в строку запроса (с помощью “+ IntToStr(…) +”) на параметры, то запрос возвращает пустую выборку (но если использовать любой из этих двух параметров, а второй IntToStr оставить, то запрос опять будет работать правильно).
Вот первоначальный запрос (он выполняется нормально – возвращает 3 записи):
DMQ.ADODS.Active := false;
DMQ.ADODS.CommandText :=
'SELECT j.kvartira, j.schet, j.familia + '' '' + j.imia + '' '' + j.otchestvo AS FIO, s1.summa AS s1_summa, n.summa AS n_summa, sum(o.summa) AS sum_o_summa, s2.summa AS s2_summa ' +
'FROM (((nachisl AS n ' +
'LEFT JOIN jilci AS j ON (j.id = n.IDJilci AND j.IDDoma = ' + IntToStr(id) + ')) ' +
'LEFT JOIN saldo AS s1 ON (s1.IDJilci = n.IDJilci AND s1.god = ' + IntToStr(god) + ' AND s1.mes = ' + IntToStr(mes) + ')) ' +
'LEFT JOIN saldo AS s2 ON (s2.IDJilci = n.IDJilci AND s2.god = ' + IntToStr(NextGod) + ' AND s2.mes = ' + IntToStr(NextMes) + ')) ' +
'LEFT join oplati AS o ON (o.IDJilci = n.IDJilci AND o.god = ' + IntToStr(god) + ' AND o.mes = ' + IntToStr(mes) + ') ' +
'WHERE n.god = ' + IntToStr(god) + ' AND n.mes = ' + IntToStr(mes) + ' ' +
'GROUP BY n.id, j.kvartira, j.schet, j.familia + '' '' + j.imia + '' '' + j.otchestvo, s1.summa, n.summa, s2.summa;';
DMQ.ADODS.Parameters.ParseSQL(DMQ.ADODS.CommandText, true);
DMQ.ADODS.Open;
Вот запрос, который возвращает пустую выборку (этот запрос отличается от первоначального только тем, что два “+IntToStr(…)+” я заменил на параметры – в коде я их выделил жирным шрифтом):
DMQ.ADODS.Active := false;
DMQ.ADODS.CommandText :=
'SELECT j.kvartira, j.schet, j.familia + '' '' + j.imia + '' '' + j.otchestvo AS FIO, s1.summa AS s1_summa, n.summa AS n_summa, sum(o.summa) AS sum_o_summa, s2.summa AS s2_summa ' +
'FROM (((nachisl AS n ' +
'LEFT JOIN jilci AS j ON (j.id = n.IDJilci AND j.IDDoma = :PIDDoma)) ' +
'LEFT JOIN saldo AS s1 ON (s1.IDJilci = n.IDJilci AND s1.god = ' + IntToStr(god) + ' AND s1.mes = ' + IntToStr(mes) + ')) ' +
'LEFT JOIN saldo AS s2 ON (s2.IDJilci = n.IDJilci AND s2.god = ' + IntToStr(NextGod) + ' AND s2.mes = ' + IntToStr(NextMes) + ')) ' +
'LEFT join oplati AS o ON (o.IDJilci = n.IDJilci AND o.god = ' + IntToStr(god) + ' AND o.mes = ' + IntToStr(mes) + ') ' +
'WHERE n.god = :PNGod AND n.mes = ' + IntToStr(mes) + ' ' +
'GROUP BY n.id, j.kvartira, j.schet, j.familia + '' '' + j.imia + '' '' + j.otchestvo, s1.summa, n.summa, s2.summa;';
DMQ.ADODS.Parameters.ParseSQL(DMQ.ADODS.CommandText, true);
with DMQ.ADODS.Parameters.ParamByName('PIDDoma') do
begin
DataType := ftInteger;
Direction := pdInput;
Value := id;
end;
with DMQ.ADODS.Parameters.ParamByName('PNGod') do
begin
DataType := ftInteger;
Direction := pdInput;
Value := god;
end;
Если параметрами заменить не оба, а только один из “+IntToStr(…)+”, то запрос опять начнет работать правильно, возвращая 3 строки.
Подскажите, пожалуйста, в чем здесь ошибка? Скорее всего, это я что-то сделал не так, но, может быть, это все же Access или ADO?
Заранее спасибо.
12 (2010-08-10 14:42:00) [1]
> LEFT JOIN b ON b.id = a.id AND b.value = 100
плохая запись, имхо
на месте компилятора/интерпретатора я бы такие только за деньги пропускал :)
И. Павел (2010-08-10 14:38:00) [2]> sniknik ©
Спасибо. Буду делать так. Для интереса протестирую - что быстрее.
> какую!?!
Я имел ввиду, что если таблица a имеет записи (пусть и с a.id <> b.id), то первый запрос вернет для каждой строки таблицы "a" по записи, у которой b.id = NULL.
sniknik (2010-08-10 14:26:00) [3]> какую!?!
;) вот поэтому и считаю подобные непонятными/"мутными"
sniknik (2010-08-10 14:24:00) [4]> А как тогда можно будет построить запрос, эквивалентный такому:
> SELECT *
> FROM a
> LEFT JOIN b ON b.id = a.id AND b.value = 100
SELECT *
FROM a
LEFT JOIN (SELECT * FROM b WHERE ID = 1) b ON b.ID = a.ID
в принципе, такой даже работать должен быстрее (проверить бы на больших таблицах).
12 (2010-08-10 14:22:00) [5]
> SELECT *
> FROM a
> LEFT JOIN b ON b.id = a.id
> WHERE b.value = 100
>
> То этот запрос не вернет ничего, если не найдет строку с
> b.value = 100, а первый запрос все равно вернет запись.
какую!?!
И. Павел (2010-08-10 13:49:00) [6]> и все таки лучше разделять, "ограничители" от "обьеденителей".
А как тогда можно будет построить запрос, эквивалентный такому:
SELECT *
FROM a
LEFT JOIN b ON b.id = a.id AND b.value = 100
Ведь если написать
SELECT *
FROM a
LEFT JOIN b ON b.id = a.id
WHERE b.value = 100
То этот запрос не вернет ничего, если не найдет строку с b.value = 100, а первый запрос все равно вернет запись.
Если написать:
SELECT *
FROM a
RIGHT JOIN b ON b.id = a.id
WHERE b.value = 100
то тоже может не вернутся ни одна запись, если найдется b.id = a.id, но, например, с b.value = 200, и эта запись отсеяться в предложении WHERE.
sniknik (2010-08-10 12:23:00) [7]> Оказывается, это может привести к неожиданным результатам.
сам удивился. вообще считал подобное смешение "типов" влияющим только на понимание/ясность запросов, а получается может приводить и к логическим ошибкам... (хорошо, что не допускаю подобной путаницы... т.к. работаю с аксесс (mssql подобное понимает)
И. Павел (2010-08-10 11:42:00) [8]sniknik ©
Большое Вам спасибо. Вынес в where и все заработало!
Но глобальное "опараметривание" запросов я все же делать не буду. Оказывается, это может привести к неожиданным результатам.
12 ©
Спасибо.
sniknik (2010-08-10 11:25:00) [9]> Например, такой запрос нормально выполняется как с параметрами, так и без них:
и все таки лучше разделять, "ограничители" от "обьеденителей".
sniknik (2010-08-10 11:24:00) [10]проблема скорее всего в том, что условие ограничения внесено в условие объединения...
т.е. j.IDDoma = :PIDDoma надо вынести в WHERE, тогда все работает.
И. Павел (2010-08-10 11:14:00) [11]>[14] 12 © (10.08.10 10:52)
Несколько запросов так переписывал - нормально работают. Да и сам этот запрос работает, если заменить параметрами все IntToStr, кроме последнего или первого.
Например, такой запрос нормально выполняется как с параметрами, так и без них:
'SELECT d.nazvanie, d.adres, sum(s1.summa) AS sum_s1_summa, sum(n.summa) AS sum_n_summa, sum(o.summa) AS sum_o_summa, sum(s2.summa) AS sum_s2_summa '
'FROM ((((doma AS d ' +
'LEFT JOIN jilci AS j ON j.IDDoma = d.id) ' +
'LEFT JOIN saldo AS s1 ON (s1.IDJilci = j.id AND s1.god = :PS1God AND :PS1Mes)) ' +
'LEFT JOIN saldo AS s2 ON (s2.IDJilci = j.id AND s2.god = :PS2God AND s2.mes = :PS2Mes)) ' +
'LEFT join nachisl AS n ON (n.IDJilci = j.id AND n.god = :PNGod AND n.mes = :PNMes)) ' +
'LEFT join oplati AS o ON (o.IDJilci = j.id AND o.god = :POGod AND o.mes = :POMes) ' +
'GROUP BY d.id, d.nazvanie, d.adres;'
12 (2010-08-10 10:52:00) [12]а если вообще никуда Design Time не смотреть,
создать динамически,
спросить что-то тупое (чтоб понять, что работает)
потом перезаписать sql с параметрами и спросить?
И. Павел (2010-08-10 10:41:00) [13]> > а если не указывать?
А если просто оставить параметры, созданные ADODataSet автоматически нетронутыми (т.е. со значением null), то тоже возвращается пустая выборка, как и в том случае, если задать значения параметров.
И. Павел (2010-08-10 10:39:00) [14]> а если не указывать?
Пробовал не указывать тип параметров - оставить ftString - тогда тоже возвращается пустая выборка.
А если не указать значения параметров, то ADO ругается: "Отсутствует значение для одного или нескольких параметров".
12 (2010-08-10 10:37:00) [15]
> значение и тип параметров указал в Design Time
а если не указывать?
И. Павел (2010-08-10 10:35:00) [16]Я попробовал ввести запрос в Access. Он спросил у меня значения обоих параметров и выполнил запрос. Вернул 3 строки, т.е. как и нужно.
Потом я создал новый проект и поместил на него только ADOConnection, ADODataSet, ADODataSource и Table. В ADODataSet.CommandText задал запрос (вместо параметров подставил свои значения: god = 2010, PIDDoma = 1) – запрос тоже выполнился нормально (вернул 3 записи).
Потом я заменил в CommandText числа ‘2010’ и ‘1’ на параметры, а значение и тип параметров указал в Design Time. В инспекторе объектов установил ADODataSet.Active:=true. И получилась пустая выборка...
Если у кого-нибудь из участников форума найдется время, пожалуйста, посмотрите мою тестовую программу. Она содержит два ADODatsSet, все параметры которых задаются в Design Time. Один из них (где запрос задается без параметров) возвращает 3 строки, а второй (где используются 2 параметра) - возвращает пустую выборку.
Тестовую программку я сохранил здесь:
http://narod.ru/disk/23584869000/Тестовая%20программа%20для%20Access.rar.html
sniknik (2010-08-10 09:48:00) [17]> все равно ошибка осталась.
не ошибка, раз запрос выполняется. что то не так задал, раз результат не устраивает.
sniknik (2010-08-10 09:46:00) [18]> Просто у меня для задания текста запроса и параметров есть специальный модуль с функциями.
не пользуйся модулем для тестов... тест это должно быть ровно то же самое что ты здесь показываешь.
> но результат тот же.
проверь еще раз. где то что то отличается. можно проверить прямо в дизайне, прописать один запрос увидеть свою пару записей, после прямо в нем заменить на параметры у них (в дизайне) поставить те же значения и посмотреть.
ну и из экзотики (было как то. глюк парсера в какой то версии дельфи видать. сам не получал но обсуждалось)
>= :PIDDoma)
вот тут попробуй поставить пробел между параметром и кавычкой.
И. Павел (2010-08-10 09:42:00) [19]> [5] 12 © (10.08.10 09:29)
Поставил пробелы (в том числе и между параметром и скобкой, на всякий случай) и задал только Value для параметра - все равно ошибка осталась.
И. Павел (2010-08-10 09:35:00) [20]> [4] sniknik © (10.08.10 09:28)
Спасибо. Просто у меня для задания текста запроса и параметров есть специальный модуль с функциями. Поэтому я вынес их код полностью.
Я установил ParamCheck = false и удалил задания типа и направления параметров, но ошибка осталась. Такой запрос тоже возвращает пустую выборку:\\
DMQ.ADODS.Active := false;
DMQ.ADODS.CommandText :=
'SELECT j.kvartira, j.schet, j.familia + '' '' + j.imia + '' '' + j.otchestvo AS FIO, s1.summa AS s1_summa, n.summa AS n_summa, sum(o.summa) AS sum_o_summa, s2.summa AS s2_summa ' +
'FROM (((nachisl AS n ' +
'LEFT JOIN jilci AS j ON (j.id = n.IDJilci AND j.IDDoma = :PIDDoma)) ' +
'LEFT JOIN saldo AS s1 ON (s1.IDJilci = n.IDJilci AND s1.god = ' + IntToStr(god) + ' AND s1.mes = ' + IntToStr(mes) + ')) ' +
'LEFT JOIN saldo AS s2 ON (s2.IDJilci = n.IDJilci AND s2.god = ' + IntToStr(NextGod) + ' AND s2.mes = ' + IntToStr(NextMes) + ')) ' +
'LEFT join oplati AS o ON (o.IDJilci = n.IDJilci AND o.god = ' + IntToStr(god) + ' AND o.mes = ' + IntToStr(mes) + ') ' +
'WHERE n.god = :PNGod AND n.mes = ' + IntToStr(mes) + ' ' +
'GROUP BY n.id, j.kvartira, j.schet, j.familia + '' '' + j.imia + '' '' + j.otchestvo, s1.summa, n.summa, s2.summa;';
DMQ.ADODS.Parameters.ParseSQL(DMQ.ADODS.CommandText, true);
with DMQ.ADODS.Parameters.ParamByName('PIDDoma') do
Value := id;
with DMQ.ADODS.Parameters.ParamByName('PNGod') do
Value := god;
DMQ.ADODS.Open;
PS: еще пробовал удалить ParseSQL и выставить ParamCheck - но результат тот же.