Имя: Пароль:
1C
1С v8
Можно ли оптимизировать данный запрос?
,
0 SherifSP
 
22.04.13
10:48
Конфа УПП, в типовом общем модуле в функции ОпределитьНаличиеДвиженийПоРегистратору(ДокументСсылка) есть запрос, который выполняется в цикле, как я помню это не оптимально, кто что думает по этому поводу?

Для Каждого Движение ИЗ МетаданнныеДокумента.Движения Цикл
       // в запросе получаем имена регистров, по которым есть хотя бы одно движение
       // например,
       // ВЫБРАТЬ Первые 1 «РегистрНакопления.ТоварыНаСкладах»
       // ИЗ РегистрНакопления.ТоварыНаСкладах
       // ГДЕ Регистратор = &Регистратор
       
       // имя регистра приводим к Строка(200), см. ниже
       ТекстЗапроса = ТекстЗапроса + "
       |" + ?(ТекстЗапроса = "", "", "ОБЪЕДИНИТЬ ВСЕ ") + "
       |ВЫБРАТЬ ПЕРВЫЕ 1 ВЫРАЗИТЬ(""" + Движение.ПолноеИмя()
       +  """ КАК Строка(200)) КАК Имя ИЗ " + Движение.ПолноеИмя()
       + " ГДЕ Регистратор = &Регистратор";
       
       // если в запрос попадает более 256 таблиц – разбиваем его на две части
       // (вариант документа с проведением по 512 регистрам считаем нежизненным)
       счетчик_таблиц = счетчик_таблиц + 1;
       Если счетчик_таблиц = 256 Тогда
           Прервать;
       КонецЕсли;
       
   КонецЦикла;
   
   Запрос = Новый Запрос(ТекстЗапроса);
   ЗАпрос.УстановитьПараметр("Регистратор", ДокументСсылка);
   // при выгрузке для колонки «Имя» тип устанавливается по самой длинной строке из запроса
   // при втором проходе по таблице новое имя может не «влезть», по этому сразу в запросе
   // приводится к строка(200)
   ТаблицаЗапроса = Запрос.Выполнить().Выгрузить();
1 Maxus43
 
22.04.13
10:51
Текст запроса собюирается в цикле, а не запрос в цикле
2 SherifSP
 
22.04.13
10:52
(1) Вижу, но все же, замер показал 2 секунды на данном запросе
3 Classic
 
22.04.13
10:53
(0)
Нормальный такой запрос.
Единственный его недостаток - если идет блокировка какой-то таблицы, то фиг вычислишь, что за таблица.
4 МихаилМ
 
22.04.13
10:53
ВЫБРАТЬ ПЕРВЫЕ без сортировки увольнять надо
для подобных проверок существует exist (в 1с в )

нечего отимизировать тк нет текста запроса.
5 Maxus43
 
22.04.13
10:53
(2) и причем тут замер? Как ещё ты определишь по каким регистрам есть движения у документа?
6 Maxus43
 
22.04.13
10:53
(4) Вы предлагаете уволить сотрудников 1с чтоли?
7 Галахад
 
гуру
22.04.13
10:54
(2) Ну две секунды. Много, что-ли? Или эти секунды в цикле?
8 Classic
 
22.04.13
10:55
(4)
Ну напиши текст запроса с учетом экзиста.
Посмотри как он будет выполняться
9 SherifSP
 
22.04.13
10:55
(5) Движения, не покатит?
10 Classic
 
22.04.13
10:56
(9)
Сразу доставать в память всю таблицу?
11 SherifSP
 
22.04.13
10:56
(7) Нет, но есть еще кусок, который выполняется 2 секунды, тоже типовой, это помимо мелких транзакций, думаю это не кошерно по времени
12 H A D G E H O G s
 
22.04.13
11:00
2  секунды - это огого на самом на деле.
13 Maxus43
 
22.04.13
11:04
(12) не то в консерватории что-то, у меня и тебя не 2 секунды же эта хрень
14 ДенисЧ
 
22.04.13
11:05
Натыкался я на этот запрос...
Там уже некуда, там идет clustered index seek - быстрее просто некуда...
15 Maxus43
 
22.04.13
11:05
(9) дольше будет, их надо ещё Прочитать() каждый чтоб понять есть ли там что. а запрос к движениям шустрей
16 Нуф-Нуф
 
22.04.13
11:08
получить объект по ссылке. получить движения. перебрать коллекцию движений.
17 Maxus43
 
22.04.13
11:08
(16) и что?
18 ДенисЧ
 
22.04.13
11:09
(16) Свободен...
19 ДенисЧ
 
22.04.13
11:09
(16) Берём документ РСВ. Читаем его движения (а их там 4 000 000). Сколько по времени? :-)
20 Classic
 
22.04.13
11:10
(16)
Для каждого движения делать неявный запрос к базе, доставать всю таблицу движения в память, получать первую строку. Жесть
21 Нуф-Нуф
 
22.04.13
11:11
(19) если у тебя есть такой документ - то сделай ради теста - СсылкаРСВ.ПолучитьОбъект().Движения
22 ДенисЧ
 
22.04.13
11:11
(21) .Прочитать - около минуты. Дальше?
23 ДенисЧ
 
22.04.13
11:11
И то, если не вылетит на аут оф мемори.
24 Maxus43
 
22.04.13
11:12
(21) движения надо Прочитать ещё
25 Нуф-Нуф
 
22.04.13
11:13
(22) причем здесь прочитать?
26 Classic
 
22.04.13
11:15
(25)
Запрос из (0) получает по каким регистрам были движения.
СсылкаРСВ.ПолучитьОбъект().Движения - это список всех регистров, по которым МОГЛИ БЫТЬ движения
27 H A D G E H O G s
 
22.04.13
11:15
(25) Притом.
28 Maxus43
 
22.04.13
11:15
(25) цель (0) - понять если ли движения у документа. Если док двигает РН, это не значит что там что-то записано
29 Нуф-Нуф
 
22.04.13
11:19
все. посыпал голову пеплом и ушел.
30 Fragster
 
гуру
22.04.13
11:22
этот кусок из типовой выпиливать надо нафиг
31 Classic
 
22.04.13
11:23
(30)
И что делать? Это обычно перед удалением движений выполняется
32 Fragster
 
гуру
22.04.13
11:23
вообще, емнип, в данном куске была еще запись пустых наборов при обходе
33 Fragster
 
гуру
22.04.13
11:24
(31) ЕМНИП, тормозил (32). а чтобы не тормозил (0) - надо не забывать делать регламентное обслуживание базы, переиндексирование там, реструктуризацию (это если физически возможно)
34 Classic
 
22.04.13
11:27
(33)
А что план запроса по этому поводу говорит, там скан идет по всем движениям документа, или по одному движению с каждого регистра?
35 Maxus43
 
22.04.13
11:28
(32) в этом куске у меня нет записи пустых, вообще он есть дальше, если движения нашлись. А как ещё ты удалишь движения не записывая пустой набор?)
36 Fragster
 
гуру
22.04.13
11:38
(34) кластеред индекс сик должен быть
(35) при перепроведении зачем удалять? надо перезаписывать просто...
37 ДенисЧ
 
22.04.13
11:38
(34) см (14)...
38 Maxus43
 
22.04.13
11:40
(36) дак не выпиливать надо его тогда уж, а допиливать
39 Maxus43
 
22.04.13
11:41
(38)+ да и то не факт. Меняешь операцию например - движения по другим регистрам пойдут, в процедуры "перезаписи" старых регистров и не зайдёт. Так что как бы... пусть так лучше, меньше возможных ошибок
40 Classic
 
22.04.13
11:43
(36)
"при перепроведении зачем удалять? надо перезаписывать просто..."
Тогда проведение надо делать гарантированно по всем движениям. Большая такая ловушка для невнимательных получается.
41 Fragster
 
гуру
22.04.13
11:43
(39) специально флаг "записывать" сделали
42 Maxus43
 
22.04.13
11:44
(41) какой флаг? Как ты определишь ЧТО надо перезаписать? Всё?
43 notton
 
22.04.13
11:45
(0) распараллелить удаление фоновыми заданиями )
44 Широкий
 
22.04.13
11:45
(4) "ВЫБРАТЬ ПЕРВЫЕ без сортировки увольнять надо "
Поясни
45 Classic
 
22.04.13
11:46
(43)
Это часть обработки проведения. Распаралелить проведение?
46 notton
 
22.04.13
11:48
(45) так тоже можно )
47 Maxus43
 
22.04.13
11:50
(46) дай бог чтоб успело удалится фоновым, пока туда сам док ничо не записал :)
48 Classic
 
22.04.13
11:51
(46)
И че с транзакциями будет?
49 H A D G E H O G s
 
22.04.13
11:52
(47) Я делал удаление в фоновых. Упиралось в скорость записи SQL сервера (вероятнее всего оно) и выигрыша было 0.
50 H A D G E H O G s
 
22.04.13
11:53
(44) Чтобы МихаилМ что-то пояснил из своего потока сознания? Да вы верно шутите!
51 Maxus43
 
22.04.13
11:53
(48) фоновые можно запустить в рамках одной транзакции, видел реализацию
52 Fragster
 
гуру
22.04.13
11:57
(42) запрос из (0) не должен тормозить (если база обслужена нормально). Как правило тормозит удаление наборов. Так вот, вместо удаления - у соответствующих наборов в Движениях" можно выставить флаг "записывать", тогда они при записи сами запишутся. Если они не были изменены - пустыми наборами, если были - то только 1 раз уже заполненными новыми записями.
53 Fragster
 
гуру
22.04.13
11:59
(49) а ты тест мой делал? по регистрам рост был с ростом количества потоков?
54 Широкий
 
22.04.13
11:59
(0) Я вижу в запросе только один недочет - это блокировка на чтение сразу всех таблиц вошедших в запрос.
ИМХО лучше бы крутить в цикле по одному регистру.
55 Fragster
 
гуру
22.04.13
12:00
(54) там по диапазону индекса все равно блокировка
56 Широкий
 
22.04.13
12:01
+54 Мне даже кажется что дольше будет обращения к метаданным , чем регистру
"Движение.ПолноеИмя()"
57 notton
 
22.04.13
12:02
(0) на пакет можно еще переделать, возможно это уменьшит блокировки
58 Широкий
 
22.04.13
12:02
(55) Но блокировка то все равно на все таблицы будет
59 H A D G E H O G s
 
22.04.13
12:03
(53) Нет, не делал. Последнее время - некогда.
60 Maxus43
 
22.04.13
12:03
(52) пробовал? этим флагом не играл..
61 Classic
 
22.04.13
12:04
(52)
А где этот флаг?
(54)
Не на чтение, а на запись. Почему это минус?
62 H A D G E H O G s
 
22.04.13
12:05
(56) Да ладно. Эти метаданные до этого уже раз 100 запрашивались, все давно уже в кэше.
63 Широкий
 
22.04.13
12:10
А хотя наверно пофиг.. Он итоги не блокирует.
Читает только свои записи.
64 Fragster
 
гуру
22.04.13
12:12
(61)

РегистрНакопленияНаборЗаписей.<Имя регистра накопления>.Записывать (AccumulationRegisterRecordSet.<Имя регистра накопления>.Write)
РегистрНакопленияНаборЗаписей.<Имя регистра накопления> (AccumulationRegisterRecordSet.<Имя регистра накопления>)
Записывать (Write)
Использование:

Чтение и запись.
Описание:

Тип: Булево.
Ложь - не происходит записи набора в информационную базу при вызове Записать коллекции движений документа, которой принадлежит набор, а также при стандартной обработке проведения документа, если значение свойства метаданного документа "Запись движений при проведении" в Конфигураторе выставлено в "Записывать выбранные".

Доступность:

Сервер, толстый клиент, внешнее соединение.
65 Широкий
 
22.04.13
12:12
Вот это не понятно
Если счетчик_таблиц = 256 Тогда
  Прервать;
КонецЕсли;

1. Ограничение вроде как убрали
2. Что за дикий документ по 256 регистрам проходит.
3. Как посчитана РЛС
66 hhhh
 
22.04.13
12:12
(0) это древний кусок дерьма. Он был в БП 1.6 и соответственно в УПП. В БП 2.0 и в 3.0 естественно всё оптимизировали. Проверяют только те регистры, которые надо проверять.

   Если ВыборочноОчищатьРегистры Тогда
       СписокРегистровДляОчисткиДвижений = Новый Массив;
       СписокРегистровДляОчисткиДвижений.Добавить(Тип("РегистрНакопленияНаборЗаписей.РасходыПриУСН"));
   КонецЕсли;
   
   //Очистка движений документа
   Для Каждого Движение ИЗ ДокументОбъект.Движения Цикл
       
       Если ВыборочноОчищатьРегистры И (СписокРегистровДляОчисткиДвижений.Найти(ТипЗнч(Движение))<>неопределено) Тогда
           Продолжить;
       КонецЕсли;
       Движение.Очистить();
       
   КонецЦикла;
   
   //Запись пустых наборов движений в ИБ(очистка старых движений)    
   Для Каждого Движение ИЗ ДокументОбъект.Движения Цикл
       
       Если (ВыборочноОчищатьРегистры И (СписокРегистровДляОчисткиДвижений.Найти(ТипЗнч(Движение))<>неопределено))
           ИЛИ НЕ ВыборочноОчищатьРегистры Тогда
           
           Если Движение.Количество() > 0 Тогда
               ПозицияТочки = Найти(Строка(Движение), ".");
               ТипРегистра = Лев(Строка(Движение), ПозицияТочки - 13);
               ИмяРегистра = СокрП(Сред(Строка(Движение), ПозицияТочки + 1));
               
               ЕСли ТипРегистра = "РегистрНакопления" Тогда
                   МетаданныеНабора = Метаданные.РегистрыНакопления[ИмяРегистра];
                   Набор = РегистрыНакопления[ИмяРегистра].СоздатьНаборЗаписей();
                   
               ИначеЕсли ТипРегистра = "РегистрБухгалтерии" Тогда
                   МетаданныеНабора = Метаданные.РегистрыБухгалтерии[ИмяРегистра];
                   Набор = РегистрыБухгалтерии[ИмяРегистра].СоздатьНаборЗаписей();
                   
               ИначеЕсли ТипРегистра = "РегистрСведений" Тогда
                   МетаданныеНабора = Метаданные.РегистрыСведений[ИмяРегистра];
                   Набор = РегистрыСведений[ИмяРегистра].СоздатьНаборЗаписей();
                   
               ИначеЕсли ТипРегистра = "РегистрРасчета" Тогда
                   МетаданныеНабора = Метаданные.РегистрыРасчета[ИмяРегистра];
                   Набор = РегистрыРасчета[ИмяРегистра].СоздатьНаборЗаписей();
                   
               КонецЕсли;
               
               Если НЕ ПравоДоступа("Изменение", МетаданныеНабора) Тогда
                   // отсутствуют права на всю таблицу регистра
                   СообщитьОбОшибке("Нарушение прав доступа", Отказ, Строка(Движение));
                   Возврат;
               КонецЕсли;
               
               Набор.Отбор.Регистратор.Установить(ДокументОбъект.Ссылка);            
               
           Иначе
               Набор = Движение;
           КонецЕсли;
           
           Попытка
               Набор.Записать();
           Исключение
               // возможно «сработал» RLS или механизм даты запрета изменения
               СообщитьОбОшибке(ОписаниеОшибки(), Отказ, Набор);
               ВызватьИсключение "Операция не выполнена";
           КонецПопытки;
       КонецЕсли;
       
   КонецЦикла;
67 Fragster
 
гуру
22.04.13
12:13
(66) все равно нафиг
68 Fragster
 
гуру
22.04.13
12:14
единственный плюс - в режиме полных прав запись идет
69 Fragster
 
гуру
22.04.13
12:14
но при перепроведении - запись идет два раза
70 Fragster
 
гуру
22.04.13
12:15
с соответствующим пересчетом итогов
71 Широкий
 
22.04.13
12:15
(69) Штатно она и так два раза идет
72 Maxus43
 
22.04.13
12:15
(66) а как это вот формирутеся?

  Если ВыборочноОчищатьРегистры Тогда
       СписокРегистровДляОчисткиДвижений = Новый Массив;
       СписокРегистровДляОчисткиДвижений.Добавить(Тип("РегистрНакопленияНаборЗаписей.РасходыПриУСН"));
   КонецЕсли;


жёско прописано в каждом документе?
73 Fragster
 
гуру
22.04.13
12:16
(71) я и говорю - надо выпиливать
74 Maxus43
 
22.04.13
12:21
(73) слишком много переделывать, чтоб заюзать Записывать.
Переделывать везде причем. 1с не пойдёт на это, вставит (66) в УПП 2.0 скорей всего, иль чо там щас, в тестовой?
75 Fragster
 
гуру
22.04.13
12:21
(74) так ведь все равно копро и двойная запись. а перепиливать можно частично - начиная по самым проблемным местам...
76 Maxus43
 
22.04.13
12:24
(75) как изменили в УПП 2.0 ознакомительной - так и будет, возможно посчитали (и не без оснований), что в данном случае выхлопа будет слишком мало, исходя из объёма работ. хз
77 notton
 
22.04.13
12:25
(74) уп 2.0

))

// Функция формирует массив имен регистров, по которым документ имеет движения.
// Вызывается при подготовке записей к регистрации движений.
//
Функция ПолучитьМассивИспользуемыхРегистров(Регистратор, Движения, МассивИсключаемыхРегистров = Неопределено) Экспорт

   Запрос = Новый Запрос;
   Запрос.УстановитьПараметр("Регистратор", Регистратор);

   Результат = Новый Массив;
   МаксимумТаблицВЗапросе = 256;

   СчетчикТаблиц   = 0;
   СчетчикДвижений = 0;

   ВсегоДвижений = Движения.Количество();
   ТекстЗапроса  = "";
   Для Каждого Движение Из Движения Цикл

       СчетчикДвижений = СчетчикДвижений + 1;

       ПропуститьРегистр = МассивИсключаемыхРегистров <> Неопределено
                           И МассивИсключаемыхРегистров.Найти(Движение.Имя) <> Неопределено;

       Если Не ПропуститьРегистр Тогда

           Если СчетчикТаблиц > 0 Тогда

               ТекстЗапроса = ТекстЗапроса + "
               |ОБЪЕДИНИТЬ ВСЕ
               |";

           КонецЕсли;

           СчетчикТаблиц = СчетчикТаблиц + 1;

           ТекстЗапроса = ТекстЗапроса +
           "
           |ВЫБРАТЬ ПЕРВЫЕ 1
           |""" + Движение.Имя + """ КАК ИмяРегистра
           |
           |ИЗ " + Движение.ПолноеИмя() + "
           |
           |ГДЕ Регистратор = &Регистратор
           |";

       КонецЕсли;

       Если СчетчикТаблиц = МаксимумТаблицВЗапросе Или СчетчикДвижений = ВсегоДвижений Тогда

           Запрос.Текст  = ТекстЗапроса;
           ТекстЗапроса  = "";
           СчетчикТаблиц = 0;

           Если Результат.Количество() = 0 Тогда

               Результат = Запрос.Выполнить().Выгрузить().ВыгрузитьКолонку("ИмяРегистра");

           Иначе

               Выборка = Запрос.Выполнить().Выбрать();
               Пока Выборка.Следующий() Цикл
                   Результат.Добавить(Выборка.ИмяРегистра);
               КонецЦикла;

           КонецЕсли;
       КонецЕсли;
   КонецЦикла;

   Возврат Результат;

КонецФункции
78 Лефмихалыч
 
22.04.13
12:27
Может попробовать использовать соединение вместо объединения?
ВЫБРАТЬ КОЛИЧЕСТВО(рег1.Регистратор) как Рег1, КОЛИЧЕСТВО(Рег2.Регистратор) КАК Рег 2
ИЗ
   РегистрНакопления.Рег1,
   РегистрНакопления.Рег2
ГДЕ
   Рег1.Регистратор = &Регистратор
И     Рег2.Регистратор = &Регистратор
?..
Ну и там поиграться с разными агрегатными функциями. Как минимум тут не будет уходить время на преобразования строк к строкам
79 Лефмихалыч
 
22.04.13
12:28
+(78)  хотя надо еще понять, что сервер 1С с этим запросом сделает, но субъективно это должно быть легче, чем "ПЕРВЫЕ 1" и "объединить ВСЕ"
80 notton
 
22.04.13
12:29
(79) или пакет запросов? (57)
81 Лефмихалыч
 
22.04.13
12:31
(80) скорее "И", а не "ИЛИ"
82 Maxus43
 
22.04.13
12:34
(77) ну значит ничо не изменилось)
83 Fragster
 
гуру
22.04.13
12:35
(82) ну почему же... записи наборов нету, возврат имен регистров...
84 Fragster
 
гуру
22.04.13
12:35
(83)+ надо смотреть дальше
85 Maxus43
 
22.04.13
12:36
(83) у меня и щас так, у тебя неправильная УПП, я ж писал что запись не тут
86 Maxus43
 
22.04.13
12:37
у меня 1.2.27 древняя
87 Fragster
 
гуру
22.04.13
12:38
(85) у меня не УПП, код тот же, только запись в той же функции (ну и название немного другое).