Запрос "Приход-Расход"
-
- Сообщения: 53
- Зарегистрирован: 11 мар 2005, 15:44
Для себя я как-то сравнивал скорость прямой выборки и через процедуру (простейший запрос из одной таблицы). Разница оказалась примерно 3 раза. Поэтому для себя я решил больших выборок через процедуры не делать.Akella писал(а):Оказалось, что вьюха работает быстрее, заметно, очень заметно...
328 ms против выборки из процедуры - >5 sek.
-
- Заслуженный разработчик
- Сообщения: 644
- Зарегистрирован: 15 фев 2005, 11:34
Потому что твой подход в корне неверен.Akella писал(а):Оказалось, что вьюха работает быстрее, заметно, очень заметно...
328 ms против выборки из процедуры - >5 sek.
это наверное связано с вложенными запросами, но и там и там вложенные запросы...
Надо чтоб процедура отработала ОДИН раз, потом к ней приджойнить лефтом названия из справочника, а ты же отбираешь все позиции справочника и на КАЖДУЮ позицию заствляешь сервер ЧЕТЫРЕЖДЫ исполнить процедуру, даже если по этой позиции номенклатуры нет ни остаков, ни движения. Тебе надо подойти с другого боку.
Примерно так
Вых. параметры: ид, сальдо, дебет, кредит, сальдо
вход: дата_от, дата_до
for select from приход where отсев_по_дате
do
begin
if дата_прихода < дата_от
then сальдо=колво, дебет = 0, кредит = 0 , сальдо = 0
else сальдо=0, дебет = колво, кредит = 0 , сальдо = 0
suspend
end
аналогичный фор селект для расхода.
и возможно подобные блоки для "красноты", если ты ее явно в программе не запретил.
В итоге при распечатке оборотки такая процедура должна будет сработать один раз, к ней же можно будет потом пришпилить и названия номенклатуры, помимо ид.
-
- Заслуженный разработчик
- Сообщения: 644
- Зарегистрирован: 15 фев 2005, 11:34
Не совсем я понял Вашу идею, а может и совсем не понял, простите,for select from приход where отсев_по_дате
do
begin
if дата_прихода < дата_от
then сальдо=колво, дебет = 0, кредит = 0 , сальдо = 0
else сальдо=0, дебет = колво, кредит = 0 , сальдо = 0
suspend
end
аналогичный фор селект для расхода.
и возможно подобные блоки для "красноты", если ты ее явно в программе не запретил.
я вижу
дебет = 0, кредит = 0
-
- Заслуженный разработчик
- Сообщения: 644
- Зарегистрирован: 15 фев 2005, 11:34
Отбираем приход, если пришло ДО пишем на остаток, пришло после в приход, колоночка расход заполнится из следующего блока фор селект, который по таблице расходов пройдется. Тут хорошо иметь хранимый агрегат "остаток на определенное число", чтоб не шерстить ВСЮ таблицу приходов со времен Царя гороха, а индексом отсечь старое и и прибавить сохраненные остатки. А конечное сальдо можно и на клиенте подсчитать имея 3 колонки из ХП, или в конечное сальдо писать все что израсходуется после даты_конца, но тогда надо все неизрасходованое закрепить за специальной накладной.
После выбоки их этой ХП результат надо будет сгруппировать, схлопнуть по ИД и дебет и кредит встанут в одну строку.
Я даю только одно из возможных направлений, а не готовый пирожок, который можно взять и слопать
После выбоки их этой ХП результат надо будет сгруппировать, схлопнуть по ИД и дебет и кредит встанут в одну строку.
Я даю только одно из возможных направлений, а не готовый пирожок, который можно взять и слопать

вот вот, я и пытаюсь понять Ваше направление...
я так подозреваю, что Вы считаете, что я неверно подситываю сальдо на начало/конец и обороты по приходу и рсаходу, исзходя из приведенного ранее кода вьюхи
и самого кода выборки из этой вьюхи
я так подозреваю, что Вы считаете, что я неверно подситываю сальдо на начало/конец и обороты по приходу и рсаходу, исзходя из приведенного ранее кода вьюхи
Код: Выделить всё
CREATE VIEW VW_OBOROTKA(
PREPARAT_ID,
A_DATE,
AD_AMOUNT,
PRICE_RETAIL)
AS
SELECT
AD.PREPARAT_ID,
A.DATE_INPUT,
AD.AMOUNT,
AD.PRICE_RETAIL
FROM ARRIVAL_DET AD
LEFT JOIN ARRIVAL A ON (AD.arrival_ID = A.ID)
UNION ALL
SELECT
ED.PREPARAT_ID,
E.DATE_OUT,
-ED.AMOUNT,
-ED.PRICE_RETAIL
FROM EXPENSE_DET ED
LEFT JOIN EXPENSE E ON (ED.EXPENSE_ID = E.ID)
;
Код: Выделить всё
SELECT
P.ID,
P.Name,
--Сальдо на начало
(SELECT SUM(O.AD_AMOUNT) FROM VW_OBOROTKA O
where (O.PREPARAT_ID = P.id) and (O.A_DATE < :DateBegin)
GROUP BY O.PREPARAT_ID) AS AmountOnBegin,
(SELECT SUM(O.PRICE_RETAIL) FROM VW_OBOROTKA O
where (O.PREPARAT_ID = P.id) and (O.A_DATE < :DateBegin)
GROUP BY O.PREPARAT_ID) AS PriceOnBegin,
--Обороты по приходу
(SELECT SUM(O.AD_AMOUNT) FROM VW_OBOROTKA O
where (O.PREPARAT_ID = P.id) and (O.AD_AMOUNT > 0)and (O.A_DATE >= :DateBegin) and (O.A_DATE <= :DateEnd)
GROUP BY O.PREPARAT_ID) AS ArrivalAmount,
(SELECT SUM(O.PRICE_RETAIL) FROM VW_OBOROTKA O
where (O.PREPARAT_ID = P.id) and (O.PRICE_RETAIL > 0)and (O.A_DATE >= :DateBegin) and (O.A_DATE <= :DateEnd)
GROUP BY O.PREPARAT_ID) AS ArrivalPrice,
--Обороты по расходу
(SELECT -SUM(O.AD_AMOUNT) FROM VW_OBOROTKA O
where (O.PREPARAT_ID = P.id) and (O.AD_AMOUNT < 0) and (O.A_DATE >= :DateBegin) and (O.A_DATE <= :DateEnd)
GROUP BY O.PREPARAT_ID) AS ExpenseAmount,
(SELECT -SUM(O.PRICE_RETAIL) FROM VW_OBOROTKA O
where (O.PREPARAT_ID = P.id) and (O.PRICE_RETAIL < 0) and (O.A_DATE >= :DateBegin) and (O.A_DATE <= :DateEnd)
GROUP BY O.PREPARAT_ID) AS ExpensePrice,
--Сальдо на конец
(SELECT SUM(O.AD_AMOUNT) FROM VW_OBOROTKA O
where (O.PREPARAT_ID = P.id) and (O.A_DATE <= :DateEnd)
GROUP BY O.PREPARAT_ID) AS AmountOnEnd,
(SELECT SUM(O.PRICE_RETAIL) FROM VW_OBOROTKA O
where (O.PREPARAT_ID = P.id) and (O.A_DATE <= :DateEnd)
GROUP BY O.PREPARAT_ID) AS PriceOnEnd
FROM PREPARATS P
я так подозреваю, что в Вашем псевдокоде
не хватает временных переменных, в короые всё и собирается а потом... я пока даже не представляю код процедуры, буду думать... 
Код: Выделить всё
Вых. параметры: ид, сальдо, дебет, кредит, сальдо
вход: дата_от, дата_до
for select from приход where отсев_по_дате
do
begin
if дата_прихода < дата_от
then сальдо=колво, дебет = 0, кредит = 0 , сальдо = 0
else сальдо=0, дебет = колво, кредит = 0 , сальдо = 0
suspend
end

-
- Заслуженный разработчик
- Сообщения: 644
- Зарегистрирован: 15 фев 2005, 11:34
Честно говоря я не вчитывался в твой код досконально, я лишь упираю на то что надо по максимуму весь отсев сделать на первом этапе. Ты же набираешь достаточно громоздкую вьюху, потом из нее многократно отбираешь.
Я не утверждаю что твой вариант считает неправильно, просто конструктивно он гораздо более ресурсоемкий для сервера.
Суть моего подхода в том что при первоначальной выборке я сразу отсеиваю все те документы которые не нужны: как то что пришло после даты_конца, что ушло до даты_начала, то что пришло-ушло до сохраненного агрегата(если таковой будет, точка актуализации, так называемая).
Далее группировка будет идти на достаточно малом кол-ве записей, бухгалтера обычно собирают оборотку за месяц, кране редко за более длительный период, то есть после пары лет работы программы сбор оборотки будет оперировать всего 5% данных из таблиц первичных документов, чего не скажешь про выборку через вью без отсева по дате.
Я не утверждаю что твой вариант считает неправильно, просто конструктивно он гораздо более ресурсоемкий для сервера.
Суть моего подхода в том что при первоначальной выборке я сразу отсеиваю все те документы которые не нужны: как то что пришло после даты_конца, что ушло до даты_начала, то что пришло-ушло до сохраненного агрегата(если таковой будет, точка актуализации, так называемая).
Далее группировка будет идти на достаточно малом кол-ве записей, бухгалтера обычно собирают оборотку за месяц, кране редко за более длительный период, то есть после пары лет работы программы сбор оборотки будет оперировать всего 5% данных из таблиц первичных документов, чего не скажешь про выборку через вью без отсева по дате.
-
- Заслуженный разработчик
- Сообщения: 644
- Зарегистрирован: 15 фев 2005, 11:34
Не усложняй, код вполне самодостаточен, переменных больше не надо, группировку сделаешь когда будешь селектить из этой процедуры. Всего 2 этапа: сначала фильтрация и расстановка галочек, только это от процедуры и требуется, потом группировка и подстановка наименований.не хватает временных переменных, в короые всё и собирается а потом... я пока даже не представляю код процедуры, буду думать...
По крайней мере у меня так работает, оптимизация на то, что 99% процентов обороток собираются за месяц, а не более длинный период.
Уважаемый Иван, пытаюсь вот реализовать Ваше "направление" но..
IBExpert ругается на оператор DO, что же не так, вроде "ваш" код...
IBExpert ругается на оператор DO, что же не так, вроде "ваш" код...
Код: Выделить всё
--тело процедуры
begin
FOR SELECT
AD.PREPARAT_ID,
AD.AMOUNT,
AD.PRICE_RETAIL
FROM ARRIVAL_DET AD
LEFT JOIN ARRIVAL A ON (AD.arrival_ID = A.ID)
where (a.DATE_INPUT >= :DATE_BEG) AND (A.DATE_INPUT < :DATE_END)
do
begin
if a.DATE_INPUT < :DATE_BEG then
:S_BEG_AMOUNT = ad.AMOUNT,
end;
Invalid token.
Dynamic SQL Error.
SQL error code = -104.
Token unknown - line 24, column 3.
do.
-
- Заслуженный разработчик
- Сообщения: 644
- Зарегистрирован: 15 фев 2005, 11:34