Имя: Пароль:
1C
1С v8
Индексы в запросах.
0 Lionelho
 
19.12.13
14:21
Здравствуйте. Подскажите пожалуйста. Есть левое соединение 2-х временных таблиц. Левая таблица индексируется по связующему полю. Имеет ли смысл индексировать ещё правую таблицу по тому же полю? Ускорит ли это запрос?  Количество строк в таблицах в расчет не берем.
1 Defender aka LINN
 
19.12.13
14:26
"Количество строк в таблицах в расчет не берем". Полагаешь, на таблице из одной строки и из 100500 индекс даст одинаковое изменение быстродействия?
2 Lionelho
 
19.12.13
14:30
Полагаю что разное. Представьте что в таблице 100 строк.
3 Classic
 
19.12.13
14:34
(0)
В чем смысл индексировать левую таблицу? Тогда уж лучше только правую
А вообще для одного соединения индексить как-то некомильфо.
4 Fragster
 
гуру
19.12.13
14:35
для 100 вообще можно ничего не индексировать
5 Жан Пердежон
 
19.12.13
14:38
(3) смысл: надеятся, что сервер бд сам догадается запустить цикл по правой таблице
6 selenat
 
19.12.13
14:39
(0) 1.попробую сказать такую глупость. Индексирование может играть роль в плане ускорения при большом объеме данных. Индексировать маленькие таблицы нет нужды вообще. 2. если есть желание оптимизировать запрос, то всегда можно заменить левое соединение объединениям запросов с группировкой. По скорости этот вариант работает куда быстрее левого соединения.
7 Classic
 
19.12.13
14:40
(5)
А что потом? Добирать из левой все неприсоединенные?
Да и зачем в общем случае вообще цикл по правой делать?
например одна таблица содержит только

Номенклатура1   12
Номенклатура1   13
Номенклатура1   14
Номенклатура1   15

А правая - всю номенклатуру с ценами.
Зачем правую всю обходить то?
8 H A D G E H O G s
 
19.12.13
14:43
Я бы повесил индекс на большую таблицу с высокой селективностью поля.
И посмотрел профайлер.
9 Жан Пердежон
 
19.12.13
14:48
(7) а если наоборот? то очень даже имеет.
в общем случае - конечно нет, но статистика как раз не для общих случаев и ведется
10 H A D G E H O G s
 
19.12.13
14:50
А вообще - я бы не стал вещать индекс.
Если у тебя ВТ большого размера - значит ты криво данные выбрал уже, в 87.9 % случаях.
11 Classic
 
19.12.13
14:51
(9)
Что наоборот?
Левая таблица с ценами, правая маленькая?
Так в любом случае большую с ценами надо обойти полностью
12 Lionelho
 
19.12.13
14:59
H A D G E H O G s, а можешь по подробней рассказать про кривую выборку ВТ.
13 Fragster
 
гуру
19.12.13
15:01
(10) а если я из биллинга ТЗ гружу для обработки и формирования доков в 1с?
14 H A D G E H O G s
 
19.12.13
15:03
(13) Для вас я оставил 12.1%
15 selenat
 
19.12.13
15:03
Идея заменить левое соединение объединением во внутреннем запросе и группировкой всеми было проигнорировано. Я в печали. Мне этот прием всегда помогал решать проблемы быстродействия запроса, которые возникают при левом соединении. Ну да ладно.
16 H A D G E H O G s
 
19.12.13
15:07
(15) Идея внутреннного запроса - это че такое? Вложенный штоле?
17 selenat
 
19.12.13
15:08
(16) да. Объединение во вложенном и группировка результата этого самого вложенного.
18 H A D G E H O G s
 
19.12.13
15:14
(17) Бррр.
19 selenat
 
19.12.13
15:17
(18) ну, чуть сложнее в написании конечно, но по скорости - небо и земля по сравнению с левым соединением. Если стоИт задача оптимизации запроса на больших таблицах, я бы не задумываясь шел именно по этому пути.
20 H A D G E H O G s
 
19.12.13
15:17
(19) Ты какие-то странные вещи походу оптимизировал.
21 selenat
 
19.12.13
15:39
(20) ничуть. По-моему это из разряда "новое - это хорошо забытое старое". Я давно не появлялся на мисте, но научился этому приему помнится именно здесь. И с тех пор он меня ни разу не подводил.
22 Classic
 
19.12.13
15:43
(21)
Где-то видел этот метод. Но мне помнится, что он совсем не универсальный.
Максимальная размерность результата при соединении M*N, при объединении M + N, где M и N - размер таблиц
23 selenat
 
19.12.13
15:46
(22) ну речь ведь именно о больших таблицах. На малых особой разницы не будет видно, оба запроса будут отрабатывать достаточно быстро для того, чтобы не заморачиваться и писать такой текст запроса, который удобнее программисту.  А программисту конечно удобнее написать левое соединение.И вопрос об индексах на малых таблицах не стоИт.
24 Classic
 
19.12.13
15:49
(23)
Да я не про скорость и изящество. Просто не понимаю, как объединением получить результат идентичный соединению.

В примеру

ВЫБРАТЬ 1 КАК Поле
ПОМЕСТИТЬ ТЗ1
ОБЪЕДИНИТЬ ВЫБРАТЬ 2
ОБЪЕДИНИТЬ ВЫБРАТЬ 3
ОБЪЕДИНИТЬ ВЫБРАТЬ 4
ОБЪЕДИНИТЬ ВЫБРАТЬ 4
;
ВЫБРАТЬ 1 КАК Поле
ПОМЕСТИТЬ ТЗ2
ОБЪЕДИНИТЬ ВЫБРАТЬ 2
ОБЪЕДИНИТЬ ВЫБРАТЬ 3
ОБЪЕДИНИТЬ ВЫБРАТЬ 4
ОБЪЕДИНИТЬ ВЫБРАТЬ 4
;

ВЫБРАТЬ
   ТЗ1.Поле,
   ТЗ2.Поле
ИЗ ТЗ1 КАК ТЗ1
   ЛЕВОЕ СОЕДИНЕНИЕ ТЗ2 КАК ТЗ2
   ПО Истина


Вот как ты "объединением с группировкой" получишь идентичный результат?
25 selenat
 
19.12.13
15:50
(22) ты сейчас под результатом что понимаешь? Если готовую таблицу, то запрос составляется так, что она одинакова в обоих случаях.
26 Classic
 
19.12.13
15:51
(25)
Так покажи, как в примере (24) заменить левое соединение объединением
27 toypaul
 
гуру
19.12.13
15:52
(0) в левом соединении лучше индексировать правую таблицу. левую необязательно
28 DVN
 
19.12.13
15:56
(25)Пример в студию.. а то я здесь таблицы по 100-300к строк в запросе левым соединением соединяю.. хочу потестить производительность
29 selenat
 
19.12.13
16:25
Пардон, отлучался на поесть.

(26) ты прав, в общем случае это наверное не всегда возможно. Поэтому откорректирую свою позицию. Во всех случаях, которые мне встречались на практике, когда меня не удовлетворяла скорость запроса с левым соединением, мне удавалось найти вариант с объединением и группировкой, который давал тот же самый результат и работал в десятки раз быстрее.
30 selenat
 
19.12.13
17:27
Тем не менее, выложу самый элементарный пример, чтобы было понятно что вообще имеется в виду для тех, кто не знаком с этим приемом.
Вариант 1:

ВЫБРАТЬ
    РеализацияТоваровУслугТовары.Номенклатура,
    РеализацияТоваровУслугТовары.Количество,
    ЕСТЬNULL(ТоварыНаСкладахОстатки.КоличествоОстаток, 0) КАК Остаток
ИЗ
    Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
        ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.ТоварыНаСкладах.Остатки(, Номенклатура В (&СписокНоменклатуры)) КАК ТоварыНаСкладахОстатки
        ПО РеализацияТоваровУслугТовары.Номенклатура = ТоварыНаСкладахОстатки.Номенклатура
ГДЕ
    РеализацияТоваровУслугТовары.Ссылка = &Ссылка

Вариант 2:

ВЫБРАТЬ
    ВложенныйЗапрос.Номенклатура,
    СУММА(ВложенныйЗапрос.Количество) КАК Количество,
    СУММА(ВложенныйЗапрос.Остаток) КАК Остаток
ИЗ
    (ВЫБРАТЬ
        РеализацияТоваровУслугТовары.Номенклатура КАК Номенклатура,
        РеализацияТоваровУслугТовары.Количество КАК Количество,
        0 КАК Остаток
    ИЗ
        Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
    
    ОБЪЕДИНИТЬ ВСЕ
    
    ВЫБРАТЬ
        ТоварыНаСкладахОстатки.Номенклатура,
        0,
        ТоварыНаСкладахОстатки.КоличествоОстаток
    ИЗ
        РегистрНакопления.ТоварыНаСкладах.Остатки(, Номенклатура В (&СписокНоменклатуры)) КАК ТоварыНаСкладахОстатки) КАК ВложенныйЗапрос

СГРУППИРОВАТЬ ПО
    ВложенныйЗапрос.Номенклатура

Что-то в это роде. Я отдаю себе отчет в том, что запросы написаны небрежно, в частности что разные строки в документе реализация могут содержать одинаковую номенклатуру и надо бы для начала ее сгруппировать, но аккуратно написанный запрос будет бОльшего размера и сложнее восприниматься. Думаю, принцип понятен. Иногда в более сложных случаях приходится вводить в объединяемые запросы дополнительные служебные поля а потом при группировке результата фильтровать при помощи конструкции ИМЕЮЩИЕ. Обычно такие служебные поля делаются либо числовыми (тогда при группировке берется СУММА()), либо булевскими (при группировке используется МАКСИМУМ() или МИНИМУМ()).

В общем, если у кого-то есть проблемы со скоростью выполнения запроса с левым соединением, попробуйте построить для него аналог по такому принципу. Возможно результат вас приятно удивит своей скоростью.
31 selenat
 
19.12.13
17:28
+30 во втором варианте конечно тоже должно быть еще

ГДЕ
    РеализацияТоваровУслугТовары.Ссылка = &Ссылка
32 H A D G E H O G s
 
19.12.13
17:32
(30) Зачем тут Вложенный?
33 selenat
 
19.12.13
17:35
(32) не понял. Ты имеешь в виду зачем вообще второй вариант запроса, если можно использовать первый? Или ты хочешь предложить более простую версию второго варианта запроса?
34 H A D G E H O G s
 
19.12.13
17:41
(33) Все, понял зачем.

ЯБыСделалТак и даже не парился.


ВЫБРАТЬ
    РеализацияТоваровУслугТовары.Номенклатура,
    РеализацияТоваровУслугТовары.Количество
ПОМЕСТИТЬ Товары
ИЗ
    Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
ГДЕ
    РеализацияТоваровУслугТовары.Ссылка = &Ссылка
    И РеализацияТоваровУслугТовары.Номенклатура В(&СписокНоменклатур)
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    ТоварыНаСкладахОстатки.Номенклатура,
    ТоварыНаСкладахОстатки.КоличествоОстаток
ПОМЕСТИТЬ Остатки
ИЗ
    РегистрНакопления.ТоварыНаСкладах.Остатки(, Номенклатура В (&СписокНоменклатур)) КАК ТоварыНаСкладахОстатки
;

////////////////////////////////////////////////////////////////////////////////
ВЫБРАТЬ
    Товары.Номенклатура,
    Товары.Количество,
    ЕСТЬNULL(Остатки.КоличествоОстаток, 0) КАК Остаток
ИЗ
    Товары КАК Товары
        ЛЕВОЕ СОЕДИНЕНИЕ Остатки КАК Остатки
        ПО Товары.Номенклатура = Остатки.Номенклатура
35 selenat
 
19.12.13
17:44
(34) и в случае большого количества строк в документе реализации получил бы на порядок более медленный за счет левого соединения запрос, чем в варианте с объединением и группировкой. Потестируй.
36 H A D G E H O G s
 
19.12.13
17:53
(35) Мой будет более медленным, но не на порядки если твой вложенный так и остается, как есть, а не помещается еще куда-нибудь вглубь других вложенных.

Более медленным он будет только из за помещения данных во временные таблицы, но это все равно пару сотен миллисекунд.
37 selenat
 
19.12.13
17:57
(36) ты протестировал или просто предполагаешь, что это так? Штука в том, что за прошедшие года я таким приемом оптимизировал немало запросов, естественно с многочисленными замерами производительности. Нередко бывало, что писал запрос с левым соединением, потому что так проще писать, а потом когда видел, что не удовлетворяет меня полученная скорость, переписывал все по образцу второго примера.
38 selenat
 
19.12.13
18:02
Не изучал теоретическую сторону этого вопроса, но могу предположить, что причина кроется в том, что написано в (22):
"Максимальная размерность результата при соединении M*N, при объединении M + N, где M и N - размер таблиц".
Только речь не о размере результата (мы предполагаем, что результат нам удалось получить идентичным для обоиз вариантов запроса), а о количестве операций по выполнению запросов.
39 selenat
 
19.12.13
18:02
Всем пока, завтра можем продолжить интересный разговор. :)
40 Serginio1
 
19.12.13
18:08
(34) Тогда во втором запросе лучше добавить индекс по Номенклатура

В варианте с 30 Запрос будет использовать либо Хэш таблицу либо индекс в зависимости от размера. И твой вариант может быть значительно медленнее
41 Serginio1
 
19.12.13
18:34
(0) Есть алгоритм Джойнов который использует индексы.
Принцип его таков берем первые строки и сравниваем. Если они не равны то продвигаем наименьшую до тех пор пока не будет больше или равна. При равных двигаемся вперед по двум таблицам. По такому принципу работает сортровка слиянием wiki:%D1%EE%F0%F2%E8%F0%EE%E2%EA%E0_%F1%EB%E8%FF%ED%E8%E5%EC

При этом нет бинарного поиска и объединение быстрее. Например сортировка QuickSort значительно быстрее чем вставками. Но и сортировка слиянием тоже хороша.
42 DVN
 
20.12.13
07:54
(30) Спасибо, теперь понял о чем Вы говорили.. надо будет попробовать.. а то у меня есть запросик с промежуточными таблицами в >400к строк :)
43 Sammo
 
20.12.13
08:16
(30) У меня есть очень грустный опыт использования в () в качества параметра виртуальной таблицы.
В случае, когда данное условие накладывалось на измерение не первое/второе (3,4 или ниже), то иногда получалось быстрее это условие переместить в ГДЕ.
Особенно это было видно на неселктивных измерениях и измерениях с составным типом.
За пример спасибо, конечно.
44 selenat
 
20.12.13
08:48
Надо пожалуй пояснить еще кое-что по поводу примера в 30. По поводу параметра &СписокНоменклатуры. Я как-то не сказал, что чтобы запрос в результате дал именно левое соединение, в этом списке должна быть вся та же номенклатура, которая идет в самом документе реализации. На практике я бы вообще не использовал внешний параметр, а сделал бы по типу (34). Т.е. засунул бы состав реализации во временную таблицу (причем еще и сгруппировал бы ее по номенклатуре, а то у меня в примере отсутствие этой группировки является ошибкой), а в параметре виртуальной таблицы остатков использовал именно эту временную таблицу, а не параметр. Но это уже частности. Интересен другой вопрос. В более общем случае бывает, что присоединяемая таблица имеет записи, которые не должны попасть в итоговый результат. Для нашего случая предположим, что в параметре &СписокНоменклатуры есть элементы, которых нет в документе реализации. В этом случае запрос пишется примерно так:

ВЫБРАТЬ
    ВложенныйЗапрос.Номенклатура,
    СУММА(ВложенныйЗапрос.Количество) КАК Количество,
    СУММА(ВложенныйЗапрос.Остаток) КАК Остаток,
    СУММА(ВложенныйЗапрос.СлужебноеПоле) КАК СлужебноеПоле
ИЗ
    (ВЫБРАТЬ
        РеализацияТоваровУслугТовары.Номенклатура КАК Номенклатура,
        РеализацияТоваровУслугТовары.Количество КАК Количество,
        0 КАК Остаток,
        1 КАК СлужебноеПоле
    ИЗ
        Документ.РеализацияТоваровУслуг.Товары КАК РеализацияТоваровУслугТовары
    
    ОБЪЕДИНИТЬ ВСЕ
    
    ВЫБРАТЬ
        ТоварыНаСкладахОстатки.Номенклатура,
        0,
        ТоварыНаСкладахОстатки.КоличествоОстаток,
        0
    ИЗ
        РегистрНакопления.ТоварыНаСкладах.Остатки(, Номенклатура В (&СписокНоменклатуры)) КАК ТоварыНаСкладахОстатки) КАК ВложенныйЗапрос

СГРУППИРОВАТЬ ПО
    ВложенныйЗапрос.Номенклатура

ИМЕЮЩИЕ
    СУММА(ВложенныйЗапрос.СлужебноеПоле) > 0

Ну или в качестве служебного поля можно использовать булев тип, а при группировке не СУММА(), а МАКСИМУМ().
45 selenat
 
20.12.13
08:51
(43) у меня с параметрами виртуальной таблицы всегда отрабатывало быстрее, особенно если как в моем примере регистр остатков проиндексирован по номенклатуре. Но вообще можно это проверять в каждом конкретном случае. Метод ведь позволяет использовать его и с конструкцией ГДЕ вместо параметра виртуальной таблицы.
46 selenat
 
20.12.13
09:08
Выигрыш в скорости у такого метода сильно зависит от размера соединяемых таблиц. На маленьких таблицах разницы практически нет, при том что синтаксис левого соединения все-таки удобнее. На больших таблицах выигрыш может быть многократный. Один раз у меня был случай, когда обработкой корректировались старые данные в базе, когда таблицы были реально огромные. Так вот левое соединение просто повесило базу, через пару часов я устал ждать и просто прибил сеанс. А после переписывания по этому методу запрос прекрасно себе отработал за несколько минут. Вот, как-то так...
47 H A D G E H O G s
 
20.12.13
11:57
Померил я 3 метода в профайлере.
План запросов - одинаковый.
Единственное - с временными таблицами - медленней, потому что в них помещаются данные.

Возьмите профайлер.
48 H A D G E H O G s
 
20.12.13
12:04
1881 строк в документе, размер остаточной таблицы регистра - 3093.
49 H A D G E H O G s
 
20.12.13
12:05
(40) Нет. Никак нельзя добавлять регистр. Где вы видели РТУ больше 1000 строк?
50 H A D G E H O G s
 
20.12.13
12:05
(40) Нет. Никак нельзя добавлять индекс. Где вы видели РТУ больше 1000 строк?
51 selenat
 
20.12.13
12:17
(47) Странно это. Попробую проверить чуть позже по свободе на том примере, который я здесь приводил. Сейчас могу только сказать, что много раз решал таким способом проблемы и не склонен думать, что мне это все приснилось. :)
52 selenat
 
20.12.13
13:49
Еще не проверял, но пока ходил по делам появились мысли по этому поводу. Я так полагаю, что план запроса, который составляет 1С не всегда однозначно определяется только текстом запроса. Существуют дополнительные факторы, которые влияют на то, как 1С интерпретирует написанный нами запрос. В частности в нашем примере с левым соединением наверняка план запроса был бы другим, если бы поле номенклатуры было составным. Вполне вероятно, что существуют и другие факторы, которые влияют на это. И получается, что когда мы используем левое соединение, мы даем инструкции 1С в отношении того, какой результат хотим получить, но не контролируем оптимальность пути, которым 1С этого результата достигает. Нам может повезти и 1Ска выберет оптимальный путь, но может и не повезти. В варианте с объединением и группировкой мы даем инструкции не только относительно результирующей таблицы, но и относительно того, каким путем она должна быть получена. Т.е. мы даем 1Ске меньше произвола в отношении построения плана запроса. Правдоподобно звучит?
53 H A D G E H O G s
 
20.12.13
13:56
(52) Правдоподобно.
Поэтому, дабы совсем не было волностей у 1С - я все делаю через Временные таблицы, ибо фиг знает, как потом запрос переписать придется и куда ваш Вложенный запрос засунется.

Но ваш метод - неплох.
54 selenat
 
20.12.13
13:58
(48) кстати, не понятно как у тебя таблица остатков получилась больше таблицы остатков регистра. Я конечно понимаю, что у тебя там могут быть записи относящиеся к другим измерениям регистра, типа характеристики, серии и т.д. Но по логике примера в этом случае все это должно быть сгруппировано по номенклатуре. Т.е. количество строк в присоединяемой таблице обязано быть меньше или равно количеству различных элементов номенклатуры в документе.
55 selenat
 
20.12.13
13:59
(53) Насчет временных таблиц полностью солидарен. Я бы тоже на практике использовал их. Но это не противоречит тому, чтобы получив данные во временные таблицы, использовать их потом не в виде левого соединения, а в виде объединения и дальнейшей группировки.
56 selenat
 
20.12.13
14:00
В примере просто старался максимально упростить структуру запроса для наглядности идеи.
57 H A D G E H O G s
 
20.12.13
14:05
(54) Я имел ввиду всю таблицу остатков регистра, по всем измерениями.
58 selenat
 
20.12.13
14:06
(57) ок, не суть важно.
59 H A D G E H O G s
 
20.12.13
14:06
(55)

"Но это не противоречит тому, чтобы получив данные во временные таблицы, использовать их потом не в виде левого соединения, а в виде объединения и дальнейшей группировки."

Вот это тоже годно!