Сложный запрос

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

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

Ответить
Lars
Сообщения: 86
Зарегистрирован: 21 дек 2005, 16:34

Сложный запрос

Сообщение Lars » 15 авг 2007, 14:41

Таблица табеля содержит поля:

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

TAB_DATE - день месяца
ADDIT_MARK-праздники/выходные (1-суббота, 3-воскр, 2 - праздник)
TIME_LENGTH_P-продолжительность смены
TODAY_STATE-состояние дня (0-выход на работу, >0 - невыходы на работу по различным причинам)
ID_CARD- карточка сотрудника
Есть таблица списка периодов
ID_CARD - карточка
DATE_START-дата нач. период
DATE_END-дата ок. периода
CATEGORY- категория сотрудника

Теперь вот такой запрос:
Необходимо вычислить дни, которые "Выходные и праздничные дни". Замечу, в один день может смена растянуться на два дня. то есть отработанные дни будут зацеплять и рабочий день и выходной. Например: выход в пятницу в 19-00,, продолжительностью 14 часов. Таким образом след день (суббота) не должен входить в список.

Теперь. Написана функция, которая высчитывает на сколько дней переходит смена.

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

CREATE PROCEDURE SP_GET_DAYS (
    IN_DATE DATE,
    IN_TIME TIME,
    IN_LEN DECIMAL(15,2),
    IN_GRAPHIC INTEGER)
RETURNS (
    OUT_DAYS INTEGER)
AS
DECLARE VARIABLE D1 TIMESTAMP;
DECLARE VARIABLE D2 TIMESTAMP;
DECLARE VARIABLE DTIME0 TIME;
DECLARE VARIABLE LENTEMP DECIMAL(9,4);
begin
   /*Процедура расчета отработанных дней с учетом переноса*/
   IF (IN_TIME IS NULL  OR IN_LEN=0) THEN
   BEGIN
        OUT_DAYS=0;
        SUSPEND;
        EXIT;
   end

   DTIME0 = '00:00:00';


   D1 =  CAST(IN_TIME AS TIMESTAMP);
   D2 =  CAST(IN_TIME AS TIMESTAMP);

   D2 = D1+ IN_LEN/24.00;

   --есть ли перенос на след. день?
   if ( CAST(D1 as date) != CAST(D2 AS DATE) ) then
   begin
        --перенос есть

        --првоерим, есть ли переход между месяцами?
        if (EXTRACT(MONTH FROM IN_DATE) != EXTRACT(MONTH FROM IN_DATE+1) ) then
        begin
            --переход есть, и пока считаем, что будущий месяц не учитывается в этом.
            OUT_DAYS = 1;
        end
        else
        begin
            OUT_DAYS = 2;
        end
   end
   else
   begin
   --переноса нет между днями, все в пределах одного
        OUT_DAYS = 1;
   end
  suspend;
end
теперь самое интересное. Мое решение проблемы.



Группировка идет по категориям. Сколько в каждой категории было выходных и праздников.

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

SELECT
    VIEW_CONSOLIDATE.CATEGORY,
   SUM( (
    SELECT COUNT(TABEL.ID)
    FROM TABEL TBL
    WHERE
            (TBL.ADDIT_MARK > 0) --ищем только субботы, воскр, празд
    AND
            (TBL.TIME_LENGTH_P = 0)--тогда, когда смены нет в день
    AND
            (TBL.TODAY_STATE not in( 8,9))--не учитываем отпуска
    AND
            (TABDATE BETWEEN VIEW_CONSOLIDATE.DATESTART AND VIEW_CONSOLIDATE.DATEEND)
    AND
            (TBL.ID_CARD = VIEW_CONSOLIDATE.ID_CARD)
    AND
            NOT EXISTS ( SELECT ID FROM TABEL T WHERE t.TABDATE = TBL.TABDATE AND T.ID_CARD = TBL.ID_CARD AND T.SMENA_LEN =1 )--не учитываем дни с переносами с прошлого месяца

    AND
            (SELECT OUT_DAYS FROM SP_GET_DAYS(TBL.TABDATE-1, (SELECT START_TIME_P FROM TABEL T1 WHERE T1.TABDATE = TBl.TABDATE-1 AND T1.ID_CARD = TBL.ID_CARD AND T1.SMENA_LEN=0)  ,(SELECT T1.TIME_LENGTH_P FROM TABEL T1 WHERE T1.TABDATE = TBl.TABDATE-1 AND T1.ID_CARD = TBL.ID_CARD AND T1.SMENA_LEN=0),TBL.ID_GRAPHIC)) !=2
--учитываем дни, где смена не 2 дня
    AND
            TBL.TABDATE-1 >VIEW_CONSOLIDATE.DATESTART 

    )) CNT

FROM VIEW_CONSOLIDATE

WHERE
    VIEW_CONSOLIDATE.DATESTART >='01.08.2007'
AND
    VIEW_CONSOLIDATE.DATEEND <='31.08.2007'
AND
    VIEW_CONSOLIDATE.ID_GROUP in (SELECT ID FROM SP_REC_GROUPS(0))

GROUP BY  VIEW_CONSOLIDATE.CATEGORY

ORDER BY VIEW_CONSOLIDATE.CATEGORY

Запрос вызывает несколько вложенных запросов, а также обрабатывает вложенную процедуру. Насколько такой запрос корректен и реален для работы.
Сотрудников(периодов): 300.
Записей в табеле: 31*300=9300 на один месяц.

WildSery
Заслуженный разработчик
Сообщения: 1738
Зарегистрирован: 05 июн 2006, 16:19

Сообщение WildSery » 15 авг 2007, 15:52

Читал через строку. Это ты просто похвастался? Чего-то вопроса не увидел :)

Обычно в отдельной таблице просто ведётся производственный календарь с нормами работы, и к нему легко клеятся периоды любого сотрудника. А уж после такой простой склейки сразу видно, где он работал в выходные, а где он в отпуске.

Lars
Сообщения: 86
Зарегистрирован: 21 дек 2005, 16:34

Сообщение Lars » 15 авг 2007, 18:00

WildSery писал(а):Читал через строку. Это ты просто похвастался? Чего-то вопроса не увидел :)

Обычно в отдельной таблице просто ведётся производственный календарь с нормами работы, и к нему легко клеятся периоды любого сотрудника. А уж после такой простой склейки сразу видно, где он работал в выходные, а где он в отпуске.
А так получилось? Конечно же не такая была цель.

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

Вопрос об оптимизации и более простой реализации.
Обычно в отдельной таблице просто ведётся производственный календарь с нормами работы, и к нему легко клеятся периоды любого сотрудника. А уж после такой простой склейки сразу видно, где он работал в выходные, а где он в отпуске.
Ну собственно оно так и есть. Есть особенность: сотр. может работать 3 дня подряд в ночь, из-за чего получается ситуация (у нас принятая), что отработано 4 дня! Из-за этого и не получается просто посчитать день как праздничный/выходной. Он может являться продолжением смены с прошлого дня.

Табель пишется для большого завода с кучей всякихз ньансов и разночтений для каждого подразделения.

WildSery
Заслуженный разработчик
Сообщения: 1738
Зарегистрирован: 05 июн 2006, 16:19

Сообщение WildSery » 15 авг 2007, 18:49

Отработал 3 дня, но считаем 4. Нда. Что-то тут не так.
Сколько отработал, столько и должно быть. Ничего "считаться" не должно. И уж тем более забивать прямо в запросы смены - нехорошо.
Если 4-й день прибавляется для "особого" расчёта - так это нужно ночные смены по другому тарифу просто считать.

Lars
Сообщения: 86
Зарегистрирован: 21 дек 2005, 16:34

Сообщение Lars » 16 авг 2007, 06:57

WildSery писал(а):Отработал 3 дня, но считаем 4. Нда. Что-то тут не так.
Такая специфика расчета сводки, принятой на заводе. Хотя и меня поначалу удивляло сильно.

Однако, чтобы не пугать уж совсем скажу - з.п. начисляется на часы, а не дни. Такой расчет необходим для статистики. Это своего рода перевод килограммов в тонны. Не можем же считать для некоторых в днях, а у других в сменах? даже если и можем - надо ТАК КАК ЕСТЬ.

И потом, в 19:00 начата смена, длится 14 часов, получается, что захватывает и первый день и второй. Вот здесь и смысл.

Ответить