Имя: Пароль:
1C
1C 7.7
v7: Прямые запросы - связь между реквизитами типа "Справочник" и "Справочник.X"
0 Chai Nic
 
04.10.13
08:39
Как сделать left join с каким-то конкретным справочником, если в основной таблице поле имеет тип "Справочник" (общий)? В документации по 1с++ об этом ни слова, там только про дополнительную типизацию параметров запроса с помощью "~" в этом контексте... пробовал применять "~" при указании условия связи - выдает ошибку.
1 ADirks
 
04.10.13
08:55
FROM
   ...  A
   left join $Справочник.ХХХ Б ON Left(А.Рекв, 4) = $ВидСправочника36.ХХХ AND  ХХХ.ID = Right(А.Рекв, 9)
2 Chai Nic
 
04.10.13
09:00
(1) Спасибо! Только наверное с точки зрения быстродействия это не слишком оптимально будет :( Выражения с получением подстроки в условии связи не дадут применить индекс..
3 ADirks
 
04.10.13
09:05
(2) именно так
поэтому мы делаем (там где актуально) искуственный рекв. ид13 с отбором, и триггером его заполняем
4 ADirks
 
04.10.13
09:06
хотя можно конечно и в ПриЗаписи() написать

ид13 = ТекущийЭлемент();
5 Chai Nic
 
04.10.13
09:48
(3) Вот почему 1с так сама не сделала.. Вместо дурацкого составного реквизита могли бы в базе хранить два реквизита - описание типа и собственно ссылку.
6 viktor_vv
 
04.10.13
09:57
C Left() есть шанс что будет использован индекс, хотя бы отберет по виду.
7 ADirks
 
04.10.13
10:05
(5) не, на самом деле было бы удобнее, чтобы все ИД поля были ид13 - никакой путаницы, и все ИДшники в рамках базы уникальны.  Но как обычно - экономим на спичках, просираем сотни нефти.
8 Salimbek
 
04.10.13
10:15
left join $Справочник.ХХХ Б ON А.Рекв = $ВидСправочника36.ХХХ+Б.ID
9 Mikeware
 
04.10.13
10:18
(5)1с в 1998 и представить в страшном сне не могла, что ее структуру так расковыряют....
Нуралиев же отвечал на просьбу отдать 7.7. в свободное плаванье, что не будет, ибо не дурак делать себе конкурента (а 7.7 с доработками вполне себе на уровне снеговика.)
10 Mikeware
 
04.10.13
10:19
(7)тогда эти "спички" были нелишние... скажи мне в те годы, что вполне реальна база коммерческой компании в 100Г - я б драться полез...
11 ADirks
 
04.10.13
10:28
(10) ну вообще да, всё-таки ДБФ не всякие вольности позволяет.

(8) вот я специально этот вариант не стал приводить, обычно он затратнее.
12 Chai Nic
 
04.10.13
10:57
(7) Да, наверное так было бы лучше. Правда, по сравнению со способом хранения периодических реквизитов это мелочь. Вот там кошмар полный.. для производственной базы с ценами материалов, нормировкой техопераций, вариантами техпроцесса эта таблица раздувается просто неприлично, доступ к данным при этом значительно замедляется. Что мешало периодические реквизиты конкретного справочника хранить в отдельной таблице? Возможность получить все возможные периодические реквизиты на заданную дату одним запросом, разве что.. а нафига?
13 Salimbek
 
04.10.13
11:01
(11) Ну так то возможно. Только (обычно) справочники не очень большие и поиск пролетает за доли секунды. Если же справочник большой... Тогда, типа:
SELECT $ВидСправочника36.ХХХ+Б.ID as ID13, Б.ID as ID (плюс еще какие нужно поля) INTO #tmp FROM $Справочник.ХХХ as Б
затем
CREATE INDEX idx_id13 (ID13)
ну и
left join #tmp Б ON А.Рекв = Б.ID13
14 Ёпрст
 
04.10.13
11:02
(12) не.. 1сконст это цветочки, вот блоб в дбф это нечто.
15 trad
 
04.10.13
11:05
(2),(3) будьте спокойны, в (1) ни один индекс не пострадает.
Там отлично отработает индекс - IDD

и в плане скорости совершенно пофиг
Б.ID = Right(А.Рекв, 9)
или
Б.ID36 = А.Рекв
за исключением микроскопических долей процента на операцию right
16 trad
 
04.10.13
11:06
а вот в (8) - вредительство, тут точно индекс работать не будет
17 Mikeware
 
04.10.13
11:09
(14) блоб - аналог мемополей.
----
а вообще, интерсно - спустя 15 от создания (при среднем сроке эксплуатации софта 5 лет) мы обсуждаем архитектуру....
18 Chai Nic
 
04.10.13
11:18
(17) С блобами вообще непонятно.. FoxPro поддерживал блобы штатно, что мешало воспользоваться их технологией, а не изобретать велосипед?
19 Salimbek
 
04.10.13
11:19
(16) Хм, буду знать, почему-то считал что так выгоднее... (ушел в печали)
20 viktor_vv
 
04.10.13
11:20
(15) Насчет микроскопической доли на Right() поспорил бы.
Я правда на Журнале проверял по общему реквизиту с отбором. Так плана

Select
    ДокЗаявкаШапка.IDDOC as ДокЗаявка ,
    Ж.Date_Time_IDDOC as ДатаДокОсн
From
    dh4319 as ДокЗаявкаШапка (nolock)
    Left join _1Sjourn as Ж (nolock)
        on
                 Left(Ж.sp4304, 4) = ' 3BZ'
                 and
            ДокЗаявкаШапка.IDDOC = Right(Ж.sp4304,9)

Left(Ж.sp4304, 4) = ' 3BZ' - index scan с условием coast 60%
            ДокЗаявкаШапка.IDDOC = Right(Ж.sp4304,9) - compute scalar стоимость 20%.

остальные варианты примерно одинаково отрабатывают, тоько распределение стоимости по операциям меняется.
21 viktor_vv
 
04.10.13
11:22
(20)+ Хотя если на чистую выполнять, то это пожалуй самый быстрый вариант получается.
22 viktor_vv
 
04.10.13
11:26
А такой вариант

Select
    ДокЗаявкаШапка.IDDOC as ДокЗаявка ,
    Ж.Date_Time_IDDOC as ДатаДокОсн
From
    dh4319 as ДокЗаявкаШапка (nolock)
    Left join _1Sjourn as Ж (nolock)
        on
                -- Left(Ж.sp4304, 4) = ' 3BZ'
                -- and
            ДокЗаявкаШапка.IDDOC = Right(Ж.sp4304,9)

index scan без условия - 30%
compute scalar - 5%
зато уже на hash match - 55%
23 trad
 
04.10.13
11:30
(20) эээ, нет
у тебя же совсем не то что в (1)
в (1) написано Б.ID = Right(А.Рекв, 9)
а у тебя А.ID = Right(Б.ID,9)
24 trad
 
04.10.13
11:32
по поводу right.
тратим время на right, зато сравниваем char(9)
не тратим время на right, зато сравниваем char(13)
хотя, я говорю, не стоит на этом заморачиваться
25 viktor_vv
 
04.10.13
11:34
(23) Таки да :).
26 trad
 
04.10.13
11:34
в (1) не будет никаких index scan
там будет православный index seek в loop join'е
27 viktor_vv
 
04.10.13
11:43
(26) Нету там index seek.

Select
    ДокЗаявкаШапка.IDDOC as ДокЗаявка ,
    Ж.Date_Time_IDDOC as ДатаДокОсн
From
     _1Sjourn as Ж (nolock)
    Left join dh4319 as ДокЗаявкаШапка (nolock)
--    Left
        on
                 Left(Ж.sp4304, 4) = ' 3BZ'
               and
            ДокЗаявкаШапка.IDDOC = Right(Ж.sp4304,9)

index scan по индексу журнала, причем полный, без условия. Условие используется уже аж в hash match.
28 viktor_vv
 
04.10.13
11:46
(27)+ Насчет нету, это достаточно условно, конкрентно в приведенном примере, на моих данных.
29 Ёпрст
 
04.10.13
11:47
(27) дык (23)
30 Джордж1
 
04.10.13
11:50
(12)а ведь есть способ эмулирования периодических реквизитов на регистрах
31 trad
 
04.10.13
11:59
(29) ну (27) он привел к виду (1)
32 ADirks
 
04.10.13
12:18
Ну прям интересно стало  :))

4 варианта:

-- 1
select
    спр.ID
FROM
    регПартииИтоги р
    left join спрМатериалы спр ON  ' 1EH'+спр.ID = р.МПЗ
WHERE
    р.Period = '20130101'

-- 2
select
    спр.ID
FROM
    регПартииИтоги р
    left join спрМатериалы спр ON  left(р.МПЗ, 4)=' 1EH' and спр.ID = Right(р.МПЗ, 9)
WHERE
    р.Period = '20130101'

-- 3
select
    спр.ID
FROM
    регПартииИтоги р
    left join спрМатериалы спр ON спр.ид13 = р.МПЗ
WHERE
    р.Period = '20130101'

-- 4
select
    спр.ID
FROM
    регПартииИтоги р
    left join спрМатериалы спр ON спр.ид13 = р.МПЗ
WHERE
    р.Period = '20130101'
    and left(р.МПЗ, 4)=' 1EH'


Самый незатратный - №2. Даже читерский вариант 4 - затратнее.
Процентики:
1 - 23
2 - 20
3 - 31 - clustered index scan
4 - 26 - аналогично

А вот если в селект-листе вместо ID получать ROW_ID - то №4 выигрывает, потому что начинает использовать индекс VIA, а №2 и №3 одинаковы по времени.
Так что, в индекс попасть не всегда так просто как кажется. Надо поправку на ветер и плотность воздуха учитывать!
33 trad
 
04.10.13
12:23
(27) да, подтверждаю, index scan может быть

Зависит от размера таблицы Б
если она небольшая, то seek+loop
если большая, то skan+(hash||merge)
34 trad
 
04.10.13
12:28
(32) 2 и 4 логически же не равны

А во втором убери left(р.МПЗ, 4)=' 1EH' тоже в where
35 viktor_vv
 
04.10.13
12:28
(33) Да. У меня соотношение количества записей таблица Б к таблице А в районе 10%, это уже достаточно много для seek, поэтому и делает scan и hash.
36 ADirks
 
04.10.13
12:35
(34) не-не, я же говорю, что №4 - читерство. Надо же все записи получать, а не только по одному справочнику.

Сравнивать надо 2 и 3 - и тут результаты очень близки, как по времени, так и по использованию индексов - просто индексы разные.
37 trad
 
04.10.13
12:36
(35) + при этом если его принудить к loop, то выполняется быстрее примерно в 2 раза и количество чтений страниц меньше на порядок (на моих данных)
38 viktor_vv
 
04.10.13
12:40
(37) Да я вот тоже замечал, что hash тормозной, хотя его описывают как быстрый, наверное накладные расходы на создание hash-таблиц большие.
39 trad
 
04.10.13
12:45
(38) просто для hash нужны обе таблицы в полном составе, т.е. и таблица Б должна быть полностью прочитана перед связыванием,
а для loop "выдергиваются" только записи по условию связывания по мере обхода таблицы А и он (loop) очень подходит при "выдергивании" индекс-сиком
40 Ёпрст
 
04.10.13
12:59
(39) не напомнишь, tabledoc может управлять РВД на форме ?
41 Salimbek
 
04.10.13
13:05
(32) Я дико извиняюсь, но можно попробовать такой вариант, чисто для любопытства:

select
    спр.ID
FROM
    регПартииИтоги р
    left join спрМатериалы спр ON  left(р.МПЗ, 4)=' 1EH' and ' 1EH'+спр.ID = р.МПЗ
WHERE
    р.Period = '20130101'
42 trad
 
04.10.13
13:09
(40) не пробовал даже :)
43 trad
 
04.10.13
13:12
(42)+ попробовал, не может
44 viktor_vv
 
04.10.13
13:21
(41) Попробовал похожий вариант.

Select
    ДокЗаявкаШапка.IDDOC as ДокЗаявка ,
    Ж.Date_Time_IDDOC as ДатаДокОсн
From
     _1Sjourn as Ж (nolock)
    Left join dh4319 as ДокЗаявкаШапка (nolock)
--    Left
        on
                 Left(Ж.sp4304, 4) = ' 3BZ'
               and
            ' 3BZ'+ДокЗаявкаШапка.IDDOC = Ж.sp4304

по сравнению с ДокЗаявкаШапка.IDDOC = Right(Ж.sp4304,9) уменьшилась стоимость для compute scalar ' 3BZ'+ДокЗаявкаШапка.IDDOC .

Оно логично, так как compute scalar для Right(Ж.sp4304,9) выполняется на большей таблице, а для 3BZ'+ДокЗаявкаШапка.IDDOC на меньшей.
45 ADirks
 
04.10.13
13:23
(41)
-- 1
select
    М.ROW_ID
FROM
    регПартииИтоги р
    left join спрМатериалы М ON  left(р.МПЗ, 4)=' 1EH' and М.ID = Right(р.МПЗ, 9)
WHERE
    р.Period = '20130101'

-- 2
select
    спр.ID
FROM
    регПартииИтоги р

    left join спрМатериалы спр ON  left(р.МПЗ, 4)=' 1EH' and ' 1EH'+спр.ID = р.МПЗ
WHERE
    р.Period = '20130101'


№1 - 49%
№2 - 51%

разница в основном из-за ' 1EH'+спр.ID - ещё один compute scalar, и дольнейший hash join получается 61% против 59%
46 viktor_vv
 
04.10.13
13:29
(45) У меня по 50% процентов получились запросы.
47 Salimbek
 
04.10.13
14:16
(45) Любопытно, т.е. фактическая стоимость операций практически одинакова, и далее, как указали в (44) выгоднее смотреть, какая из таблиц меньше и в зависимости от этого использовать либо тот либо другой вариант?
48 viktor_vv
 
04.10.13
14:41
(47) Не совсем так. У меня получилось перераспределние стоимости между compute scalar и hash match.
Но в итоге запросы оказались по времени одинаковые. При выполнении двух запросов пакетом, у них стоимость каждого в пакете оказалась одинаковая, по 50%.
49 ADirks
 
04.10.13
15:00
А вот более жизненный вариант - джойнятся все справочники, которые м.б. в измерении

-- 1
select
    М.ROW_ID,
    Н.ROW_ID
FROM
    регПартииИтоги р
    left join спрМатериалы М ON  ' 1EH'+М.ID = р.МПЗ
    left join спрНоменклатура Н ON  ' 35L'+Н.ID = р.МПЗ
WHERE
    р.Period = '20130101'
-- 2
select
    М.ROW_ID,
    Н.ROW_ID
FROM
    регПартииИтоги р
    left join спрМатериалы М ON  left(р.МПЗ, 4)=' 1EH' and М.ID = Right(р.МПЗ, 9)
    left join спрНоменклатура Н ON  left(р.МПЗ, 4)= ' 35L' and Н.ID = Right(р.МПЗ, 9)
WHERE
    р.Period = '20130101'

--3
select
    М.ROW_ID,
    Н.ROW_ID
FROM
    регПартииИтоги р
    left join спрМатериалы М ON М.ид13 = р.МПЗ
    left join спрНоменклатура Н ON  Н.ид13 = р.МПЗ
WHERE
    р.Period = '20130101'


Процентики
1 - 67%
2 - 17%
3 - 17%
для №1 строится сильно другой план, затраты на который сильно больше
50 ADirks
 
04.10.13
15:03
ой, в №1 забыл  условие по left(р.МПЗ, 4)= ' 35L'
так получается 52, 22, 22
Проблемы невозможно решaть нa том же уровне компетентности, нa котором они возникaют. Альберт Эйнштейн