Запрос "Приход-Расход"

Запросы, планы, оптимизация запросов, ...

Модераторы: kdv, CyberMax

Akella
Сообщения: 95
Зарегистрирован: 13 дек 2005, 12:07

Сообщение Akella » 15 май 2006, 16:07

Оказалось, что вьюха работает быстрее, заметно, очень заметно...
328 ms против выборки из процедуры - >5 sek.

это наверное связано с вложенными запросами, но и там и там вложенные запросы...

Andrew Sagulin
Сообщения: 53
Зарегистрирован: 11 мар 2005, 15:44

Сообщение Andrew Sagulin » 15 май 2006, 17:07

Akella писал(а):Оказалось, что вьюха работает быстрее, заметно, очень заметно...
328 ms против выборки из процедуры - >5 sek.
Для себя я как-то сравнивал скорость прямой выборки и через процедуру (простейший запрос из одной таблицы). Разница оказалась примерно 3 раза. Поэтому для себя я решил больших выборок через процедуры не делать.

Ivan_Pisarevsky
Заслуженный разработчик
Сообщения: 644
Зарегистрирован: 15 фев 2005, 11:34

Сообщение Ivan_Pisarevsky » 16 май 2006, 10:07

Akella писал(а):Оказалось, что вьюха работает быстрее, заметно, очень заметно...
328 ms против выборки из процедуры - >5 sek.

это наверное связано с вложенными запросами, но и там и там вложенные запросы...
Потому что твой подход в корне неверен.
Надо чтоб процедура отработала ОДИН раз, потом к ней приджойнить лефтом названия из справочника, а ты же отбираешь все позиции справочника и на КАЖДУЮ позицию заствляешь сервер ЧЕТЫРЕЖДЫ исполнить процедуру, даже если по этой позиции номенклатуры нет ни остаков, ни движения. Тебе надо подойти с другого боку.

Примерно так

Вых. параметры: ид, сальдо, дебет, кредит, сальдо
вход: дата_от, дата_до

for select from приход where отсев_по_дате
do
begin
if дата_прихода < дата_от
then сальдо=колво, дебет = 0, кредит = 0 , сальдо = 0
else сальдо=0, дебет = колво, кредит = 0 , сальдо = 0
suspend
end

аналогичный фор селект для расхода.

и возможно подобные блоки для "красноты", если ты ее явно в программе не запретил.

В итоге при распечатке оборотки такая процедура должна будет сработать один раз, к ней же можно будет потом пришпилить и названия номенклатуры, помимо ид.

Akella
Сообщения: 95
Зарегистрирован: 13 дек 2005, 12:07

Сообщение Akella » 16 май 2006, 13:22

а обороты...

Ivan_Pisarevsky
Заслуженный разработчик
Сообщения: 644
Зарегистрирован: 15 фев 2005, 11:34

Сообщение Ivan_Pisarevsky » 16 май 2006, 14:05

Akella писал(а):а обороты...
В смысле? Или это про схлопнуть результаты выборки ХП "GROUP BY ID"...

Akella
Сообщения: 95
Зарегистрирован: 13 дек 2005, 12:07

Сообщение Akella » 16 май 2006, 14:18

for select from приход where отсев_по_дате
do
begin
if дата_прихода < дата_от
then сальдо=колво, дебет = 0, кредит = 0 , сальдо = 0
else сальдо=0, дебет = колво, кредит = 0 , сальдо = 0
suspend
end

аналогичный фор селект для расхода.

и возможно подобные блоки для "красноты", если ты ее явно в программе не запретил.
Не совсем я понял Вашу идею, а может и совсем не понял, простите,
я вижу
дебет = 0, кредит = 0

Ivan_Pisarevsky
Заслуженный разработчик
Сообщения: 644
Зарегистрирован: 15 фев 2005, 11:34

Сообщение Ivan_Pisarevsky » 16 май 2006, 14:40

Отбираем приход, если пришло ДО пишем на остаток, пришло после в приход, колоночка расход заполнится из следующего блока фор селект, который по таблице расходов пройдется. Тут хорошо иметь хранимый агрегат "остаток на определенное число", чтоб не шерстить ВСЮ таблицу приходов со времен Царя гороха, а индексом отсечь старое и и прибавить сохраненные остатки. А конечное сальдо можно и на клиенте подсчитать имея 3 колонки из ХП, или в конечное сальдо писать все что израсходуется после даты_конца, но тогда надо все неизрасходованое закрепить за специальной накладной.
После выбоки их этой ХП результат надо будет сгруппировать, схлопнуть по ИД и дебет и кредит встанут в одну строку.

Я даю только одно из возможных направлений, а не готовый пирожок, который можно взять и слопать :wink:

Akella
Сообщения: 95
Зарегистрирован: 13 дек 2005, 12:07

Сообщение Akella » 16 май 2006, 14:48

вот вот, я и пытаюсь понять Ваше направление...
я так подозреваю, что Вы считаете, что я неверно подситываю сальдо на начало/конец и обороты по приходу и рсаходу, исзходя из приведенного ранее кода вьюхи

Код: Выделить всё

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

Akella
Сообщения: 95
Зарегистрирован: 13 дек 2005, 12:07

Сообщение Akella » 16 май 2006, 15:04

я так подозреваю, что в Вашем псевдокоде

Код: Выделить всё

Вых. параметры: ид, сальдо, дебет, кредит, сальдо 
вход: дата_от, дата_до 

for select from приход where отсев_по_дате 
do 
begin 
if дата_прихода < дата_от 
then сальдо=колво, дебет = 0, кредит = 0 , сальдо = 0 
else сальдо=0, дебет = колво, кредит = 0 , сальдо = 0 
suspend 
end 
не хватает временных переменных, в короые всё и собирается а потом... я пока даже не представляю код процедуры, буду думать... :?

Ivan_Pisarevsky
Заслуженный разработчик
Сообщения: 644
Зарегистрирован: 15 фев 2005, 11:34

Сообщение Ivan_Pisarevsky » 16 май 2006, 15:05

Честно говоря я не вчитывался в твой код досконально, я лишь упираю на то что надо по максимуму весь отсев сделать на первом этапе. Ты же набираешь достаточно громоздкую вьюху, потом из нее многократно отбираешь.
Я не утверждаю что твой вариант считает неправильно, просто конструктивно он гораздо более ресурсоемкий для сервера.
Суть моего подхода в том что при первоначальной выборке я сразу отсеиваю все те документы которые не нужны: как то что пришло после даты_конца, что ушло до даты_начала, то что пришло-ушло до сохраненного агрегата(если таковой будет, точка актуализации, так называемая).
Далее группировка будет идти на достаточно малом кол-ве записей, бухгалтера обычно собирают оборотку за месяц, кране редко за более длительный период, то есть после пары лет работы программы сбор оборотки будет оперировать всего 5% данных из таблиц первичных документов, чего не скажешь про выборку через вью без отсева по дате.

Ivan_Pisarevsky
Заслуженный разработчик
Сообщения: 644
Зарегистрирован: 15 фев 2005, 11:34

Сообщение Ivan_Pisarevsky » 16 май 2006, 15:24

не хватает временных переменных, в короые всё и собирается а потом... я пока даже не представляю код процедуры, буду думать...
Не усложняй, код вполне самодостаточен, переменных больше не надо, группировку сделаешь когда будешь селектить из этой процедуры. Всего 2 этапа: сначала фильтрация и расстановка галочек, только это от процедуры и требуется, потом группировка и подстановка наименований.

По крайней мере у меня так работает, оптимизация на то, что 99% процентов обороток собираются за месяц, а не более длинный период.

Akella
Сообщения: 95
Зарегистрирован: 13 дек 2005, 12:07

Сообщение Akella » 16 май 2006, 16:41

Уважаемый Иван, пытаюсь вот реализовать Ваше "направление" но..
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.

Ivan_Pisarevsky
Заслуженный разработчик
Сообщения: 644
Зарегистрирован: 15 фев 2005, 11:34

Сообщение Ivan_Pisarevsky » 16 май 2006, 16:50

"into" кто ставить будет? :)

Ответить