|
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)
Угу. |
Форум | Правила | Описание | Объявления | Секции | Поиск | Книга знаний | Вики-миста |