Имя: Пароль:
1C
1C 7.7
v7: Получить остатки на каждый день прямым запросом
, ,
0 Karambol
 
21.08.13
07:16
Добрый день!

Можно ли при помощи прямого запроса получить из регистра остатков остатки с группировкой по дням.
Т.е., имеем такой запрос:
ТекстЗапроса = "
    |Период С ВыбНачПериода По ВыбКонПериода;
    |Контрагент        = Регистр.ПодключенныеУслуги.Предприятие;
    |Номенклатура     = Регистр.ПодключенныеУслуги.Номенклатура;
    |Сумма             = Регистр.ПодключенныеУслуги.Сумма;
    |Количество     = Регистр.ПодключенныеУслуги.Количество;
    |СостояниеУслуги= Регистр.ПодключенныеУслуги.СостояниеУслуги;
    |ЕдиницаИзмеренияАбПлаты = Регистр.ПодключенныеУслуги.ЕдиницаИзмеренияАбПлаты;
    |ТипТарифа        = Регистр.ПодключенныеУслуги.Номенклатура._ТипТарифа;
    |
    |Группировка Контрагент Без Групп;
    |Группировка Номенклатура Без Групп;
    |Группировка День;
    |
    |Функция КоличествоКонОст = КонОст(Количество);
    |Функция СуммаСумма          = Сумма(Сумма);
    |Условие(СостояниеУслуги = Перечисление._СостояниеУслуг.Подключено);
    |";

Можно ли его перевести на прямой?
Пробовал брать данные из ВТ Остатки и обороты, но там есть данные только по тем дням, когда были движения.
1 Rie
 
21.08.13
07:18
(0) Создай таблицу всех нужных дней. Соедини её с остатками/оборотами.
2 dk
 
21.08.13
07:39
нифига там не просто "соединить" ))
--
я решил постобработкой, текст откуда-то стырил и под себя доработал

Функция глСреднедневнаяПродажаСУчетомПрисутствияПоСкладам(Дата1, Дата2, фМФТМ="", фМФНоменклатура="", фМФНомИскл="", фМФСклад="", фМФСкладИскл="") Экспорт
    
    Перем Результат;

    Результат = СоздатьОбъект("ТаблицаЗначений");
    Результат.НоваяКолонка("Товар",        "Справочник.Номенклатура");
    Результат.НоваяКолонка("Склад",        "Справочник.МестаХранения");
    Результат.НоваяКолонка("Дни",        "Число");
    Результат.НоваяКолонка("ОборотКол",    "Число");
    Результат.НоваяКолонка("ОборотСеб",    "Число");
    Результат.НоваяКолонка("ОборотСум",    "Число");
    
    рс = СоздатьОбъект("ODBCRecordset");
    
    // Зададим фильтр по складам
    флНуженФильтрПоСкладу = 0;
    Если ПустоеЗначение(фМФСклад) = 0 Тогда
        флНуженФильтрПоСкладу = 1;
        рс.УложитьСписокОбъектов(фМФСклад, "#фМФСклад", "МестаХранения");
    КонецЕсли;
    
    Если ПустоеЗначение(фМФСкладИскл) = 0 Тогда
        флНуженФильтрПоСкладу = 1;
        рс.УложитьСписокОбъектов(фМФСкладИскл, "#фМФСкладИскл", "МестаХранения");
    КонецЕсли;

    Если флНуженФильтрПоСкладу <> 0 Тогда
        
        ТекстЗапроса = "
        |if (select object_id('tempdb..#mytemptableFilterSklad')) is NOT null DROP TABLE #mytemptableFilterSklad
        |CREATE TABLE #mytemptableFilterSklad (
        |    Val        char(9)       DEFAULT $ПустойИд  NOT NULL,
        |UNIQUE CLUSTERED (Val))
        |";
        
        рс.ВыполнитьСкалярный(ТекстЗапроса);
        
        ТекстЗапроса = "
        |INSERT INTO #mytemptableFilterSklad
        |SELECT
        |    МестаХранения.ID
        |FROM
        |    $Справочник.МестаХранения AS МестаХранения With (NOLOCK)
        |WHERE
        |    (1=1)"+?(ПустоеЗначение(фМФСклад)=0," AND (МестаХранения.ID IN (SELECT VAL FROM #фМФСклад)) ","")+
                    ?(ПустоеЗначение(фМФСкладИскл)=0," AND (МестаХранения.ID NOT IN (SELECT val FROM #фМФСкладИскл))","")+"
        |";
    
        рс.ВыполнитьСкалярный(ТекстЗапроса);
        
    КонецЕсли;    //"Если флНуженФильтрПоСкладу <> 0 Тогда
    
    // Зададим фильтр по товару
    флНуженФильтрПоТовару = 0;
    
    Если ПустоеЗначение(фМФТМ) = 0 Тогда
        флНуженФильтрПоТовару = 1;
        рс.УложитьСписокОбъектов(фМФТМ, "#фМФТМ", "ТорговыеМарки");
    КонецЕсли;
    
    Если ПустоеЗначение(фМФНоменклатура) = 0 Тогда
        флНуженФильтрПоТовару = 1;
        рс.УложитьСписокОбъектов(фМФНоменклатура, "#фМФНоменклатура", "Номенклатура");
    КонецЕсли;
    
    Если ПустоеЗначение(фМФНомИскл) = 0 Тогда
        флНуженФильтрПоТовару = 1;
        рс.УложитьСписокОбъектов(фМФНомИскл, "#фМФНомИскл", "Номенклатура");
    КонецЕсли;
    
    Если флНуженФильтрПоТовару <> 0 Тогда
        
        ТекстЗапроса = "
        |if (select object_id('tempdb..#mytemptableFilterTovar')) is NOT null DROP TABLE #mytemptableFilterTovar
        |CREATE TABLE #mytemptableFilterTovar (
        |    Val        char(9)       DEFAULT $ПустойИд  NOT NULL,
        |UNIQUE CLUSTERED (Val))
        |";
        
        рс.ВыполнитьСкалярный(ТекстЗапроса);
        
        ТекстЗапроса = "
        |INSERT INTO #mytemptableFilterTovar
        |SELECT
        |    Номенклатура.ID
        |FROM
        |    $Справочник.Номенклатура AS Номенклатура With (NOLOCK)
        |WHERE
        |    (1=1)"+?(ПустоеЗначение(фМФТМ)=0," AND ($Номенклатура.ТорговаяМарка IN (SELECT val FROM #фМФТМ)) ","")+
                    ?(ПустоеЗначение(фМФНоменклатура)=0," AND (Номенклатура.ID IN (SELECT val FROM #фМФНоменклатура))","")+
                    ?(ПустоеЗначение(фМФНомИскл)=0," AND (Номенклатура.ID NOT IN (SELECT val FROM #фМФНомИскл))","");
    
        рс.ВыполнитьСкалярный(ТекстЗапроса);
        
    КонецЕсли;    //"Если флНуженФильтрПоТовару <> 0 Тогда
    
    ТекстЗапроса = "
    |if (select object_id('tempdb..#mytemptableSrednie')) is NOT null DROP TABLE #mytemptableSrednie
    |CREATE TABLE #mytemptableSrednie (
    |    Товар        char(9)       DEFAULT $ПустойИд  NOT NULL,
    |    Склад        char(9)       DEFAULT $ПустойИд  NOT NULL,
    |    День        datetime      DEFAULT '17530101' NOT NULL,
    |    Остаток        numeric(19,5) DEFAULT 0          NOT NULL,
    |    ОборотКол    numeric(19,5) DEFAULT 0          NOT NULL,
    |    ОборотСеб    numeric(19,5) DEFAULT 0          NOT NULL,
    |    ОборотСум    numeric(19,5) DEFAULT 0          NOT NULL,
    |    Дни            numeric(19,0) DEFAULT 0          NOT NULL,
    |    Флаг        numeric(1,0)  DEFAULT 0          NOT NULL,
    |UNIQUE CLUSTERED (Товар, Склад, День))
    |";
    
    рс.ВыполнитьСкалярный(ТекстЗапроса);
    
    ТекстЗапроса = "
    |INSERT INTO #mytemptableSrednie
    |SELECT
    |    
    |     Вложенный.Товар1 _Товар
    |    , Вложенный.Склад1    _Склад
    |    , Вложенный.Период1 _День
    |    , sum(Вложенный.Остаток1) _Остаток
    |    , sum(Вложенный.ОборотКол1) _ОборотКол
    |    , sum(Вложенный.ОборотСеб1) _ОборотСеб
    |    , sum(Вложенный.ОборотСум1) _ОборотСум
    |    , sum(0) _Дни
    |    , sum(Вложенный.Флаг1) _Флаг
    |FROM
    |(SELECT
    |        ТовОстаткиОбороты.Товар Товар1
    |    ,    ТовОстаткиОбороты.Склад Склад1
    |    ,    NullIf(ТовОстаткиОбороты.Период, '17530101') Период1
    |    ,    sum(ТовОстаткиОбороты.ОстатокТовараКонечныйОстаток) Остаток1
    |    ,    min(0) ОборотКол1
    |    ,    min(0) ОборотСеб1
    |    ,    min(0) ОборотСум1
    |    ,    min(1) Флаг1
    |FROM $РегистрОстаткиОбороты.ОстаткиТоваров(:НачДата,
    |        :КонДата~,
    |        День,
    |        ДвиженияИГраницыПериода,,"+?(флНуженФильтрПоСкладу <> 0,"(Склад IN (SELECT val FROM #mytemptableFilterSklad))","")+?(флНуженФильтрПоТовару<>0,?(флНуженФильтрПоСкладу <> 0," AND ","")+"(Товар IN (SELECT val FROM #mytemptableFilterTovar))","")+",(Товар,Склад),(ОстатокТовара)) AS ТовОстаткиОбороты With (NOLOCK)
    |GROUP BY
    |    ТовОстаткиОбороты.Товар, ТовОстаткиОбороты.Склад, NullIf(ТовОстаткиОбороты.Период, '17530101')
    |
    |UNION ALL
    |
    |SELECT
    |    $Продажи.Товар _Товар
    |    , $СпрМаг.Склад _Склад
    |    , NullIf(Cast(Left(Продажи.DATE_TIME_IDDOC, 8) AS datetime), '17530101') Документ_дата
    |    , min(0) _Остаток1
    |    , sum($Продажи.Количество) ОборотКол
    |    , sum($Продажи.Себестоимость) ОборотСеб
    |    , sum($Продажи.Сумма) ОборотСум
    |    , min(0) Флаг
    |FROM $Регистр.Продажи AS Продажи With (NOLOCK)
    |    INNER JOIN    $Справочник.Магазины AS СпрМаг With (NOLOCK) ON $СпрМаг.Клиент = $Продажи.Клиент
    |WHERE (Продажи.DATE_TIME_IDDOC  between :НачДата AND :КонДата~) "+?(флНуженФильтрПоСкладу <> 0," AND ($СпрМаг.Склад IN (SELECT val FROM #mytemptableFilterSklad))","")+?(флНуженФильтрПоТовару<>0," AND ($Продажи.Товар IN (SELECT val FROM #mytemptableFilterTovar))","")+"
    |GROUP BY $Продажи.Товар, $СпрМаг.Склад,NullIf(Cast(Left(Продажи.DATE_TIME_IDDOC, 8) AS datetime), '17530101')
    |) AS Вложенный
    |GROUP BY
    |    Вложенный.Товар1, Вложенный.Склад1, Вложенный.Период1
    |";
    
    рс.УстановитьТекстовыйПараметр("НачДата", Дата1);
    рс.УстановитьТекстовыйПараметр("КонДата", Дата2);
    рс.ВыполнитьСкалярный(ТекстЗапроса);

    ТекстЗапроса = "
        |INSERT INTO #mytemptableSrednie (Товар, Склад, День)
        |SELECT B.Товар, B.Склад, A.date
        |FROM (SELECT date = Cast((b.number * 0x100) + a.number AS datetime)
        |    FROM master..spt_values a
        |        INNER JOIN master..spt_values b ON (b.number * 0x100) + a.number <= Cast(Cast(:КонДата AS datetime) AS integer)
        |            AND (b.number * 0x100) + a.number >= Cast(Cast(:НачДата AS datetime) AS integer)
        |    WHERE (a.type = 'p') AND (b.type = 'p')
        |        AND (b.number * 0x100 <= Cast(Cast(:КонДата AS datetime) AS integer))
        |    ) A CROSS JOIN
        |    (SELECT DISTINCT Товар,Склад
        |    FROM #mytemptableSrednie) B
        |WHERE NOT Exists(SELECT * FROM #mytemptableSrednie WHERE #mytemptableSrednie.Товар = B.Товар AND #mytemptableSrednie.День = A.date AND #mytemptableSrednie.Склад = B.Склад)
        |";

    рс.УстановитьТекстовыйПараметр("НачДата", Дата1);
    рс.УстановитьТекстовыйПараметр("КонДата", Дата2);
    рс.ВыполнитьСкалярный(ТекстЗапроса);
    
    ТекстЗапроса = "
        |DECLARE @n numeric(19,5)
        |DECLARE @s char(9)
        |DECLARE @d char(9)
        |SET @n = 0
        |SET @s = $ПустойИд
        |SET @d = $ПустойИд
        |
        |UPDATE #mytemptableSrednie
        |SET @n = CASE WHEN (Остаток = 0) AND (Товар = @s) AND (Склад = @d) AND (Флаг = 0) THEN @n ELSE Остаток END,
        |    Остаток = @n,
        |    Дни = CASE WHEN ((@n <> 0) OR (ОборотКол <> 0)) THEN 1 ELSE 0 END,
        |    @s = Товар,
        |    @d = Склад
        |";
        
    рс.ВыполнитьСкалярный(ТекстЗапроса);
    
    ТекстЗапроса = "
        |SELECT
        |    Товар [Товар $Справочник.Номенклатура]
        |,    Склад [Склад $Справочник.МестаХранения]
        |,    sum(Дни)       Дни
        |,    sum(ОборотКол) ОборотКол
        |,    sum(ОборотСеб) ОборотСеб
        |,    sum(ОборотСум) ОборотСум
        |FROM
        |    #mytemptableSrednie
        |GROUP BY
        |    Товар,Склад";
    
    Результат = рс.ВыполнитьИнструкцию(ТекстЗапроса);
    
    рс.ВыполнитьСкалярный("DROP TABLE #mytemptableSrednie");
    
    Возврат Результат;
    
КонецФункции    //глСреднедневнаяПродажаСУчетомПрисутствияПоСкладам
3 Rie
 
21.08.13
07:44
(2) Согласен, соединение сработает для оборотов - а ТС нужны остатки.
4 varelchik
 
21.08.13
08:34
Виртуальная таблица остатков и оборотов

Синтаксис:

$РегистрОстаткиОбороты.<ИмяРегистра>([<НачалоПериода>][, <КонецПериода>][, <Периодичность>][,<МетодДополнения>][,
        <Соединение>][,
        <Условие>][,
        <Измерение>][,<Ресурс>]
) [as <Алиас>]

Параметры:

НачалоПериода - тип: значение. Строка, представляющая период или момент времени во внутреннем формате 1С. По умолчанию начало ведения учета.

КонецПериода - тип: значение. Строка, представляющая период или момент времени во внутреннем формате 1С. Если передан период времени (дата, позиция документа), он в расчет не включается. Для того чтобы он включался в расчет нужно воспользоваться модификатором, или предварительно сформировать момент времени (См. СформироватьПозициюДокумента()). По умолчанию точка актуальности.

Периодичность - тип: конструкция типа ключевое слово. Указывается дополнительный разворот итогов по периодичности. Задается один из следующих вариантов:

Период | Period - только за период (не разворачивать);
Документ | Document - разворачивать по документу;
День | Day - разворачивать по дням;
Неделя | Week - разворачивать по неделям;
Месяц | Month - разворачивать по месяцам;
Квартал | Quarter - разворачивать по кварталам;
Год | Year - разворачивать по годам.
По умолчанию Период.

МетодДополнения - тип: конструкция типа ключевое слово. Имеет смысл, только когда используется разворот по периодам. Задается один из следующих вариантов:

Движения (Actions) - в таблицу включаются обороты по каждому периоду движений, и текущие остатки только по тем комбинациям измерений, по которым были движения в период расчета;
ДвиженияИГраницыПериода (ActionsAndPeriodBoundaries) - в таблицу включаются обороты по каждому периоду движений и текущие остатки; также таблица дополняется записями о ненулевых остатках на начало и/или конец на границы периода расчета.
Соединение - тип: конструкция типа join. На языке SQL можно описать дополнительные соединения с таблицами, которые могут быть необходимы для формирования условий в следующем параметре. Можно оперировать полями измерений регистра, обращаясь к ним по идентификатору 1С. По умолчанию отсутствует.

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

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

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

Поля:

<ИмяИзмерения> - имена измерений указанных к расчету; тип определяется типом измерения регистра;
<ИмяРесурса>НачальныйОстаток - имена ресурсов с добавлением слова НачальныйОстаток, указанных к расчету; тип определяется типом ресурса регистра;
<ИмяРесурса>Приход - имена ресурсов с добавлением слова Приход, указанных к расчету; тип определяется типом ресурса регистра;
<ИмяРесурса>Расход - имена ресурсов с добавлением слова Расход, указанных к расчету; тип определяется типом ресурса регистра;
<ИмяРесурса>КонечныйОстаток - имена ресурсов с добавлением слова НачальныйОстаток, указанных к расчету; тип определяется типом ресурса регистра;
Период - тип datetime; дата начала периода, по которому происходит разворот оборотов; существует только в том случае, если указана периодичность День, Неделя, Месяц, Квартал, Год;
ПозицияДокумента - тип char(23); строка в формате date_time_iddoc (8+6+9); существует только в том случае, если указана периодичность Документ;
ВидДокумента - тип int; идентификатор вида документа; существует только в том случае, если указана периодичность Документ.
Описание: Используется для получения остатков и оборотов. Определена только для регистра остатков.

Оптимизация расчета:
по таблицам итогов и движений рассчитывается всегда только один из остатков, другой вычисляется из оборотов при агрегировании;
рассчитывается тот остаток (начальный/конечный), который получить легче, исходя из оптимизации расчета остатков, См. выше описание виртуальной таблицы остатков;
5 dk
 
21.08.13
08:42
(4) пофиг, не разворачивает она с группировкой день по всем дням, если нет движений в этом дне
6 varelchik
 
21.08.13
08:46
Плохо запрос пишешь.
Все оно разворачивает.
7 varelchik
 
21.08.13
08:47
+ Не нравятся мне ваши кошки.
А вы не умеете их готовить.
8 Ёпрст
 
21.08.13
08:50
(0)
либо табличка с датами + лефт джоин твой запрос, либо тупо хранимка, которая подставляет в ВТ дату на каждый день и получает останки.
9 Ёпрст
 
21.08.13
08:51
но прочще, на выходе посчитать самому, в цикле обхода результата запроса.
Всего то нужно, знать НачОст Приход и Расход за день.
10 dk
 
21.08.13
09:16
(7) а проверить слабо?
Ошибка? Это не ошибка, это системная функция.