Имя: Пароль:
1C
1C 7.7
v7: Как получить остатки на каждый день прямым запросом
0 perkos
 
21.03.13
10:35
ТЗ_Результ=СоздатьОбъект("ТаблицаЗначений");
   
   Мета=СоздатьОбъект("MetaDataWork");
   
   ЗапросSQL = 0;
   ЗапросSQL = СоздатьОбъект("ODBCRecordSet");
   ЗапросSQL.УстБД1С();  
   
   ЗапросSQL.УстановитьТекстовыйПараметр("ВыбНачПериода",ВыбНачПериода);
   ЗапросSQL.УстановитьТекстовыйПараметр("ВыбКонПериода",ВыбКонПериода);
   
   ТекстЗапроса = "
   |SELECT
   |Остатки.Период AS Период,
   |Остатки.Номенклатура AS [Товар $Справочник.Номенклатура],
   |Остатки.КоличествоНачальныйОстаток AS Количество
   |FROM $РегистрОстаткиОбороты.ОстаткиТМЦ(:ВыбНачПериода,:ВыбКонПериода~,День,ДвиженияИГраницыПериода,,(Склад IN (SELECT Val FROM #СписокС)) AND (Номенклатура IN (SELECT Val FROM #СписокН)),(Номенклатура),(Количество)) AS Остатки
   |ORDER BY Остатки.Период,Остатки.Номенклатура
   |";                                                              
   
   //СписокТоваров = СоздатьОбъект("СписокЗначений");
   //тзТовары.Выгрузить(СписокТоваров,,,"Товар");
   ЗапросSQL.УложитьСписокОбъектов(ВыбНоменклатура, "#СписокН", "Номенклатура");
   
   СписокСкладов = СоздатьОбъект("СписокЗначений");
//    тзСклады.Выгрузить(СписокСкладов,,,"Склад");
   ЗапросSQL.УложитьСписокОбъектов(ВыбСклад, "#СписокС", "Склады");
   
   Если ЗапросSQL.Подготовить(ТекстЗапроса) = 0 Тогда
       Сообщить(""+ЗапросSQL.GetLastError()+"
       |"+ТекстЗапроса, "!");
       Возврат ТЗ_Результ;
   КонецЕсли;

Чето не могу понять как получать остатки на каждый день, хелп.
1 Maxus43
 
21.03.13
10:38
зачем прямой запрос?
2 perkos
 
21.03.13
10:42
чето долго делается такой

   ТекстЗапроса =
   "//{{ЗАПРОС(Сформировать)
   |Период с ВыбНачПериода по ВыбКонПериода;
   |Номенклатура = Регистр.ОстаткиТМЦ.Номенклатура;
   |Склад = Регистр.ОстаткиТМЦ.Склад;
   |Количество = Регистр.ОстаткиТМЦ.Количество;
   |Функция КоличествоНачОст = НачОст(Количество);
   |Группировка День;
   |Условие(Склад в ВыбСклад);
   |Условие(Номенклатура в ВыбНоменклатура);
   |"//}}ЗАПРОС
3 Maxus43
 
21.03.13
10:43
а, 7-ка. я думал 8
4 perkos
 
21.03.13
10:47
из руководства:

$РегистрОстаткиОбороты.<ИмяРегистра>([<НачалоПериода>][, <КонецПериода>][, <Периодичность>][,<МетодДополнения>][,<Соединение>][,<Условие>] [,<Измерение>][,<Ресурс>]) [as <Алиас>]
Здесь из нового появился параметр МетодДополнения. Имеет смысл, только когда используется разворот по периодам:
ДвиженияИГраницыПериода (ActionsAndPeriodBoundaries) – !!!в таблицу включаются обороты по каждому периоду движений и текущие остатки;!!! также таблица дополняется записями о ненулевых остатках на начало и/или конец на границы периода расчета

хммм где я ошибаюсь?
5 perkos
 
21.03.13
11:58
Прямой запрос: как получить движения на каждый день?
Используй класс ПрямойЗапрос, эту же ВТ и параметр Дополнение. Получишь именно то что надо

"День Дополнение"

далее качаем из
http://www.1cpp.ru/forum/YaBB.pl?num=1285352210

как то используем defcls.prm
обновляем 1с++
ставим 1сСклЛайт

должно работать, но у меня пока не взлетит, забил
6 ЧеловекДуши
 
21.03.13
11:58
Используй лучше этот метод.

$РегистрОстатки.<?>(<?>,
       <?>,
       <?>,
       <?>,
       <?>
)
Виртуальные таблицы поддерживаются только для SQL формата ИБ.
Виртуальная таблица остатков
Синтаксис:
$РегистрОстатки.<ИмяРегистра>([<ГраницаРасчета>] [,
<Соединение>] [,<Условие>] [,<Измерение>] [,<Ресурс>]) [as
<Алиас>]

Используется для получения остатков. Определена только для регистров остатков.

Параметры:
<ГраницаРасчета>
Тип: значение.
Строка, представляющая период или момент времени во внутреннем формате 1С.
Если передан период времени (дата или позиция документа), остатки
рассчитываются на
начало периода. Для того чтобы рассчитать остатки на конец периода необходимо
передавать момент времени. Для этого можно воспользоваться вторым
модификатором типа Дата, что соответствует моменту времени конца этой даты. Для
формирования моментов времени также можно воспользоваться позицией
документа: процедурой СформироватьПозициюДокумента() синтаксис 1, указывая не
нулевой ФлагСмещения; или СформироватьПозициюДокумента() синтаксис 2.Если
граница расчета больше ТА, то будут получены остатки по ТА;
По умолчанию остатки на точку актуальности.

<Соединение>
Тип: конструкция типа join. На языке SQL можно описать дополнительные
соединения с таблицами, которые могут быть необходимы для формирования
условий в следующем параметре. Можно оперировать полями измерений регистра,
обращаясь к ним по идентификатору 1С.
По умолчанию отсутствует.

<Условие>
Тип: конструкция типа where. На языке SQL можно описать условие для ограничения
выборки. Можно оперировать полями измерений регистра, обращаясь к ним по
идентификатору 1С, а также полями таблиц, соединения с которыми были описаны
в предыдущем параметре.
По умолчанию отсутствует.

<Измерение>
Тип: конструкция типа список идентификаторов. Указывается список измерений по
которым нужно рассчитать остатки.
По умолчанию по всем.

<Ресурс>
Тип: конструкция типа список идентификаторов. Указывается список ресурсов
которые нужно рассчитать.
По умолчанию по всем.

Поля:
<ИмяИзмерения>
имена измерений указанных к расчету;
<ИмяРесурса>Остаток
имена ресурсов с добавлением слова Остаток указанных к расчету.

Пример:
Имеем регистр:
       Имя: Взаиморасчеты
       Измерения: Фирма, Контрагент, Договор, РасчДокумент
       Ресурсы: Сумма, НДС, НП, Себестоимость
Задача: рассчитать остатки по измерениям Контрагент, РасчДокумент в ресурсе
Сумма, при условии что вид контрагента - Поставщик, и только по фирмам которые
платят НДС.
Текст запроса для решения:
select *
from $РегистрОстатки.Взаиморасчеты(
       :ДатаРасчета,
       inner join $Справочник.Контрагенты as СпрКонтрагенты
on            
СпрКонтрагенты.id = Контрагент
       inner join $Справочник.Фирмы as СпрФирмы on
СпрФирмы.id = Фирма,
$СпрКонтрагенты.ВидКонтрагента = :ВидПоставщик and
$СпрФирмы.ВидФирмы
= :ВидПлатитНДС,
       (Контрагент, РасчДокумент),
       Сумма
) as ОстаткиПоРасчетам

Оптимизация расчета:
+ при получении остатков по ТА, используется только таблица итогов;
+ при получении остатков по дату конца периода сохранения остатков,
       используется только таблица итогов;
+ при получении остатков на дату начала периода сохранения остатков,
       используется только таблица итогов;
+ при получении остатков на дату начала или конца периода сохранения остатков,
и расчет идет по всем измерениям, используется только таблица итогов без
группирования (без предложения group by в запросе);
+ при получении остатков на или по дату близкую к концу текущего периода
       сохранения остатков, используются остатки на конец текущего периода и
       обороты от границы расчета до конца текущего периода (обратный расчет);
+ при получении остатков на или по дату в актуальном периоде сохранения остатков
       близкую к дате ТА, используются актуальные остатки и обороты от границы
расчета по ТА (обратный расчет).
7 ЧеловекДуши
 
21.03.13
12:00
(5) Мне как то класс "ПрямойЗапрос" не по вкусу, я люблю чистый SQL :)
8 perkos
 
21.03.13
12:16
(6) ты предлогаешь перечень дат соеденить с запросом по остаткам?
(7) ВТ $РегистрОстатки это тоже не SQL
9 perkos
 
21.03.13
12:18
просто ребята делавшие $РегистрОстаткиОбороты не подумали про параметр Дополнение в Периодичности.... товарищ собравший класс прямойЗапрос об этом подумал... видимо еще много о чем подумал.
10 perkos
 
21.03.13
12:20
(6) подкинь идею как соеденить по

$РегистрОстатки.Взаиморасчеты(
       :ДатаРасчета,
11 Проггер
 
21.03.13
12:27
Заполняешь таблицу дат и получаешь по каждой дате остатки
12 Mikeware
 
21.03.13
12:28
(9) разница во времени - практически 7 лет.
13 ЧеловекДуши
 
21.03.13
12:28
(8) Ну, так то он ближе к SQL-лю и нет заморочки с остатками.
14 Mikeware
 
21.03.13
12:30
(10) зачем?
----
красивое решение лежит где-то на форуме 1с++, и оттуда опубликовано на sql.ru
15 perkos
 
21.03.13
12:33
(13) нет ничего ближе к скл чем скл, просто как сказал(12) разница между методами в 7 лет, у микрософта где то был лозунг девелопер из нот пламбер(=
(14) помоги найти(=
16 Ёпрст
 
21.03.13
12:36
способов то всего ничего -
либо написать хранимку, которая тупо с ВТ.Останки берёт остаток на каждый день, либо тупо с табличкой дат соединятся.
17 perkos
 
21.03.13
12:40
(16) либо в цикле делать запрос на каждый день, либо соеденить? что быстрее пытаюсь выяснить, наивно надеюсь что разницы нет
18 МихаилМ
 
21.03.13
12:43
(16)
есть еще курсоры и CTE

(17)

проще получить остатки на начало
свернытые по дням движения
расчитать таблицу остатков на каждый день
19 perkos
 
21.03.13
12:45
вот и еще варик(= почему бы и нет(= проще всетаки юзать клас прямых, но не у меня(=
20 perkos
 
21.03.13
12:50
"//{{ЗАПРОС(Сформировать)
   |Период с ВыбНачПериода по ВыбКонПериода;
   |Номенклатура = Регистр.ОстаткиТМЦ.Номенклатура;
   |Склад = Регистр.ОстаткиТМЦ.Склад;
   |Количество = Регистр.ОстаткиТМЦ.Количество;
   |Функция КоличествоНачОст = НачОст(Количество);
   |Группировка День;
   |Условие(Склад в ВыбСклад);
   |Условие(Номенклатура в ВыбНоменклатура);
   |"//}}ЗАПРОС

18 сек

   |SELECT
   |SUM(Остатки.КоличествоОстаток) AS КоличествоН
   |FROM $РегистрОстатки.ОстаткиТМЦ(:ВыбНачПериода,,(Склад IN (SELECT Val FROM #СписокС)) AND (Номенклатура IN (SELECT Val FROM #СписокН)),,(Количество)) AS Остатки
   |";    

14 сек

за 1 день разница 4 сек
21 perkos
 
21.03.13
13:05
за два дня нет разницы, 27 сек оба.
22 Ёпрст
 
21.03.13
13:11
27 секунд это п.ц
23 Ёпрст
 
21.03.13
13:12
прямой должен выполнится за 0,1 сек и быстре
24 Ёпрст
 
21.03.13
13:12
е
25 Гефест
 
21.03.13
13:32
Есть у меня быдлозапрос для этого, если никто ничего лучше не предложит - покажу
26 Ёпрст
 
21.03.13
13:34
(25) у меня тоже есть твой запрос
:)
27 ЧеловекДуши
 
21.03.13
13:35
(25) Давай, кажи :)
28 perkos
 
21.03.13
13:37
ТекстЗапроса = "
   |SELECT
   |SUM(Остатки.КоличествоОстаток) AS КоличествоН
   |FROM $РегистрОстатки.ОстаткиТМЦ(:ВыбНачПериода,,,,(Количество)) AS Остатки
   |";

вот такой без фильтров делается за 12 секунд, получается если надо месяц анализировать то жуть.... кто дурак?
29 perkos
 
21.03.13
13:37
админ?
30 perkos
 
21.03.13
13:41
может мне потребовать нового железа на сервер? там какое то унылое гомно 2004 года стоит на скази в 1 рейде
31 Ёпрст
 
21.03.13
13:42
как меряешь то хоть время запроса ?
32 Mikeware
 
21.03.13
13:42
(30) потребуй купить выпрямитель.
для рук.
33 perkos
 
21.03.13
13:46
(32) ну покажи где я дурак(= я для этого тему и завел чтобы показали... как сделать запрос чтобы по нужной мне выборке складов и номенклатуре остатки я бы получил хотябы за 1 секунду

(31) принципиально? я понимаю что можно профайлером посмотреть но мне точность ненужна

Процедура Химия()
   Перем Час,минуты,сек;
   
   ТекущееВремя(Час,минуты,сек);
   НачалоФормирования = Час*3600+минуты*60+сек;
   
   Для ы=ВыбНачПериода По ВыбКонПериода Цикл
       Сообщить("Одбц = "+одбц(ы));
   КонецЦикла;
   
   ТекущееВремя(Час,минуты,сек);
   ОкончаниеФормирования = Час*3600+минуты*60+сек;  
   Сообщить("Выполнение: "+Число(ОкончаниеФормирования-НачалоФормирования)+" сек.");
       
КонецПроцедуры
34 Ёпрст
 
21.03.13
13:47
(33) п...ц
35 Ёпрст
 
21.03.13
13:49
+ непонятно, нахрена в (0) делать Подготовить
36 trad
 
21.03.13
13:49
(33) дарю
//#класс Таймер=КОП_Таймер@MD
//#{
//#};

//текст модуля обработки КОП_Таймер в МД
Перем Счетчик;

Функция ФорматВремя(Счетчик) Экспорт
   мС=Счетчик%1000;
   СС=Цел(Счетчик/1000);
   ММ=Цел(СС/60);СС=СС%60;
   ЧЧ=Цел(ММ/60);ММ=ММ%60;
   Возврат Формат(ЧЧ,"Ч(0)2")+":"+Формат(ММ,"Ч(0)2")+":"+Формат(СС,"Ч(0)2")+"."+Формат(мС,"Ч(0)3");
КонецФункции

Функция Метка() Экспорт
   _Счетчик=_GetPerformanceCounter();
   СтрВремя=ФорматВремя(_Счетчик-Счетчик);
   Счетчик=_Счетчик;
   Возврат СтрВремя;
КонецФункции

Процедура Конструктор()
   Счетчик=0;
КонецПроцедуры
37 Ёпрст
 
21.03.13
13:49
для не параметрического запроса
38 trad
 
21.03.13
13:51
+ использование:
Таймер = СоздатьОбъект("Таймер");
Таймер.Метка();
ВыполнитьИнструкцию();
Сообщить(Таймер.Метка());
39 perkos
 
21.03.13
14:01
всякую фигню советуете(=
(36) от твоего замера быстрее запрос делатся не будет
(37) какая разница? от этого запрос ощутимо быстрее выполнятся не будет
40 perkos
 
21.03.13
14:03
(18) может проканать.... надо попробовать но все равно остатки получать 14 секунд это долго
41 Гефест
 
21.03.13
14:03
нате
   |declare @@curDate datetime
   |declare @@endDate datetime
   |
   |set @@curDate = cast('"+Дата1+"' AS datetime)  
   |set @@endDate = cast('"+Дата2+"' AS datetime)
   |
   |set nocount on
   |
   |
   |while @@curDate<=cast('"+Дата2+"' AS datetime)
   |begin
   |     insert into #DateTable(Товар,ОстатокТовара,Расход,Дата)
   |     SELECT
   |       Рег.Товар                [Товар],
   |       SUM(Рег.ОстатокТовара)    [ОстатокТовара],  
   |       SUM(Рег.Расход)            [ОстатокТовара],  
   |       @@curDate                [Дата]
   |     FROM
   |        (  
   |           SELECT
   |             $rg.Товар                    [Товар],
   |             $rg.ОстатокДляПродажи        [ОстатокТовара],  
   |             0                            [Расход]  
   |           FROM
   |             $РегистрИтоги.ОстаткиТоваров AS rg (nolock)
   |           WHERE
   |              rg.period = dateadd(m,-1,dateadd(dd,-day(@@curDate)+1,@@curDate))";
                   Если СписокНоменклатура.РазмерСписка()>0 Тогда    
                       ТекстЗапроса = ТекстЗапроса + "
                       |AND $rg.Товар IN (SELECT val FROM #СписокТоваров)";
                       ЗапросSQL.УложитьСписокОбъектов(СписокНоменклатура,"#СписокТоваров","Номенклатура");    
                   КонецЕсли;  
                   Если СписокСклады.РазмерСписка()>0 Тогда    
                       ТекстЗапроса = ТекстЗапроса + "
                       |AND $rg.Склад IN (SELECT val FROM #СписокСкладов)";
                       ЗапросSQL.УложитьСписокОбъектов(СписокСклады,"#СписокСкладов","МестаХранения");    
                   КонецЕсли;  
                   ТекстЗапроса = ТекстЗапроса + "
   |           UNION ALL
   |           SELECT
   |             $ra.Товар                                      [Товар],
   |             $ra.ОстатокДляПродажи*(1-ra.debkred*2)        [ОстатокТовара],  
   |             0                                                [Расход]  
   |           FROM
   |             $Регистр.ОстаткиТоваров ra (nolock)
   |           INNER JOIN
   |             _1sjourn j (nolock) on j.iddoc = ra.iddoc
   |           WHERE
   |              cast(left(j.date_time_iddoc,8) AS datetime) between dateadd(dd,-day(@@curDate)+1,@@curDate) AND @@curDate";
                   Если СписокНоменклатура.РазмерСписка()>0 Тогда    
                       ТекстЗапроса = ТекстЗапроса + "
                       |AND $ra.Товар IN (SELECT val FROM #СписокТоваров)";
                   КонецЕсли;    
                   Если СписокСклады.РазмерСписка()>0 Тогда    
                       ТекстЗапроса = ТекстЗапроса + "
                       |AND $ra.Склад IN (SELECT val FROM #СписокСкладов)";
                   КонецЕсли;    
                   ТекстЗапроса = ТекстЗапроса + "  
   |           UNION ALL  
   |
   |           SELECT
   |             $ra2.Товар                                      [Товар],
   |             0                                                [ОстатокТовара],  
   |             $ra2.ОстатокДляПродажи*ra2.DEBKRED            [Расход]  
   |           FROM
   |             $Регистр.ОстаткиТоваров ra2 (nolock)
   |           INNER JOIN
   |             _1sjourn j (nolock) on j.iddoc = ra2.iddoc
   |           WHERE
   |              cast(left(j.date_time_iddoc,8) AS datetime) = @@curDate";
                   Если СписокНоменклатура.РазмерСписка()>0 Тогда    
                       ТекстЗапроса = ТекстЗапроса + "
                       |AND $ra2.Товар IN (SELECT val FROM #СписокТоваров)";
                   КонецЕсли;      
                   Если СписокСклады.РазмерСписка()>0 Тогда    
                       ТекстЗапроса = ТекстЗапроса + "
                       |AND $ra2.Склад IN (SELECT val FROM #СписокСкладов)";
                   КонецЕсли;      
                   ТекстЗапроса = ТекстЗапроса + "
   |           UNION ALL
   |
   |           SELECT
   |             $ra3.Товар                                      [Товар],
   |             0                                                [ОстатокТовара],  
   |             0                                                [Расход]  
   |           FROM
   |             $Регистр.ОстаткиТоваров ra3 (nolock)
   |           INNER JOIN
   |             _1sjourn j (nolock) on j.iddoc = ra3.iddoc
   |           WHERE
   |              cast(left(j.date_time_iddoc,8) AS datetime) between @@curDate AND @@endDate";
                   Если СписокНоменклатура.РазмерСписка()>0 Тогда    
                       ТекстЗапроса = ТекстЗапроса + "
                       |AND $ra3.Товар IN (SELECT val FROM #СписокТоваров)";
                   КонецЕсли;          
                   Если СписокСклады.РазмерСписка()>0 Тогда    
                       ТекстЗапроса = ТекстЗапроса + "
                       |AND $ra3.Склад IN (SELECT val FROM #СписокСкладов)";
                   КонецЕсли;          
                   ТекстЗапроса = ТекстЗапроса + "  
   |        ) Рег  
   |     GROUP BY
   |        Рег.Товар
   |     set @@curDate = dateadd(dd,1,@@curDate)
   |  end
   |";
42 perkos
 
22.03.13
06:22
важно что самый элементарный запрос по остаткам выполняется слишком долго >10сек а вы мне мозг пудрите всякими мелочами(=
43 perkos
 
22.03.13
06:55
посмотрел замером производительности оказалось что 13сек выполняется строка
ЗапросSQL.УложитьСписокОбъектов(ВыбНоменклатура, "#СписокН", "Номенклатура");

так что (41) не решает
44 DrZombi
 
гуру
22.03.13
07:30
(33) Используй не тупой Цикл, а временные таблицы :)
45 DrZombi
 
гуру
22.03.13
07:31
(42) Может у вас хренова с закрытием регистров?
Чем вы занимаетесь? Розницей? Сколько Товара в справочнике Номенклатура?
46 DrZombi
 
гуру
22.03.13
07:32
(43) Это подготовка временной таблицы, ты её можешь укладывать любым способом, хоть через Insert на прямую, не через "УложитьСписокОбъектов" :)
47 DrZombi
 
гуру
22.03.13
07:33
+(43) Тогда стоит подумать о том, что бы этот метод сделать только один раз, а не каждый раз в цикле ;)
48 perkos
 
22.03.13
10:33
(47) например?
(45) розничная база да, очень большой справочник номенклатуры ~160тыс элементов, в группе товаров по которой считаю остатки ~60тыс элементов. поэтому этот уложить так долго работает видимо...
49 Ёпрст
 
22.03.13
10:34
(48) да уж..
сам запрос то не 18 секунд однако.
+ выкинуть Подгтовить из текста надо.
ЗЫ: заместо уложитьсписокОбъектов можно пихать самому всё во временную табличку.
50 Ёпрст
 
22.03.13
10:35
и про время замера, тебе не просто так в начале говорили - не то меряешь и не тем.
51 perkos
 
22.03.13
10:35
(49) да сам запрос делается менее секунды
52 perkos
 
22.03.13
10:37
(50) мерять надежнее всего в отладчике
(49) дай пример как можно пихать самому быстрее
53 Ёпрст
 
22.03.13
10:41
(52) в отладчике ?
:)))))))))))))))))))))))))))))))))))))))))))))))))
54 perkos
 
22.03.13
10:46
(53) это не тема обсуждения где мерить, если ты нуб и не знаешь какие строки могут выполнятся медленно то ставить замеры надежнее всего на каждой строке, а если не хочешь дрочить то включи замер производительности и хотябы представление будешь иметь че тупит, я вот не догадывался сперва что именно этот уложить так тупит... как оказалось вы тоже не догадались об этом.
55 Ёпрст
 
22.03.13
10:51
http://www.1cpp.ru/forum/YaBB.pl?num=1354004508/35#35
читай, там есть готовый код
56 Ёпрст
 
22.03.13
10:52
(54):)))))))))

И эти люди только сейчас открыли для себя замер производительности и жуколов..
Когда доберутся до стека вызовов.. воопче ачуметь будет
57 Ёпрст
 
22.03.13
10:53
ну а с _GetPerformanceCounter() мозг взорвётся!
58 Ёпрст
 
22.03.13
10:54
про консоли готовые , типа 1CQA для отладки, ужо молчу
59 Ёпрст
 
22.03.13
10:54
и про профайлер и план запроса тоже
60 perkos
 
22.03.13
11:05
умный какой а(= завышенная собственная важность наказывает(=
1cqa? не, не слышал, надо почитать, спасибо(=
61 Mikeware
 
22.03.13
11:07
(57) не взорвется. нечему..
62 perkos
 
22.03.13
11:09
(= ну тупой подумаешь беда(= хех а вы ведь наверняка взрослые мужики(=
63 perkos
 
22.03.13
11:10
а похожи на школоло(=