Есть ли решение на основе множества для этой проблемы? - PullRequest
5 голосов
/ 11 марта 2010

У нас есть таблица, настроенная следующим образом:

|ID|EmployeeID|Date     |Category       |Hours|
|1 |1         |1/1/2010 |Vacation Earned|2.0  |
|2 |2         |2/12/2010|Vacation Earned|3.0  |
|3 |1         |2/4/2010 |Vacation Used  |1.0  |
|4 |2         |5/18/2010|Vacation Earned|2.0  |
|5 |2         |7/23/2010|Vacation Used  |4.0  |

Бизнес-правила:

  • Баланс отпуска рассчитывается по заработанным отпускам за вычетом использованного отпуска.
  • Используемый отпуск всегда применяется к сумме заработанной суммы за самый старый отпуск.

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

|ID|EmployeeID|Date     |Category       |Hours|
|1 |1         |1/1/2010 |Vacation Earned|1.0  |
|4 |2         |5/18/2010|Vacation Earned|1.0  |

Обратите внимание, что запись 2 была исключена, поскольку она была полностью смещена на использованное время, но записи 1 и 4 использовались только частично, поэтому они были рассчитаны и возвращены как таковые.

Единственный способ, которым мы подумали сделать это, - записать все записи, заработанные на отпуске, во временную таблицу. Затем получите общее количество использованных каникул и переберите временную таблицу, удалив самую старую запись и вычтя это значение из общего количества использованных каникул, пока общее количество использованных каникул не станет равным нулю. Мы могли бы привести это в порядок, когда оставшиеся каникулы использовались только в качестве старейшего отчета о заработанных каникулах. Это оставило бы нас только с выдающимися отчетами заработанных каникул.

Это работает, но очень неэффективно и плохо работает. Кроме того, производительность будет со временем ухудшаться по мере добавления большего количества записей.

Есть какие-нибудь предложения по лучшему решению, предпочтительнее на основе набора? Если нет, мы просто должны пойти с этим.

РЕДАКТИРОВАТЬ: Это база данных поставщиков. Мы никак не можем изменить структуру таблицы.

Ответы [ 5 ]

2 голосов
/ 11 марта 2010

Следующее должно сделать это ..

(но, как отмечают другие, лучшим решением будет корректировка оставшихся отпусков по мере их расходования ..)

select 
    id, employeeid, date, category, 
    case 
    when  earned_so_far + hours - total_spent > hours then 
        hours 
    else 
        earned_so_far + hours - total_spent
    end as hours
from 
    (
                select 
                    id, employeeid, date, category, hours,
                    (
                        select 
                            isnull(sum(hours),0)
                        from 
                            vacations 
                        WHERE 
                            category = 'Vacation Earned' 
                            and 
                            date < v.date
                            and
                            employeeid = v.employeeid
                    ) as earned_so_far,
                    (
                        select
                            isnull(sum(hours),0)
                        from
                            vacations
                        where 
                            category = 'Vacation Used'
                            and 
                            employeeid = v.employeeid
                    ) as total_spent
                from 
                    vacations V
                where category = 'Vacation Earned'
    ) earned
where
    earned_so_far + hours > total_spent

логика

  1. рассчитать для каждой earned строки, заработанные часы на данный момент
  2. рассчитать общее количество часов, использованных для этого пользователя
  3. выбрать запись, если total_hours_so_far + часов этой записи - total_spent_hours> 0
2 голосов
/ 11 марта 2010

Размышляя о проблеме, мне пришло в голову, что единственная причина, по которой вам нужно заботиться о , когда заработано , - это истечение срока. И если это так, самое простое решение - добавить в таблицу записи об окончании отпуска, чтобы оставшийся для работника отпуск всегда составлял sum(vacation earned) - (sum(vacation expired) + sum(vacatation used)). Вы даже можете показать нужные записи, используя в качестве отправной точки запроса последнюю запись с истекшим сроком отпуска.

Но я предполагаю, что это не вариант. Чтобы решить проблему в соответствии с просьбой, имейте в виду, что всякий раз, когда вы обнаружите, что используете временную таблицу, попробуйте вместо этого поместить эти данные в CTE (общее табличное выражение). К сожалению, у меня сейчас встреча, и поэтому у меня нет времени, чтобы написать запрос (может быть, позже, это звучит забавно), но это должно помочь вам начать.

1 голос
/ 11 марта 2010

Я нахожу весь ваш набор результатов запутанным и неточным, и я вижу, как сотрудники говорят: «Нет, я заработал 2 часа 25 января, а не 1». Это неправда, что они заработали 1 час в ту дату, которая была только частично компенсирована, и у вас не будет конца проблем, если вы решите отображать этот способ. Я бы посмотрел на другой способ представления информации. Обычно вы либо представляете список всех действий отпуска (заработанные, просроченные и использованные) с итоговой суммой внизу, либо вы представляете сводку доступных для использования и использованных.

За более чем 30 лет работы в рабочей силе и во многих различных системах хронометража (а также, изучая даже больше, когда я был аналитиком по управлению), я никогда не видел, чтобы кто-либо хотел так отображать информацию по хронометражу. Я думаю, что есть причина. Если это требование, я бы посоветовал отодвинуть его назад и объяснить, каким образом будет затруднительно читать данные, а также сложно получить хорошо работающее решение. Я бы не принял это как требование, не убедив клиента, что это плохая идея.

0 голосов
/ 11 марта 2010

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

Таким образом, вы можете удовлетворить простой случай («Сколько у меня времени на каникулы»), но при этом вы можете сделать неловкий свод, который вы ищете в своем «Какие кусочки времени отпуска не совпадают с другими битами », который, я надеюсь, вам не нужен очень часто.

0 голосов
/ 11 марта 2010

С течением времени и добавлением записей производительность будет ухудшаться и ухудшаться, если вы не сделаете с этим что-то, например:

  • Очистить старые строки после того, как они будут «отменены» (например, на заработанные каникулы добавлены и учтены эквивалентные использованные строки для отпуска; использованные каникулы установлены, установите отпуск с истекшим сроком, заработанный как «израсходованный»)
  • Добавьте столбец, который помечает, если строка была «отменена», и включите этот столбец в ваши индексы

Отслеживание того, как данные изменяются таким образом, кажется аргументом для изменения структур вашей таблицы (их должно быть несколько, а не одна), но это выходит за рамки вашей текущей проблемы.

Что касается самого запроса, я бы построил два агрегата, выполнил некоторое вычитание, сделал бы этот подзапрос, а затем присоединился к нему для некоторого умного использования одной из функций ранжирования. Здесь тоже пахнет коррелированным подзапросом. Я могу попытаться выяснить это позже (у меня мало времени), но держу пари, что кто-то превосходит меня.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...