powered by simpleCommunicator - 2.0.60     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / FoxPro, Visual FoxPro [игнор отключен] [закрыт для гостей] / Помогите составить запрос - очень нужно!
15 сообщений из 15, страница 1 из 1
Помогите составить запрос - очень нужно!
    #32503489
demidovich
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Всем доброе время суток!
Мне очень нужно составить запрос, суть которого я попытаюсь изложить на примере.
Есть 3 таблицы (это только пример - в реальности в каждой таблице хранятся сотни тысяч записей):

"Main"
----------------
NSER [PK]| FK
----------------
1 | 1
2 | NULL
3 | 2
4 | NULL
5 | 3

"Otz"
-----------
ID [PK]| FK
-----------
1 | 1
2 | 1
3 | 2
4 | 3
5 | 3
6 | 3

"Ik"
-----------
ID [PK]| Data
-----------
1 | "111"
2 | "222"
3 | "333"
4 | "444"
5 | "555"

Так вот, необходимо составить запрос, который возвращал бы результат джоина этих трех таблиц таким образом:

"Result"
----------------
NSER | FK
----------------
1 | "222"
2 | NULL
3 | "333"
4 | NULL
5 | "555"

Поясню:
1. джоин надо делать таким образом: Main <-(FK)-> Otz <-(ID)-> Ik
2. из таблицы Otz надо брать соответствие с максимальным ID, для которого есть инфа в Ik.
3. запрос будет выполняться через VFPOLEDB 7.0
4. Объем данных колоссальный, так что по возможности лучше избежать сложных вложенных запросов

Спасибо всем дочитавшим до этого места за терпение!-)
...
Рейтинг: 0 / 0
Помогите составить запрос - очень нужно!
    #32503539
demidovich
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Объясните мне, по крайней мере, почему для приведенного мной примера по-разному работают следующие два запроса:
1.
Код: plaintext
1.
2.
3.
select m.nser, o.fk, o.id, k.data 
from ik k 
inner join otz o on k.id = o.id
right join main m on m.fk=o.fk

2.
Код: plaintext
1.
2.
3.
select m.nser, o.fk, o.id, k.data 
from main m 
left join otz o on m.fk=o.fk
inner join ik k on k.id = o.id

???
...
Рейтинг: 0 / 0
Помогите составить запрос - очень нужно!
    #32503540
demidovich
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Следующий запрос выдает искомый рекордсет:
Код: plaintext
1.
2.
3.
4.
select m.nser, k.data 
from ik k 
inner join otz o on k.id = o.id
right join main m on m.fk=o.fk
group by m.nser

Однако, данный запрос использует отклонение от стандартного синтаксиса GROUP BY (поле k.data отсутствует в списке группировки).
Внимание, вопрос: при каких обстоятельствах будет отрабатывать этот запрос, а также чем чревато использование такого отклонения от синтаксиса?
...
Рейтинг: 0 / 0
Помогите составить запрос - очень нужно!
    #32503746
Sjfx
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
А вот с этим кодом можно попасть на неприятности, поскольку его
результат зависит от расположения данных.
(Это зависит не от наличия или отсутствия агрегирующей функции,
а от наличия полей, не участвующих в group by или агрег-й ф-ии.)
Попробуй-ка вот с такими данными:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
CREATE TABLE otz (id int, fk int)
INSERT INTO otz values( 1 , 1 )
INSERT INTO otz values( 2 , 1 )
INSERT INTO otz values( 3 , 2 )
INSERT INTO otz values( 5 , 3 ) && сначала  5 
INSERT INTO otz values( 4 , 3 ) && потом  4 
INSERT INTO otz values( 6 , 3 )

не зависит от расположения данных:
Код: plaintext
1.
2.
3.
4.
SELECT MAX(otz.id) as id, fk FROM otz RIGHT JOIN ik ON otz.id=ik.id ;
 GROUP BY fk INTO CURSOR qq
SELECT main.nser, ik.dat FROM main LEFT JOIN qq ON main.fk=qq.fk ;
 LEFT JOIN ik ON qq.id=ik.id

Однако я бы использовал навигационный подход, скорее всего, тут он
будет работать быстрее (если делать правильно).
PS. А про существование антидемидовича знаешь? :)
...
Рейтинг: 0 / 0
Помогите составить запрос - очень нужно!
    #32503808
demidovich
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
2 Sjfx:
1) Да, с такими "необычными" данными запрос не будет работать (хотя в реальной таблице все сортировано по id :).
2) Курсор - это круто. А Фокс не потянет подзапроса? Очень уж хочется сделать ОДИН запрос по типу:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
select keys.nser, k.data 
from ik k 
right join 
(select m.nser, max(o.id) as id
from ik k 
inner join otz o on k.id = o.id
right join main m on m.fk=o.fk
group by m.nser) keys on k.id = keys.id

3) А что такое "навигационный" метод? Это значит, что надо "подтягивать" данные из ik для каждого отдельновзятого nser из Main? Т.е. для каждого nser выполнять свой запрос для получения data, так?
4) про антидемидовича не знаю :)
...
Рейтинг: 0 / 0
Помогите составить запрос - очень нужно!
    #32503843
Фотография ВладимирМ
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
У тебя неправильное понимание того, как работает объединение JOIN

Вне завивимости от использования опций LEFT, RIGHT, FULL, INNER в той части данных, где ЕСТЬ данные в обеих связываемых таблицах все они работаею абсолютно одинаково! И работают аналогично объединению по той же связи в условии WHERE.

Отличия заключаются в способе обработке записей одной таблицы для которых нет соответствующих записей в другой таблице.

Смотрим твой пример:

Код: plaintext
1.
2.
3.
select m.nser, o.fk, o.id, k.data 
from ik k 
inner join otz o on k.id = o.id
right join main m on m.fk=o.fk


ik INNER JOIN otz - отбросит все записи из таблицы otz для которых нет записей в таблице ik
RIGHT JOIN main - результат предыдущего объединения будет присоединен к данным таблицы main причем в результирующую выборку попадут записи из предыдущего объединения для которых нет соответсвующих записей в таблице main

Код: plaintext
1.
2.
3.
select m.nser, o.fk, o.id, k.data 
from main m 
left join otz o on m.fk=o.fk
inner join ik k on k.id = o.id


main LEFT JOIN otz - объединит записи из таблиц main и otz причем будут добавлены записи из таблицы otz для которых нет соответсвующих записей в таблице main
INNER JOIN ik - результат предыдущего объединения будет присоединен к данным таблицы ik. Т.е. если в результате предыдущего объединения есть записи, которые не удовлетворяют условию связи с таблицей ik, то они будут отброшены. Если этого надо избежать, то вместо INNER надо использовать повторный LEFT JOIN

Итого, в первом запросе у тебя остануться записи из таблицы main с кодами 4,5, а во втором случае они будут отброшены.

При использовании группировок не по всем полям команды SELECT FoxPro выбирает (если не ошибаюсь) последние записи (в порядке их физического следования). Поэтому, если в пределах одной группы могут встречаться разные значения, то результат такого запроса - непредсказуемый.

У тебя единственный выход (если нужны именно запросы) - делать 2 последовательных запроса:
-) Сначала найти максимальные значения ID в таблице Otz (с группировкой по FK)
-) Сделать объединение с этой временной таблицей

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
SELECT fk, MAX(id) as id 
FROM otz 
INTO CURSOR tmpOtz NOFILTER
GROUP BY fk

SELECT main.nser, ik.data
FROM main
LEFT JOIN tmpOtz ON tmpOtz.fk = main.fk
LEFT JOIN ik ON tmpOtz.id = ik.id

USE IN tmpOtz


Заметь, во втором запросе 2 LEFT JOIN, поскольку INNER JOIN между ik и tmpOtz в таком синтаксисе приведет к потере записей из main для которых нет fk в таблице tmpOtz

PS: FoxPro может использовать подзапросы только в директиве WHERE, поэтому в данной задаче неприменим
...
Рейтинг: 0 / 0
Помогите составить запрос - очень нужно!
    #32503857
demidovich
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
2 Sjfx:
Да, вопрос в догонку: как после манипуляций с курсором закрыть только его (в хэлпе я нашел только close all)? Меня, конечно, интересует метод, который бы работал в VFPOLEDB. Сенкс.
...
Рейтинг: 0 / 0
Помогите составить запрос - очень нужно!
    #32503884
Фотография ВладимирМ
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Курсор - это временная таблица, которая автоматически будет закрыта по окончании сеанса работы FoxPro. Если же его надо закрыть принудительно, то для этого используют команду USE

USE IN tmpOtz

Если не указано имя файла DBF, то команда USE закроет открытую таблицу в текущей или в указанной (опция IN) рабочей области.
...
Рейтинг: 0 / 0
Помогите составить запрос - очень нужно!
    #32503941
Sjfx
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
1. Если стопроцентно уверен в отсортированности данных - используй свой запрос и не парься.
2. Можно одним "правильным" запросом, но он намного медленнее...
3. Навигационный метод - обработка данных, использующая следование записей в определённом порядке. Используются команды типа INDEX, set relation, ...
Если не знаком - то для срочной задачи не стоит и лезть.
...
Рейтинг: 0 / 0
Помогите составить запрос - очень нужно!
    #32503986
demidovich
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
2 ВладимирМ:
Спасибо за столь подробный ответ!
ВладимирМИтого, в первом запросе у тебя остануться записи из таблицы main с кодами 4,5, а во втором случае они будут отброшены.
Осмелюсь возразить: у меня во втором запросе "убиваются" записи с nser=2 и 4. Однако, я разобрался. Действительно, эти джоины должны работать по-разному.

2 Sjfx:
Идея c корявым GROUP BY точно будет работать, если id в Otz будет отсортирован (т.е. при выборе select * from otz записи будут возвращать id в порядке возрастания)?

2 All:
В итоге идея с курсором переросла в такое решение:
Код: plaintext
1.
2.
3.
4.
5.
select m.nser, max(o.id) as id from ik k inner join otz o on k.id = o.id right join main m on m.fk=o.fk group by m.nser into cursor qq;

select qq.nser, k.data from ik k right join qq on k.id = qq.id;

use in qq;

Как считаете, насколько оно грамотное?

Всем откликнувшимся спасибо!
...
Рейтинг: 0 / 0
Помогите составить запрос - очень нужно!
    #32504023
demidovich
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Чем в моем курсорном решении поможет NOFILTER?
...
Рейтинг: 0 / 0
Помогите составить запрос - очень нужно!
    #32504142
Фотография ВладимирМ
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
В FoxPro при определенных ситуациях команда Select ... INTO CURSOR создает не временную таблицу, а просто накладывает фильтр на таблицу-источник. Соответсвенно, использование такой таблицы в последующем запросе становится невозможным (будет сообщение об ошибке). Чтобы быть уверенным, что будет создана именно временная таблица, а не наложен фильтр и используют опцию NOFILTER.

Однако в твоем случае нет условий для наложения фильтра. Всегда будет создана именно временная таблица. Так что можешь и не использовать NOFILTER. Это просто перестраховка.

В твоем последнем варианте это конечно будет работать, но ты делаешь лишний шаг - дважды сканируешь таблицу ik - сначала для формирования курсора, а потом для формирования итоговой выборки.

Лучше делать так:

1)
-) Для определения MAX() объединить main LEFT JOIN otz
-) Результат объединить по LEFT ( RIGHT) с ik

2)
-) Для определения MAX() объединить ik INNER JOIN otz
-) Результат объединить по LEFT ( RIGHT) с main

3)
Ранее описанный способ определения MAX() только по таблице otz

Посмотри, какой способ будет наиболее быстрым
...
Рейтинг: 0 / 0
Помогите составить запрос - очень нужно!
    #32504725
Sjfx
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
1. Такие фишки на предварительно отсортированных выборках у меня ПОКА
ни разу не отказывали (но всё-равно неуютно при каждом переходе
на новую версию фокса).
2. Никогда не сокращай алиас до одной буквы "m", чтобы не было
недоразумений и путаницы с переменными памяти, они и уточняются
префиксом "m". Я дык вообще менее чем 2-х буквенными алиасами на
фоксе не пользуюсь, поскольку уже могут существовать уже открытые
алиасы с теми же однобуквенными префиксами. Во избежание, так сказать.
...
Рейтинг: 0 / 0
Помогите составить запрос - очень нужно!
    #32504985
demidovich
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
2 Sjfx:
1. Ну и круто!-) А то что-то мне неохота париться с курсорами - это потребует работы с батчами, которые возвращают два рекордсета :(
2. Не хочу код шибко менять, тем более, что ничего кроме банальных селектов я с фоксой не далаю (а если и делаю, то через другое соединение). Но на будущее обязательно учту!
...
Рейтинг: 0 / 0
Помогите составить запрос - очень нужно!
    #32504987
demidovich
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
2 Sjfx:
Да, кстати, в 8 версии без SET ENGINEBEHAVIOR 70 описанный номер с GROUP BY не должен прокатить...
...
Рейтинг: 0 / 0
15 сообщений из 15, страница 1 из 1
Форумы / FoxPro, Visual FoxPro [игнор отключен] [закрыт для гостей] / Помогите составить запрос - очень нужно!
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]