|
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) Эффективно не сделать, это точно!!!
Ну например вот по банку, по дням. "Даты" это справочник, чтобы не парится с созданием таблиц. Но из-за НарастающегоИтога время выполнения будет расти в геометрической прогрессии. Зато без временных таблиц ;)
|
|||
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) Согласен
|
Форум | Правила | Описание | Объявления | Секции | Поиск | Книга знаний | Вики-миста |