2016-05-05 6 views
0

У меня есть база данных SQL Server со сменной обработанной информацией для каждого сотрудника. Основная таблица (так называемая «shift_worked») структурирована как следующие:SQL Server - сумма до достижения порогового значения

id employee_id period day hours 
1  154   6   5  4.5 
2  156   7   12 7.25 
3  154   7   6  8 
4  154   7   7  6.75 
5  142   7   7  5.5 
6  156   8   12 7.1 

мне нужно определить период и день, в котором каждый сотрудник попал порог работы 500 часов ... или, конечно, быть в состоянии определить, кто еще не достиг этого порога.

Я пытаюсь посмотреть на рекурсивные запросы, чтобы справиться с этим, но я просто не могу его обработать.

***** EDIT ***** Я только предоставил его в комментариях, но db - SQL Server 2008 - к сожалению, ни одна из приятных команд 2012 года не будет работать.

+2

Какой у вас запрос? – JeffO

+0

Позвольте мне уточнить ... «пытаясь взглянуть на рекурсивные запросы», я имею в виду, что я нигде не имею ничего, что имеет смысл. Я могу опубликовать бред, если это поможет. – Steven

+0

Хорошо, это имеет смысл ... но это на самом деле очень большая таблица, и расчет занимает очень много времени. Можно ли ускорить работу, не выработав общую сумму, как только достигнут порог? – Steven

ответ

0

Наша таблица выглядит следующим образом, от того, что я понимаю:

CREATE TABLE #data (id INT IDENTITY(1,1), 
employee_id INT , 
period INT , 
[day] INT, 
[hours] DECIMAL (8,3)) 

Making данные:

DECLARE @seed INT = 0, 
    @max INT = 10000, 
    @employee INT 

WHILE @seed < @max 
BEGIN 
    SET @employee =100 + RAND()*40 

    INSERT INTO #data 
      (employee_id, period, day, hours) 
    VALUES (@employee, -- employee_id - int 
       1 + RAND() * 26, -- period - int 
       1 + RAND() * 14, -- day - int 
       4 + RAND() * 8 ) 

    SET @seed = @seed + 1 
END 

Используется Cross Apply для расчета текущих Всего часов для каждой комбинации день + Period (предполагая те последовательны).

SELECT da.employee_id, 
MIN(da.period) AS [Period], 
-- Because getting min day gets the lowest day number of all periods 
MIN(da.period * 1000 + da.day) % 1000 AS [Day] 

FROM #data da 
CROSS APPLY (
    SELECT d.employee_id, SUM(d.hours) AS [Hours] 
    FROM #data d 
    WHERE d.employee_id = da.employee_id  
    --Total number of days since period 1 day 1 
    AND d.day + d.period * 14 < da.day + da.period * 14 
    GROUP BY d.employee_id) total 

WHERE total.Hours > 500 
GROUP BY da.employee_id 
ORDER BY da.employee_id 

Даже с новым вычисляемым предложением where запрос занимает 1 секунду для выполнения против 10k записей, которые я генерирую. Вы можете получить производительность, индексируя сотрудника/день/период ... Я бы запустил анализатор, чтобы понять, что часть.

+0

Прежде всего, спасибо, что показал мне CROSS APPLY - очень полезно! К сожалению, он не работает. :(Это дает мне кажущиеся случайными результаты. Для одного человека, которого я проверил, он вытащил период оплаты, в котором они достигли более 800 часов вместо 500, а для другого это дало мне последний период, когда у них было более 2000 часов работы ... – Steven

+0

@Steven. Что такое «период» и что такое «день»? Как эти вещи работают вместе? –

+0

«Период» - двухнедельный период оплаты. День пронумерован 1-14 и представляет, какой день периода оплаты человек работал. Другая таблица имеет номер периода вместе с датой начала периода. – Steven

0

Привет, вы, кажется, ищете совокупную сумму. Посмотрите на https://msdn.microsoft.com/en-us/library/ms189461.aspx. Пример с использованием очень полезный генератор Максу: - объявить @data таблица (employee_id INT, период INT, день ИНТ, часы целое)

DECLARE @seed INT = 0 
WHILE @seed < 10000 
begin 
    INSERT INTO @data 
      (employee_id, period, day, hours) 
    VALUES (100 + RAND()*40 , -- employee_id - int 
       1 + RAND() * 8, -- period - int 
       1 + RAND() * 14, -- day - int 
       4 + RAND() * 8 -- hours - decimal 
      ) 

    SET @seed = @seed + 1 
END 

SELECT * FROM 
(
select employee_id,period,day, hours 
     ,  CumulativeTotal 
     , row_number() over (partition by employee_id order by cumulativetotal) ROWNUMBER 
from 
(
select employee_id,period,day, hours 
     ,SUM(hours) OVER (partition by employee_id 
     ORDER BY period,day 
      ROWS UNBOUNDED PRECEDING) AS CumulativeTotal 
from @data 
--where employee_id = 100 
) s 
where cumulativetotal >= 500 
) T 
WHERE T.ROWNUMBER = 1 
order by T.employee_id ,T.period,T.day 

/*Prove it by dropping into excel and adding a column in excel to confirm cumulative total*/ 
select employee_id ,period,day,hours 
     ,SUM(hours) OVER (partition by employee_id 
     ORDER BY period,day 
      ROWS UNBOUNDED PRECEDING) AS CumulativeTotal 
from @data 
where employee_id = 101 
order by employee_id,period,day 
0

До тех пор, пока у вас есть по крайней мере SQL Server 2012, а затем с окнами функции вашего лучший выбор.

with IsThresholdReached (employee_id, period, day, threshold_reached) 
as (
    select employee_id, period, day, 
      case when 
       sum(hours) over (partition by employee_id order by period, day rows unbounded preceding) >= 500 
      then 1 else 0 end 
    from shift_worked 
), 

ThresholdFirstReached (employee_id, period, day, first_reached_period, first_reached_day) 
as (
    select employee_id, period, day, 
      first_value(period) over (partition by employee_id order by period, day rows unbounded preceding), 
      first_value(day) over (partition by employee_id order by period, day rows unbounded preceding) 
    from IsThresholdReached 
    where threshold_reached = 1 
) 

select employee_id, period, day 
from ThresholdFirstReached 
where period = first_reached_period 
and day = first_reached_day 

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

Смежные вопросы