Имя: Пароль:
1C
1C 7.7
v7: 1Sqlite Left Join или in? Что быстрее
,
0 Aleksey
 
20.12.11
08:21
Задача получить резервы по определенному контрагенту (в регистре Резервы нет контрагента, а есть договор)

Вариант 1. Left Join

"SELECT
|Номенклатура [Номенклатура :Справочник.Номенклатура],
|SUM(Количество) [Количество :Число]
|FROM РегистрИтоги_РезервыТМЦ
|Left Join Справочник_Договоры as ДоговорПолучателя ON ДоговорПолучателя.ID=ДоговорПокупателя
|Where (period=:ДатаТА)
|And (ДоговорПолучателя.PARENTEXT =:ВыбПолучатель)
|group by Номенклатура
|having SUM(Количество) > 0";
           
Вариант 2 IN

"SELECT
|Номенклатура [Номенклатура :Справочник.Номенклатура],
|SUM(Количество) [Количество :Число]
|FROM РегистрИтоги_РезервыТМЦ
|Where (period=:ДатаТА)
|And (ДоговорПокупателя in (Select ID from Справочник_Договоры Where PARENTEXT=:ВыбПолучатель))
|group by Номенклатура
|having SUM(Количество) > 0";
           

Вроде бы читал, что IN  не желательно, типа не оптимизировано. А на практике у меня IN почти в 3 раза быстрее отрабатывает
1 Aleksey
 
20.12.11
08:39
спят все что ли?
2 andrewks
 
20.12.11
08:42
подзапрос выполняется один раз, а перебор при соединении - много раз
3 ДенисЧ
 
20.12.11
08:43
При нормально оптимизаторе - одинаково. но к sqlite это не относится...
4 YHVVH
 
20.12.11
08:47
(0) а запросы мягко сказать один и то же результат выдают?
5 Aleksey
 
20.12.11
08:52
(4) Да, есть сомнения?
6 YHVVH
 
20.12.11
09:11
а если  
And (ДоговорПолучателя.PARENTEXT =:ВыбПолучатель)
поставить в ON?

не будет быстрей?
7 YHVVH
 
20.12.11
09:12
(5) если только ИД уникальный в таблице договоров то одинаковый.
8 Aleksey
 
20.12.11
09:13
(6) Нет, он просто отфильтрует подзапрос. Т.е. если делать Inner join тогда да. А left возмет то, что слева (регистр) и добавит договора, т.е. NULL будет
9 Aleksey
 
20.12.11
09:14
(7) А это уже нештатная ситуация. Платформы, не должна была так сделать
10 YHVVH
 
20.12.11
09:18
(8) мне кажется что в  первом случае джойница вся таблица договоров а потом отбирается по родителю. может я ошибаюсь.
11 rs_trade
 
20.12.11
09:19
(8) и условие стоит по полю из не сохраняемой таблицы. это скорее всего логическая ошибка
12 Кириллка
 
20.12.11
09:24
(0)вариант с left join в данном случае вырождается в inner join. Третий вариант с Exists попробуй.
13 Aleksey
 
20.12.11
09:31
(12) inner join немного быстрее left join, но сопоставим с ним про скорости разница около 3%, т.е. в пределах погрешности

|Inner Join Справочник_Договоры as ДоговорПолучателя ON ДоговорПолучателя.ID=ДоговорПокупателя  and (ДоговорПолучателя.PARENTEXT=:ВыбПолучатель)
14 Кириллка
 
20.12.11
09:35
кста, а второй запрос рабочий вообще?
15 Aleksey
 
20.12.11
09:37
Максимальная скорость при двойном фильтре (процентов на 12 ускорилось)

|iner Join Справочник_Договоры as ДоговорПолучателя ON ДоговорПолучателя.ID=ДоговорПокупателя  and (ДоговорПолучателя.PARENTEXT=:ВыбПолучатель)
|Where ДоговорПолучателя.PARENTEXT=:ВыбПолучатель)

Но все равно это более чем в 2 раза дольше чем c IN
16 Aleksey
 
20.12.11
09:37
(14) Да оба запроса дают одинаковый результат
17 Aleksey
 
20.12.11
09:39
Сдается мне (судя по отладки) что Join тащит всю таблицу договора со всеми реквизита, тогда как IN только одну колонку. Из-за этого и тормоза. Как то можно ограничить колонки при join'е?
18 Кириллка
 
20.12.11
09:40
19 orefkov
 
20.12.11
09:42
Нахрен тут left join ?
Это сразу однозначно будет перебиратся ВСЕ записи в регистре, просто лишние отсекутся по условию
ДоговорПолучателя.PARENTEXT =:ВыбПолучатель
inner join, однозначно.
Может у тебя по полю ДоговорПокупателя отбор итогов есть, тогда вообще влет пойдет.
20 Aleksey
 
20.12.11
09:44
(19) Есть отбор

Так как правильно записать?
21 Aleksey
 
20.12.11
09:46
При условии, что даже модифицированный вариант 1 (с iner Join и двойным фильтром), дольше чем Вариант 2
22 orefkov
 
20.12.11
09:47
|iner Join Справочник_Договоры as ДоговорПолучателя ON ДоговорПолучателя.ID=ДоговорПокупателя
|Where ДоговорПолучателя.PARENTEXT=:ВыбПолучатель
23 orefkov
 
20.12.11
09:49
Если точно отбор итогов есть, то можно попробовать еще

"SELECT
|Номенклатура [Номенклатура :Справочник.Номенклатура],
|SUM(Количество) [Количество :Число]
|FROM Справочник_Договоры as ДоговорПолучателя
|Left Join РегистрИтоги_РезервыТМЦ ON ДоговорПолучателя.ID=ДоговорПокупателя
|Where (period=:ДатаТА)
|And (ДоговорПолучателя.PARENTEXT =:ВыбПолучатель)
|group by Номенклатура
|having SUM(Количество) > 0";
24 Aleksey
 
20.12.11
09:52
(23) Нефига себе

В 26 раз быстрее, чем Вариант 2
25 Aleksey
 
20.12.11
09:53
Моя в легком шоке
26 orefkov
 
20.12.11
09:59
то-то
27 orefkov
 
20.12.11
10:00
неправильный план sqlite выбирал, приходится его понужать немного.
28 NcSteel
 
20.12.11
10:00
(23) ну так запрос не выразится во внутреннее соединение?
29 NcSteel
 
20.12.11
10:01
(28) + из-за: (period=:ДатаТА)
30 Ёпрст
 
20.12.11
10:02
(28) выразился
31 NcSteel
 
20.12.11
10:03
Ну так зачем эти приблуды , не проще явно указать ))).
32 orefkov
 
20.12.11
10:09
В данном случае понятно, что left тут по результату равносилен Inner.
Но, когда применяется left join, sqlite не может менять порядок обхода таблиц.
Поэтому вариант в (23) заставляет его обходить сначала справочник договоров, отбирая нужные, а потом для каждого договора отбирать записи из регистра. При inner - он почему-то предпочитает обходить весь регистр, проверяя, а не для одного из нужных ли договоров запись.
33 Ёпрст
 
20.12.11
10:10
(32) зачетно, нодо будет как-нить пересмотреть свои запросы.
34 orefkov
 
20.12.11
10:12
+(32)
А, все понял, почему sqlite хочет сначала обходить регистр. Из-за "group by Номенклатура". Уж так вот в нем агрегирование сделано, предпочитает получать записи в порядке группировок.
35 NcSteel
 
20.12.11
10:13
(32) Данная фича истинна только для sqlite ? Или MS SQL подвержен так же ?
36 Ёпрст
 
20.12.11
10:14
(34) а если group by во внешнем запросе делать ?
37 orefkov
 
20.12.11
10:17
(24)
Надо было в консоли смотреть план выполнения, в каком порядке таблицы обходятся.
(35)
Не знаю. Просто в sqlie джойны сделаны только и исключительно как "nested loops". А в ms-sql насколько помню, есть еще куча вариантов исполнения джойнов.
(36)
В данном случае, возможно вынос группировки во внешний запрос поможет, а возможно, sqlite опять решит, что он очень умный и внесет группировку во внутренний запрос. Все надо проверять на практике на предмет плана выполнения. Добавляешь перед запросом "explain query plan" и смотришь, в каком порядке что обходится. В моей консоли это есть.
38 Ёпрст
 
20.12.11
10:18
(37) та, которая нп сайте в качестве примера валяется ?
39 orefkov
 
20.12.11
10:31
(38)
Угу.