Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Два почти одинаковых запроса выполняются по разному


Страницы:   1 2

И. Павел ©   (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 - но результат тот же.


Страницы:   1 2

Наверх

Титульная страница Поиск, карта сайта Написать письмо