Имя: Пароль:
1C
1C 7.7
v7: Вопрос по прямому запросу к МНЧ документа
0 zenon46
 
20.11.19
16:39
Доброго дня!
Прошу сильно не пинать, есть запрос к МНЧ документа для получения количества каждого товара, из передаваемой ТЗ. Как модифицировать его (у меня не получается), что бы запрос отработал по периоду с...по, а то у меня работает только по одному дню, ну и если ни один товар не попался в МНЧ документа, на эту дату поставились бы 0.
Сам запрос такой :

        |SELECT
    |    СпрНоменклатура.Code [Код],    
    |    t1.Номенклатура [Номенклатура $Справочник.Номенклатура],    
    |    sum(isnull(t1.Поступление,0)) КолПоступ,
    |  :ВыбДата [Дата $Дата]
    |FROM    
    |(
    |SELECT
    |   $ДокС.Номенклатура as Номенклатура,
    |   sum($ДокС.Количество) as Поступление
    |FROM
    |    _1SJourn Жур
    |INNER JOIN
    |    $ДокументСтроки.ПоступлениеТМЦ_Склад as ДокС ON ДокС.IDDoc = Жур.IDDoc
    |WHERE
    |    Жур.Date_Time_IDDoc BETWEEN :ВыбДата AND :ВыбДата~ AND
    |    Жур.IDDocDef = $ВидДокумента.ПоступлениеТМЦ_Склад AND
    |    Жур.Closed & 1 = 1 AND
    |    ($ДокС.Номенклатура IN (SELECT Val FROM #TempTable))
    |GROUP BY
    |    $ДокС.Номенклатура    
    |UNION ALL
    |SELECT
    |    VAL,
    |    0
    |FROM  
    |    #TempTable
    |) t1
        |INNER JOIN
    |    $Справочник.Номенклатура AS СпрНоменклатура ON СпрНоменклатура.ID=t1.Номенклатура
    |GROUP BY t1.Номенклатура, СпрНоменклатура.code
1 palpetrovich
 
20.11.19
17:26
(0) Хочешгь получить сколько поступлений было по выбранным товарам за каждый день периода?
2 Sserj
 
20.11.19
17:45
Получится довольно большой запрос, поэтому давай по частям.

1. Получаешь табличку пересечения всех дат и товаров.
2. Получаешь продажи за период как Дата,Товар,Количество.
3. Левым соединением связываешь Первую табличку со второй по Дата,Товар.

Первую часть лучше сделать с использованем CTE:
Запросу нужно установить параметры: начДата и конДата а так же уложить табличку товаров
#TempTable
В CTE ДатаТовары дату сразу конвертируем к типу ГГГГММДД - так лекго будет соединять
с датами из журнала.

ТекстЗапроса = "
|with ДатыПерида as (
|SELECT cast(:начДата as date) as [Дата]
|UNION ALL
|SELECT dateadd(dd,1,ДатыПерида.ДАТА) FROM ДатыПерида WHERE ДатыПерида.Дата < :КонДата
|)
|, ДатаТовары as (
|SELECT
|  ДатыПерида.Дата as [ДатаПродажи]
|  , #TempTable.val as [Товар]
|FROM ДатыПерида CROSS JOIN #TempTable
|)
|";
3 Sserj
 
20.11.19
17:51
2. Из документов получаем табличку Дата,Товар,Количество:

"
|SELECT
|  SUBSTRNIG(Жур.Date_Time_IDDoc, 1, 8) as [ДатаДок]
|  , $ДокС.Номенклатура as [Товар]
|  , $ДокС.Количество as [Количество]
|FROM
|  _1SJourn as Жур
|  JOIN $ДокументСтроки.ПоступлениеТМЦ_Склад as ДокС ON ДокС.IDDoc = Жур.IDDoc
|  JOIN #TempTable as фильтрТоваров ON $ДокС.Номенклатура = фильтрТоваров.val
|WHERE
|   Жур.Date_Time_IDDoc BETWEEN :начДата AND :конДата~ AND
|   Жур.IDDocDef = $ВидДокумента.ПоступлениеТМЦ_Склад AND
|   Жур.Closed & 1 = 1 AND
|"
4 Sserj
 
20.11.19
17:58
3. Собираем обе таблички и получаем итоговую Дата,Товар,Количество

ТекстЗапроса = "
|with ДатыПерида as (
|SELECT cast(:начДата as date) as [Дата]
|UNION ALL
|SELECT dateadd(dd,1,ДатыПерида.ДАТА) FROM ДатыПерида WHERE ДатыПерида.Дата < :КонДата
|)
|, ДатаТовары as (
|SELECT
|  ДатыПерида.Дата as [ДатаПродажи]
|  , #TempTable.val as [Товар]
|FROM ДатыПерида CROSS JOIN #TempTable
|)
|, Поступления as (
|SELECT
|  SUBSTRNIG(Жур.Date_Time_IDDoc, 1, 8) as [ДатаДок]
|  , $ДокС.Номенклатура as [Товар]
|  , $ДокС.Количество as [Количество]
|FROM
|  _1SJourn as Жур
|  JOIN $ДокументСтроки.ПоступлениеТМЦ_Склад as ДокС ON ДокС.IDDoc = Жур.IDDoc
|  JOIN #TempTable as фильтрТоваров ON $ДокС.Номенклатура = фильтрТоваров.val
|WHERE
|   Жур.Date_Time_IDDoc BETWEEN :начДата AND :конДата~ AND
|   Жур.IDDocDef = $ВидДокумента.ПоступлениеТМЦ_Склад AND
|   Жур.Closed & 1 = 1 AND
|)
|
|SELECT
|  ДатаТовары.ДатаПродажи as [ДатаОтчета $Дата]
|  , ДатаТовары.Товар as [Товар $Справочник.Номенклатура]
|  , COALESCE(Поступления.Количетсво, 0) as [Количество $Число]
|  , спрТовары.DESCR as [Товар_Наименование $Строка]
|FROM
|   ДатаТовары LEFT JOIN Поступления
|   ON (ДатаТовары.ДатаПродажи = Поступления.ДатаДок
|       AND ДатаТовары.Товар = Поступления.Товар)
|   LEFT JOIN $Справочник.Номенклатура AS спрТовары
|   ON ДатаТовары.Товар = спрТовары.ID
|ORDER BY
|  ДатаТовары.ДатаПродажи, спрТовары.DESCR, ДатаТовары.Товар
|";
5 Sserj
 
20.11.19
18:01
+(4) Ну вот написал, что дату надо сразу к ГГГГММДД привести и забыл.
На первом шаге нужно в ДатаТовары заменить:
ДатыПерида.Дата as [ДатаПродажи]
На
CONVERT(varchar(8), ДатыПерида.Дата, 112) КАК [ДатаПродажи]
6 Sserj
 
20.11.19
18:05
А ну вот еще забыл поступления свернуть на дату, если товар в нескольких постулениях в день то будет много строк, поэтому получение из документов переделываем вот так:

|, Поступления as (
|SELECT
|  SUBSTRNIG(Жур.Date_Time_IDDoc, 1, 8) as [ДатаДок]
|  , $ДокС.Номенклатура as [Товар]
|  , SUM($ДокС.Количество) as [Количество]
|FROM
|  _1SJourn as Жур
|  JOIN $ДокументСтроки.ПоступлениеТМЦ_Склад as ДокС ON ДокС.IDDoc = Жур.IDDoc
|  JOIN #TempTable as фильтрТоваров ON $ДокС.Номенклатура = фильтрТоваров.val
|WHERE
|   Жур.Date_Time_IDDoc BETWEEN :начДата AND :конДата~ AND
|   Жур.IDDocDef = $ВидДокумента.ПоступлениеТМЦ_Склад AND
|   Жур.Closed & 1 = 1
|GROUP BY
|  SUBSTRNIG(Жур.Date_Time_IDDoc, 1, 8), $ДокС.Номенклатура
|)
7 zenon46
 
20.11.19
18:22
(1) не совсем так, не сколько ДОКУМЕНТОВ поступлений было, а сколько КОЛИЧЕСТВА было в документах поступления.
8 zenon46
 
20.11.19
18:25
(4) завтра буду пробовать
9 Sserj
 
20.11.19
18:29
(8) Там в тексте иногда вместо "as" встречается "КАК" нужно поправить на "as". Это я копировал то что у меня было изначально для 1sqlite. Когда на SQL переходили все через класс ПрямойЗапрос адаптировали.
10 palpetrovich
 
20.11.19
18:30
(7) а где у меня в "Хочешгь получить сколько поступлений было по выбранным товарам за каждый день периода?"
слово ДОКУМЕНТОВ ? :)
(2) забавно календарь получаешь рекурсией, возьму на заметку, сам до этого пользовался такой вот конструкцикей:

while @начДата <= @конКон
begin
    insert into #Календарь values (@начДата);
    set @начДата = dateadd(DAY, 1, @начДата)
end;
11 Sserj
 
20.11.19
18:34
(10) Ну циклом надежнее, потому что рекурсия ограничение имеет, по умолчанию SQLServer по моему не больше 50 в глубину разрешает.
12 palpetrovich
 
20.11.19
18:41
(11) прав
Сообщение 530, уровень 16, состояние 1, строка 6
The statement terminated. The maximum recursion 100 has been exhausted before statement completion.
13 Sserj
 
20.11.19
18:48
(12) Вообще можно обойти, но это на любителя :)

|with ДатыПерида as (
|SELECT cast(:начДата as date) as [Дата]
|UNION ALL
|SELECT dateadd(dd,1,ДатыПерида.ДАТА) FROM ДатыПерида WHERE ДатыПерида.Дата < :КонДата
|)
|
|SELECT *
|FROM ДатыПерида
|OPTION (MAXRECURSION 0)
14 zenon46
 
21.11.19
12:17
(11) как раз на это и напоролся, работает очень быстро но только не более 100 дней, дальше сыпется сообщение об ошибке.
15 Sserj
 
21.11.19
16:14
(14) Решение в (13)

ТекстЗапроса = "
|with ДатыПерида as (
|SELECT cast(:начДата as date) as [Дата]
|UNION ALL
|SELECT dateadd(dd,1,ДатыПерида.ДАТА) FROM ДатыПерида WHERE ДатыПерида.Дата < :КонДата
|)
|, ДатаТовары as (
|SELECT
|  ДатыПерида.Дата as [ДатаПродажи]
|  , #TempTable.val as [Товар]
|FROM ДатыПерида OPTION (MAXRECURSION 0) CROSS JOIN #TempTable
|)
16 zenon46
 
21.11.19
16:43
(15) да я уже это сделал.
17 zenon46
 
21.11.19
16:49
(15) вот бы теперь еще что-то типа такого нарисовать только используя $РегистрОстатки.ОстаткиТМЦ
18 Sserj
 
21.11.19
17:19
(17) Не совсем понятна мысль. Хочешь получить остатки на начало движения и остатки на конец?
Тогда они будут не корректные так как наверняка есть движения по другим документам.
Для полного отражения просто используй РегистрОстаткиОбороты
19 zenon46
 
21.11.19
18:13
(18) не, остаток на начала каждого дня нужен и все. По дню я так получаю. А вот по интервалу дат, пока не получается.

    |SELECT
    |    Спр.Code as Код,
    |    Спр.ID as [Номенклатура $Справочник.Номенклатура],
    |    isnull(рег.количествоостаток,0) Остаток,
    |     :ВыбДата [Дата $Дата]
    |FROM
    |    $Справочник.Номенклатура as Спр
    |LEFT JOIN
    |    $РегистрОстатки.ОстаткиТМЦ(:ВыбДата,,Фирма = :ВыбФирма AND (Номенклатура IN (SELECT val FROM #TempTable)),(Номенклатура),(Количество)) as Рег ON спр.id=рег.номенклатура     
    |WHERE
    |    Спр.ID IN (SELECT Val FROM #TempTable)
20 Ёпрст
 
21.11.19
18:21
(19) И в чем проблема то ?

Лепишь табличку дат

select dateadd(day,(a.number+b.number*256),:НачДата) as Dat
from master..spt_values a, master..spt_values b
where a.type='P' and b.type='P'

    and b.number<=datediff(day,:НачДата,:КонДата)/256
    and a.number<=(datediff(day,:НачДата,:КонДата)-b.number*256)

к ней присоединяешь остатки на НачДата и обороты с начдата по кон дата, имеешь останки на каждый день
21 Sserj
 
21.11.19
18:43
(19) Ну я бы в таком случае полностью переделал бы запрос.
Журнал тогда не нужен, все брать из регистра, получить таблицу Дата,Товар,НачальныйОстаток,Приход,Расход с начальным остатком у первого дня периода а у остальных дней остаток посчитать с помощью оконных функций. И отдельная колонка в которой приход будет считаться по условию что вид документа ПоступлениеТМЦ_Склад.
Но у меня уже ночь совсем, так что только завтра сочинять получится.
22 Ёпрст
 
21.11.19
19:00
(21) у него 2000 скуль, облом с оконными функциями
23 Sserj
 
21.11.19
19:04
(22) Ну как это. Раз CTE прокатило то минимум 2005, а там уже оконные были.
24 Ёпрст
 
21.11.19
19:15
(23) он твой код не проверял, ибо с прошлых темах, говорил, что 2000 у него
25 Sserj
 
21.11.19
19:22
(24) Вполне вероятен большой облом. Судя по (16) что-то делал, но может делал где то у себя а потом как обновит этим рабочую базу на 2000 и обматерит всех подсказчиков :)
26 zenon46
 
21.11.19
19:38
(25) вот попа, действительно основная база на 2000м, а проверял на 2008.
27 zenon46
 
21.11.19
21:02
Как же теперь переделать этот запрос? Почитал документацию "WITH" SQL2000й не умеет.

    |WITH ДатаПериода AS
    |(
    |SELECT cast(:НачДата as date) AS [Дата]
    |UNION ALL
    |SELECT dateadd(dd,1,ДатаПериода.ДАТА) FROM ДатаПериода WHERE ДатаПериода.Дата < :КонДата
    |),
    |ДатаТовары as
    |(
    |SELECT
    |  CONVERT(varchar(8), ДатаПериода.Дата, 112) AS [ДатаПоступления],
    |  #TempTable.val AS [Номенклатура]
    |FROM ДатаПериода CROSS JOIN #TempTable
    |),
    | Поступления as
    |(
    |SELECT
    |  SUBSTRING(Жур.Date_Time_IDDoc, 1, 8) as [ДатаДок],
    |  $ДокС.Номенклатура as [Номенклатура],
    |  SUM($ДокС.Количество) as [Поступление]
    |FROM
    |  _1SJourn as Жур
    |  JOIN $ДокументСтроки.ПоступлениеТМЦ_Склад as ДокС ON ДокС.IDDoc = Жур.IDDoc
    |  JOIN #TempTable as фильтрТоваров ON $ДокС.Номенклатура = фильтрТоваров.val
    |WHERE
    |   Жур.Date_Time_IDDoc BETWEEN :НачДата AND :КонДата~ AND
    |   Жур.IDDocDef = $ВидДокумента.ПоступлениеТМЦ_Склад AND
    |   Жур.Closed & 1 = 1
    |GROUP BY
    |  SUBSTRING(Жур.Date_Time_IDDoc, 1, 8), $ДокС.Номенклатура
    |)
    |SELECT
    |    спрТовары.Code as [Код $Строка],
    |    ДатаТовары.ДатаПоступления as [Дата $Дата],
    |    ДатаТовары.Номенклатура as [Номенклатура $Справочник.Номенклатура],
    |    COALESCE(Поступления.Поступление, 0) as [Поступление $Число]
    |FROM
    |   ДатаТовары LEFT JOIN Поступления
    |   ON (ДатаТовары.ДатаПоступления = Поступления.ДатаДок
    |   AND ДатаТовары.Номенклатура = Поступления.Номенклатура)
    |   LEFT JOIN $Справочник.Номенклатура AS спрТовары
    |   ON ДатаТовары.Номенклатура = спрТовары.ID
    |ORDER BY
    |  ДатаТовары.ДатаПоступления, спрТовары.DESCR, ДатаТовары.Номенклатура
    |OPTION (MAXRECURSION 0)
    |";
28 zenon46
 
22.11.19
12:10
Как можно переделать эту часть, что бы она запустилась под SQL2000

|with ДатыПерида as (
|SELECT cast(:начДата as date) as [Дата]
|UNION ALL
|SELECT dateadd(dd,1,ДатыПерида.ДАТА) FROM ДатыПерида WHERE ДатыПерида.Дата < :КонДата
|)
|, ДатаТовары as (
|SELECT
|  ДатыПерида.Дата as [ДатаПродажи]
|  , #TempTable.val as [Товар]
|FROM ДатыПерида CROSS JOIN #TempTable
|)
29 Djelf
 
22.11.19
12:37
(28) Сделай справочник с реквизитом дата, заполни на 100 лет вперед и кросс джойни.
Можно еще туда реквизитов типа неделя, месяц, год накидать...
Зачем велосипеды изобретать то?
30 trad
 
22.11.19
12:46
(22) даже если без оконных функций сгенерить табличку дат, твой вариант не прокатит, т.к. ВТ Остатки - параметрическая ВТ, т.е. ее текст зависит, собственно, от первого параметра - от даты.
31 dk
 
22.11.19
12:49
напихай в ВремТЗ нужные даты
потом
Запрос.Подготовить("INSERT INTO #ДатыПерида VALUES(?)");
Запрос.ВыполнитьSQL_ИзТЗ(ВремТЗ);
--------------
SELECT
|  ДатыПерида.Дата as [ДатаПродажи]
|  , #TempTable.val as [Товар]
|FROM #ДатыПерида ДатыПерида CROSS JOIN #TempTable
|)
32 trad
 
22.11.19
12:52
(31) это не пришить к $РегистрОстатки
33 dk
 
22.11.19
12:54
дык ему ж документы надо
34 trad
 
22.11.19
12:57
(33) могу ошибаться, но контекст вроде сейчас такой (17) " вот бы теперь еще что-то типа такого нарисовать только используя $РегистрОстатки.ОстаткиТМЦ"
35 Ёпрст
 
22.11.19
12:58
(32) пришить, через функцию.. токма долго же
36 dk
 
22.11.19
12:58
а, точно уже и остатки треба
тогда в цикле по списку дат
типа

INSERT #Остатки
SELECT
    Спр.ID ,
    рег.количествоостаток Остаток,
     :ВыбДата [Дата $Дата]
FROM $РегистрОстатки.ОстаткиТМЦ(:ВыбДата,,Фирма = :ВыбФирма AND (Номенклатура IN (SELECT val FROM #TempTable)),(Номенклатура),(Количество)) as Рег ON спр.id=рег.номенклатура    
WHERE
    Спр.ID IN (SELECT Val FROM #TempTable)
37 trad
 
22.11.19
13:00
(35) что пришить через функцию? ВТ?
ее текст складывается еще на клиенте до отправки на скл, и ее текст зависит от параметра
38 trad
 
22.11.19
13:04
есть два варианта. В общем виде:
1.
выбрать
начальные остатки
инион
выбрать
обороты за период, сгруппировать по дате

на клиенте умнО перебрать эти остатки-обороты с подсчетом промежуточных остатков

2.
цикл от начдаты до кондаты
запрос остатков на дату
конеццикла
39 trad
 
22.11.19
13:06
Вот если бы жива была ВТ ОстаткиОбороты, то возможен бы был третий вариант, но  она, увы, непотребна
40 Ёпрст
 
22.11.19
13:07
(37) да, не о том подумал.
41 Ёпрст
 
22.11.19
13:08
(38) а зачем считать это на клиенте ? В самом запросе же можно.
42 Ёпрст
 
22.11.19
13:09
И да..это даже в классе ПрямойЗапрос было реализовано
43 zenon46
 
22.11.19
13:14
(33) да нужны документы, с остатками пока откладывается.
44 Sserj
 
22.11.19
13:42
(28) Не хватило терпежу еще выборку документов прилепить в итог. Но там просто левым соединением прилепи так же как остатки:

метаДата = СоздатьОбъект("MetaDataWork");
текстЗапроса = "
|SET NOCOUNT ON
|;  
|
|IF EXISTS(SELECT * FROM tempdb..sysobjects WHERE id = object_id('tempdb..#Период'))
|    DROP TABLE #Период
|;
|CREATE TABLE #Период (val char(9) PRIMARY KEY)
|;
|DECLARE @начДата as DATE = '"+метаДата.ПолучитьСтрИзДаты(начДата)+"'
|DECLARE @конДата as DATE = '"+метаДата.ПолучитьСтрИзДаты(конДата)+"'
|while @начДата <= @конДата
|begin
|insert into #Период values (CONVERT(char(8), @начДата, 112))
|set @начДата = dateadd(DAY, 1, @начДата)
|end
|;
|
|SELECT
|  ДатыТовары.Дата as [Дата $Дата]
|  , ДатыТовары.Товар as [Товар $Справочник.Номенклатура]
|  , Остатки.Остаток as [НачОстаток $Число]
|FROM
|  (SELECT
|    #Период.val as [Дата]
|    , #TempTable.val as [Товар]
|  FROM
|    #Период CROSS JOIN #TempTable
|  ) as ДатыТовары
|  LEFT JOIN (
|    SELECT
|      Рег.Период as [Дата]
|      , Рег.Номенклатура as [Товар]
|      , Рег.КоличествоНачальныйОстаток as [Остаток]
|    FROM
|      $РегистрОстаткиОбороты.ОстаткиТМЦ(:начДата,:конДата~,День,ДвиженияИГраницыПериода
|      , JOIN #TempTable ON Номенклатура = #TempTable.val
|      ,
|      ,(Номенклатура),(Количество)) as Рег
|  ) as Остатки
|  ON (ДатыТовары.Дата = Остатки.Дата AND ДатыТовары.Товар = Остатки.Товар)
|;
|
|DROP TABLE #Период
|;
|SET NOCOUNT OFF
|;
|";
запросДанных.УстановитьТекстовыйПараметр("начДата", начДата);
запросДанных.УстановитьТекстовыйПараметр("конДата", конДата);
тзДанные = запросДанных.ВЫполнитьИнструкцию(текстЗапроса);
45 Sserj
 
22.11.19
13:44
(39) А что случилось с ОстаткиОбороты?
Просто сам ей если честно уже давным давно не пользовался, но интересно что не так.
46 trad
 
22.11.19
13:49
(41) можно и на сервере циклы покрутить
дело вкуса и желаемого баланса нагрузки
я вот не люблю на сервере циклы крутить и делать такие батчи
47 zenon46
 
22.11.19
14:54
(44) Мастерски! На 2008, проблем нет, а вот 2000-снова не запускает Cannot assign a default value to a local variable.
48 zenon46
 
22.11.19
15:06
Да еще вот что заметил https://prnt.sc/q0k7zg, хотя остаток не менялся, т.е. цифра должна быть одна и та же.
49 Sserj
 
22.11.19
15:10
Попробуй заменить
DECLARE @начДата as DATE на DECLARE @конДата as DATETIME
И в определение конДата тоже соответственно.
50 Sserj
 
22.11.19
15:10
(48) Ну это видимо то что trad имел ввиду. Можно тогда свои остатки сделать, чуть попозже попробую.
51 zenon46
 
22.11.19
15:17
(49) в 2008 и так и так все запускается, в 2000м та же ошибка, вот что я нашел по этому поводу https://stackoverflow.com/questions/27060586/cannot-assign-a-default-value-to-a-local-variable-in-sql
52 zenon46
 
22.11.19
15:20
(49) попробовал в Query Analyzer https://prnt.sc/q0kgl8
53 zenon46
 
22.11.19
15:22
(49) а в 2008 и так и так рабоатет
54 zenon46
 
22.11.19
15:43
Все в 2000м я запустил, поступления прикрутил, осталось решить с остатками.
55 Sserj
 
22.11.19
16:05
(54) Вот смотри, на текущий момент получается таблица с остатком на первую дату, оборотами за все даты и отдельно колонка с поступлением.
Дальше мысль не получать эту таблицу а сначала ее запихать во временную, открыть курсор в порядке Товар,Дата и обойти в цикле занося начальный остаток как предыдущий+оборот.
Уже после этого выбрать данные со всеми цифрами.

    |SELECT
    |  ДатыТовары.Дата as [Дата $Дата]
    |  , ДатыТовары.Товар as [Товар $Справочник.Номенклатура]
    |  , SUM(ДатыТовары.НачОст) as [НачОстаток $Число]
    |  , SUM(ДатыТовары.Оборот) as [Оборот $Число]
    |  , SUM(ДатыТовары.Поступления) as [Поступления $Число]
    |FROM
    |  (SELECT
    |    #Период.val as [Дата]
    |    , #TempTable.val as [Товар]
    |    , 0 as [НачОст]
    |    , 0 as [Оборот]
    |    , 0 as [Поступления]
    |  FROM
    |    #Период CROSS JOIN #TempTable
    |
    |  UNION ALL
    |
    |  SELECT
    |    :начДата
    |    , Остатки.Номенклатура
    |    , Остатки.КоличествоОстаток
    |    , 0
    |    , 0
    |  FROM
    |    $РегистрОстатки.ОстаткиТМЦ(:начДата,,Номенклатура IN (SELECT val FROM #TempTable)
    |                               ,(Номенклатура),(Количество)) as Остатки
    |
    |  UNION ALL
    |
    |    SELECT
    |      свернутыеОбороты.Дата
    |      , свернутыеОбороты.Товар
    |      , свернутыеОбороты.НачОст
    |      , свернутыеОбороты.Оборот
    |      , свернутыеОбороты.Поступления
    |    FROM
    |      (SELECT
    |        SUBSTRING(Обороты.Date_Time_Iddoc, 1, 8) as [Дата]
    |        , $Обороты.Номенклатура as [Товар]
    |        , 0 as [НачОст]
    |        , SUM(CASE WHEN Обороты.DEBKRED = 1 THEN $Обороты.Количество ELSE $Обороты.Количество * (-1) END) as [Оборот]
    |        , SUM(CASE WHEN Обороты.IDDOCDEF = $ВидДокумента.ПоступлениеТМЦ_Склад THEN $Обороты.Количество ELSE 0 END) as [Поступления]
    |      FROM
    |        $Регистр.ОстаткиТМЦ as Обороты
    |      WHERE
    |        Обороты.Date_Time_Iddoc between :начДата AND :конДата~
    |        and $Обороты.Номенклатура IN (SELECT val FROM #TempTable)
    |      GROUP BY
    |         SUBSTRING(Обороты.Date_Time_Iddoc, 1, 8), $Обороты.Номенклатура
    |      ) as свернутыеОбороты
    |  ) as ДатыТовары
    |GROUP BY
    |  ДатыТовары.Дата, ДатыТовары.Товар
    |;
56 zenon46
 
22.11.19
16:10
(55) ща попробуем, а нельзя просто как в (19) у меня по одной дате в цикле к таблице дат сделать, по факту мне то нужен остаток только на начало дня, без всяких оборотов за день
ведь простой запрос как раз и показывает остаток на начало дня :
|SELECT
|    Рег.Товар as [Товар $Справочник.Номенклатура],
|    Рег.КоличествоОстаток as Количество
|FROM
|    $РегистрОстатки.ОстаткиТоваров(:ВыбДата,,
|                                 Склад = :ВыбСклад,
|                                 (Товар), (Количество)) as Рег"
57 Sserj
 
22.11.19
16:15
(56) Оборот нужен чтобы посчитать остаток для всех следующих дней. Можно конечно просто вставить колонку с коррелируемым запросом, но на приличных периодах это будет хуже чем черный запрос наверно.
58 zenon46
 
22.11.19
16:21
(57) не пойму почему, но ругается на : Invalid column name 'Date_Time_Iddoc'.
59 Sserj
 
22.11.19
16:25
(58) Значит у региста не стоит флаг Быстрая обработка движений.
Тогда тебе придется регистр соединять с журналом чтобы получить Date_Time_Iddoc и IDDOCDEF. Но конечно настоятельно рекомендую поставить флаг, будет конечно долгая обработка регистра, но запросы по нему начнут намного быстрее выполняться.
60 zenon46
 
22.11.19
16:28
(59) да не стоит, ща включим
61 trad
 
22.11.19
16:28
(56) это невозможно сделать. Почему - я объяснял выше
62 zenon46
 
22.11.19
16:33
(61) да, я уже понял. получается, задача получения остатков на каждый день  не тривиальная ?
63 zenon46
 
22.11.19
16:38
(38) судя по п.2 цикл в запросе на каждый день к регистру остатков ? это то с чего я начал...но на интервале в год не слишком быстро получается, особенно по группе.
64 trad
 
22.11.19
16:40
(62) да
65 trad
 
22.11.19
16:44
(63) п.2 - в лоб, самый простой, но не оптимальный
п.1 он же (41) (55), тут нужно голову поломать, но эффективнее не сделать на имеющейся инфраструктуре
66 zenon46
 
22.11.19
17:00
(59) https://prnt.sc/q0m4to , не считает(
67 Sserj
 
22.11.19
18:15
Вобщем получилось, но пипиц какой длинный :)
68 Sserj
 
22.11.19
18:22
текстЗапроса = "
    |SET NOCOUNT ON
    |;  
    |IF EXISTS(SELECT * FROM tempdb..sysobjects WHERE id = object_id('tempdb..#Период'))
    |    DROP TABLE #Период
    |;
    |CREATE TABLE #Период (val char(9) PRIMARY KEY)
    |;
    |DECLARE @начДата as DATE
    |SET @начДата = '"+метаДата.ПолучитьСтрИзДаты(начДата)+"'
    |DECLARE @конДата as DATE
    |SET @конДата = '"+метаДата.ПолучитьСтрИзДаты(конДата)+"'
    |while @начДата <= @конДата
    |begin
    |insert into #Период values (CONVERT(char(8), @начДата, 112))
    |set @начДата = dateadd(DAY, 1, @начДата)
    |end
    |;
    |
    |IF EXISTS(SELECT * FROM tempdb..sysobjects WHERE id = object_id('tempdb..#Итоги'))
    |    DROP TABLE #Итоги
    |;
    |CREATE TABLE #Итоги (Дата char(8), Товар char(9), НачОст int, Оборот int, Поступления int)
    |;
    |CREATE INDEX DateItem ON #Итоги (Товар,Дата)
    |;
    |
    |INSERT INTO #Итоги (Дата,Товар,НачОст,Оборот,Поступления)
    |SELECT
    |  ДатыТовары.Дата as [Дата $Дата]
    |  , ДатыТовары.Товар as [Товар $Справочник.Номенклатура]
    |  , SUM(ДатыТовары.НачОст) as [НачОстаток $Число]
    |  , SUM(ДатыТовары.Оборот) as [Оборот $Число]
    |  , SUM(ДатыТовары.Поступления) as [Поступления $Число]
    |FROM
    |  (SELECT
    |    #Период.val as [Дата]
    |    , #TempTable.val as [Товар]
    |    , 0 as [НачОст]
    |    , 0 as [Оборот]
    |    , 0 as [Поступления]
    |  FROM
    |    #Период CROSS JOIN #TempTable
    |
    |  UNION ALL
    |
    |  SELECT
    |    :начДата
    |    , Остатки.Номенклатура
    |    , Остатки.КоличествоОстаток
    |    , 0
    |    , 0
    |  FROM
    |    $РегистрОстатки.ОстаткиТМЦ(:начДата,,Номенклатура IN (SELECT val FROM #TempTable)
    |                               ,(Номенклатура),(Количество)) as Остатки
    |
    |  UNION ALL
    |
    |   SELECT
    |      SUBSTRING(Обороты.Date_Time_Iddoc, 1, 8) as [Дата]
    |      , $Обороты.Номенклатура as [Товар]
    |      , 0 as [НачОст]
    |      , SUM(CASE WHEN Обороты.DEBKRED = 1 THEN $Обороты.Количество ELSE $Обороты.Количество * (-1) END) as [Оборот]
    |      , SUM(CASE WHEN Обороты.IDDOCDEF = $ВидДокумента.ПоступлениеТМЦ_Склад THEN $Обороты.Количество ELSE 0 END) as [Поступления]
    |    FROM
    |      $Регистр.ОстаткиТМЦ as Обороты
    |    WHERE
    |      Обороты.Date_Time_Iddoc between :начДата AND :конДата~
    |      and $Обороты.Номенклатура IN (SELECT val FROM #TempTable)
    |    GROUP BY
    |       SUBSTRING(Обороты.Date_Time_Iddoc, 1, 8), $Обороты.Номенклатура
    |  ) as ДатыТовары
    |GROUP BY
    |  ДатыТовары.Дата, ДатыТовары.Товар
    |;
    |DROP TABLE #Период
    |
    |;
    |
    |DECLARE @CurrentMove as int
    |DECLARE @CurrentQty as int
    |DECLARE @NextQty as int
    |DECLARE @CurrentItem as char(9)
    |DECLARE @LastItem as char(9)
    |SET @LastItem = ''
    |
    |DECLARE cur CURSOR LOCAL FORWARD_ONLY FOR
    |SELECT Оборот,Товар,НачОст
    |FROM #Итоги
    |ORDER BY Товар,Дата
    |FOR UPDATE
    |
    |OPEN cur
    |FETCH NEXT FROM cur INTO @CurrentMove, @CurrentItem, @CurrentQty
    |WHILE @@FETCH_STATUS = 0
    |BEGIN
    |  IF NOT @LastItem = @CurrentItem
    |    BEGIN
    |       SET @NextQty = @CurrentQty + @CurrentMove
    |       SET @LastItem = @CurrentItem
    |    END
    |  ELSE
    |    BEGIN
    |      UPDATE #Итоги
    |      SET НачОст = @NextQty
    |      WHERE CURRENT OF cur
    |    
    |      SET @NextQty = @NextQty + @CurrentMove
    |    END
    |FETCH NEXT FROM cur INTO @CurrentMove, @CurrentItem, @CurrentQty
    |END
    |;
    |CLOSE cur
    |;
    |deallocate cur
    |;
    |
    |SELECT
    |  Итоги.Дата as [Дата $Дата]
    |  , Итоги.Товар as [Товар $Справочник.Номенклатура]
    |  , Итоги.НачОст as [НачОст $Число]
    |  , Итоги.Поступления as [Поступления $Число]
    |  , Товары.DESCR as [Товар_Наименование $Строка]
    |FROM
    |  #Итоги as Итоги
    |  JOIN $Справочник.Номенклатура as Товары
    |  ON Итоги.Товар = Товары.ID
    |ORDER BY
    |  Итоги.Дата, Товары.DESCR, Товары.ID
    |;
    |
    |DROP TABLE #Итоги
    |;
    |SET NOCOUNT OFF
    |";
69 Djelf
 
22.11.19
18:23
(65) Эффективно не сделать, это точно!!!
Ну например вот по банку, по дням. "Даты" это справочник, чтобы не парится с созданием таблиц.
Но из-за НарастающегоИтога время выполнения будет расти в геометрической прогрессии.
Зато без временных таблиц ;)

SELECT
    Дата [Дата :Дата]
    ,НачОст+ifnull(НарастающийОборот,0) [НачОст :Число.15.2]
    ,Приход [Приход :Число.15.2]
    ,Расход [Расход :Число.15.2]
    ,НачОст+ifnull(Приход,0)-ifnull(Расход,0)+ifnull(НарастающийОборот,0) [КонОст :Число.15.2]
    ,НарастающийОборот [НарастающийОборот :Число.15.2]
FROM(SELECT
    Даты.Дата     Дата
    ,sum(Итоги.СуммаРуб)  НачОст
    ,(SELECT sum(case DEBKRED when 1 then -СуммаРуб else СуммаРуб end)
    FROM Регистр_Банк WHERE DATE  BETWEEN :НачДата AND date(Даты.Дата,'-1 day'))  НарастающийОборот
    ,(SELECT sum(case DEBKRED when 1 then 0 else СуммаРуб end)
    FROM Регистр_Банк WHERE DATE = Даты.Дата)  Приход
    ,(SELECT sum(case DEBKRED when 1 then СуммаРуб else 0 end)
    FROM Регистр_Банк WHERE DATE = Даты.Дата)  Расход
FROM Справочник_Даты AS Даты
JOIN РегистрИтоги_Банк AS  Итоги ON Итоги.PERIOD = '20190701'
WHERE Даты.Дата BETWEEN :НачДата AND  :КонДата
GROUP BY Даты.Дата)
70 Djelf
 
22.11.19
18:26
+(69) Это я на sqlite так извернулся, без рекурсивных и оконных функций, которые уже есть и работают.
71 Djelf
 
22.11.19
18:31
(68) А это все равно перебор, только на стороне сервера sql.
Посмотри как в классе ПоставщикДанных это сделано, в нем решение более изящное, через триггеры.
72 Sserj
 
22.11.19
18:41
(71) Ну в данном случае я не уверен что куча подзапросов к одному регистру да еще в многопользовательском режиме будет быстрее чем перебор одной итоговой таблицы. Тем более таблица своя временная и на нее точно в это время никто покушаться не будет.
73 Sserj
 
22.11.19
18:49
(66) Ай как всегда в дебкредах запутался :)
В запрос поменять "CASE WHEN Обороты.DEBKRED = 1" на "CASE WHEN Обороты.DEBKRED = 0"
74 zenon46
 
23.11.19
12:04
(73) гонял туда сюда, сверял цифры наткнулся вот на такое, не понял https://prnt.sc/q0xfsp, насколько я понял, оборот построился по двум юр лицам в базе, так как именно тогда и получается вот такой остаток -2
75 zenon46
 
23.11.19
12:15
Вот рабочий скрин : https://prnt.sc/q0xj49, остатки то я по юр.лицу фильтранул, а вот обороты берутся похоже по всем юрлицам.
76 zenon46
 
23.11.19
13:11
Добавил фильтр по фирме вроде все ровно, проверяю:

    |UNION ALL
    |   SELECT
    |      SUBSTRING(Обороты.Date_Time_Iddoc,1,8)                                                                     AS [Дата],
    |      $Обороты.Номенклатура                                                                                       AS [Товар],
    |      0                                                                                                         AS [Остаток],
    |      SUM(CASE WHEN Обороты.DEBKRED = 0 THEN $Обороты.Количество ELSE $Обороты.Количество * (-1) END)             AS [Оборот],
    |      SUM(CASE WHEN Обороты.IDDOCDEF = $ВидДокумента.ПоступлениеТМЦ_Склад THEN $Обороты.Количество ELSE 0 END) AS [Поступление]
    |    FROM
    |      $Регистр.ОстаткиТМЦ as Обороты
    |     LEFT JOIN
    |        $Справочник.Фирмы as СпрФ ON СпрФ.ID = $Обороты.Фирма
    |    WHERE
    |      Обороты.Date_Time_Iddoc between :НачДата AND :КонДата~ AND
    |      $Обороты.Номенклатура IN (SELECT val FROM #TempTable) AND
    |       $Обороты.Фирма = :ВыбФирма
    |    GROUP BY
    |       SUBSTRING(Обороты.Date_Time_Iddoc, 1, 8), $Обороты.Номенклатура
    |  ) as ДатыТовары
77 Sserj
 
23.11.19
16:15
(76) Вот это совсем лишнее:
LEFT JOIN $Справочник.Фирмы as СпрФ ON СпрФ.ID = $Обороты.Фирма

Нигде же не используешь этот СпрФ. Только зря ресурсы на соединение тратятся.
78 zenon46
 
23.11.19
17:21
(77) Согласен