简体   繁体   中英

query to get vacations/sick days last month

I'm using laravel and sql srv

there are workers that sometimes takes a vacations or sickness days Those days off are being stored at "vacations" table, and the worker details that took the day off are stores at "users" table.

So actually vacations table belongs to users where Worker_id is pointing to the "id" of the users table.

having two tables:" users " and " vacations "

  1. " users " has columns: id , name

  2. " vacations " has columns: vacation_id , worker_id , begin_date , end_date , description

description can have only two values 1- vacation , 2-iilness

i want to make a query that will give me the total vacation/iilness days separately from the last month of each user separately

so that the output table will be

Name  | Type    | Total days | 

David | iilness |     2      |

David | vacation|     3      |

Sara  | iilness |     1      |

Sara  | Vacation|     5      |

i found this question might help with the answer : Retrive all rows from last month (Laravel + Eloquent)

But only part of it.

You can use JOIN , aggregation, and DATEDIFF() .

The tricky part is if you have periods in the vacation table that span accross multiple months (and possibly in the future). To handle this, you could use a subquery that computes the first and the end of the current month, and then use it to:

  • filter the vacation table with a JOIN : you want to select records whose start date or end date belong to the current month

  • adjust the computation in the SELECT : if the start date was before the beginning of the month, you want to count the days from the beginning of the month only; if the end date was after the end of the month, you want to count until the end of the month only

Consider:

SELECT
    u.name,
    v.description,
    SUM(DATEDIFF(
        LEAST(v.end_date, m.end_date), 
        GREATEST(v.begin_date, m.begin_date)
    )) total_days
FROM 
    user u
    INNER JOIN vacation v ON u.id = v.woker_id
    INNER JOIN (
        SELECT 
            LAST_DAY(NOW() - INTERVAL 1 MONTH) + INTERVAL 1 DAY begin_date, 
            LAST_DAY(NOW()) end_date
    ) m
        ON v.begin_date BETWEEN m.begin_date AND m.end_date
        OR v.end_date   BETWEEN m.begin_date AND m.end_date
GROUP BY u.id, u.name, v.description

The above is for MySQL. The equivalent in SQL Server, where date functions are different, would be:

SELECT
    u.name,
    v.description,
    SUM(DATEDIFF(
        'day',
        CASE WHEN v.begin_date < m.begin_date THEN m.begin_date ELSE v.begin_date END,
        CASE WHEN v.end_date > m.end_date     THEN m.end_date   ELSE v.end_date   END
    )) total_days
FROM 
    user u
    INNER JOIN vacation v ON u.id = v.woker_id
    INNER JOIN (
        SELECT 
            DATEADD(month, DATEDIFF(month, 0, GETDATE()), 0) begin_date, 
            EOMONTH(GETDATE()) end_date
    ) m
        ON v.begin_date BETWEEN m.begin_date AND m.end_date
        OR v.end_date   BETWEEN m.begin_date AND m.end_date
GROUP BY u.id, u.name, v.description

finally i found the following way to implement on Laravel

$query = "
        SELECT
    u.name,
    v.description,
    SUM(DATEDIFF(
        day,
        CASE WHEN v.begin_date < m.begin_date THEN m.begin_date ELSE v.begin_date END,
        CASE WHEN v.end_date > m.end_date     THEN m.end_date   ELSE v.end_date   END
    )+1) total_days
FROM 
    [turtle].[dbo].[users] u
    INNER JOIN [turtle].[dbo].[vacations] v ON u.id = v.Worker_id
    INNER JOIN (
        SELECT 
            DATEADD(month, DATEDIFF(month, 0, DATEADD(month,-1,GETDATE())), 0) begin_date, 
            EOMONTH(DATEADD(month,-1,GETDATE())) end_date
    ) m
        ON v.begin_date BETWEEN m.begin_date AND m.end_date
        OR v.end_date   BETWEEN m.begin_date AND m.end_date
GROUP BY u.id, u.name, v.description
ORDER BY u.name, v.description
        ";

        $results = DB::select(DB::raw($query));
        dd($results);

For sql server:

Haven't tested it, but something like this will do the job.

select name,type,sum(datediff(d,begin_date,end_date)) as Total_days  from vacations v
    inner join users u
    on u.id = v.worker_id
    where begin_date> select dateadd(d,-DATEPART(DD,GETDATE()),GETDATE())
    group by name,type

this is only for leaves of the last month as you described.

ok i used GMB's answer, i changed it since it gave this month's and not last month's off days. i also added one day to the subtraction begin_date-end_date since if it is only one day off then the vacation will be same date.

SELECT
    u.name,
    v.description,
    SUM(DATEDIFF(
        day,
        CASE WHEN v.begin_date < m.begin_date THEN m.begin_date ELSE v.begin_date END,
        CASE WHEN v.end_date > m.end_date     THEN m.end_date   ELSE v.end_date   END
    )+1) total_days
FROM 
    [turtle].[dbo].[users] u
    INNER JOIN [turtle].[dbo].[vacations] v ON u.id = v.Worker_id
    INNER JOIN (
        SELECT 
            DATEADD(month, DATEDIFF(month, 0, DATEADD(month,-1,GETDATE())), 0) begin_date, 
            EOMONTH(DATEADD(month,-1,GETDATE())) end_date
    ) m
        ON v.begin_date BETWEEN m.begin_date AND m.end_date
        OR v.end_date   BETWEEN m.begin_date AND m.end_date
GROUP BY u.id, u.name, v.description

now please help me further. how do i implement it in php/Laravel?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM