I have two tables, "Gate_Logs" and "Employee".
The "Gate_Logs" table has three columns.
Employee ID - A 4 Digit Unique Number assigned to every employee
Status – In or Out
Timestamp – A recorded timestamp
The "Employee" Table has
Employee ID
Level
Designation
Joining Date
Reporting Location
Reporting Location ID - Single Digit ID
I want to find out which employee had the highest weekly work time over the past year, and I am trying to get this data for each individual location. I want to look at the cumulative highest. Let's say Employee X at Location L worked 60 hours in a particular week, which was the highest at that location, so X will be the person I wanted to query.
Please provide any pointers on how I can proceed with this, have been stuck at it for a while.
SQL version 8.0.27
It can use window function LAG to pair In/Out records
periods - pair in/out records
sumup_weekly - compute weekly work hours for each employee
rank_weekly - rank employees per location per week
and finally select those rank one
WITH periods AS (
SELECT
`employee_id`,
`status` to_status,
`timestamp` to_timestamp,
LAG(`status`) OVER w AS fr_status,
LAG(`timestamp`) OVER w AS fr_timestamp
FROM gate_log
WINDOW w AS (PARTITION BY `employee_id` ORDER BY `timestamp` ASC)
),
sumup_weekly AS (
SELECT
`employee_id`,
WEEKOFYEAR(fr_timestamp) week,
SUM(TIMESTAMPDIFF(SECOND, fr_timestamp, to_timestamp)) seconds
FROM periods
WHERE fr_status = 'In' AND to_status = 'Out'
GROUP BY `employee_id`, `week`
),
rank_weekly AS (
SELECT
e.`employee_id`,
e.`location_id`,
w.`week`,
SEC_TO_TIME(w.`seconds`) work_hours,
RANK() OVER(PARTITION BY e.`location_id`, w.`week` ORDER BY w.`seconds` DESC) rank_hours
FROM sumup_weekly w
JOIN employee e ON w.`employee_id` = e.`employee_id`
)
SELECT *
FROM rank_weekly
WHERE rank_hours = 1
DEMO
Related
I want to know which hour period is the most frequent time a person visits, 2nd most frequent, 3rd most and so on. So for example 1 hour period starts at the top of the hour until the next top Eg 07:00:00-07:59:59 would be the 7am hour period.
CREATE TABLE visits (
id primary key
date_visited datetime not null,
cus_name varchar(32) not null
);
I am a little confused because I don't get how I can group by an hour
SELECT count(*)
FROM visits
GROUP BY date_visited
ORDER BY count(*) DESC
You can use HOUR function:
SELECT hour(date_visited), count(*) as number of visits
FROM visits
GROUP BY hour(date_visited)
ORDER BY count(*) DESC
If you want to give numbers to the hours according to number of visits then you can use the analytical function (MySql 8.0 or higher) as follows:
SELECT hour(date_visited), count(*) as number of visits,
row_number() over (order by count(*) desc) as num
FROM visits
GROUP BY hour(date_visited)
ORDER BY num
I'm trying to understand the right way to divide the count sums from two queries.
I'm teaching myself sql and practising it on line.
Question:
Write a SQL query to find the cancellation rate of requests made between 2017-08-01 and 2017-08-03. The cancellation rate is given by dividing the number of cancelled requests by the total number of rides each day. The result table should have 2 Columns, namely Day that shows each day and Cancellation Rate that provides the cancellation rate of that day.
Table is:
What I tried was:
count cancelled ride rates per date
count all ride requests per date
divide both the counts per date
with
cancelled_rides as
(select count(*) cancel_count, status, Request_id
from TRIPS
where status = 'cncld_driver'
group by state, Request_id)
all_rides as (
select count(*) day_count, status, Request_id
from TRIPS
group by state, Request_id) ,
select cancelled_rides.Request_id as DAY,
(cancelled_rides.cancel_count/all_rides.day_count) as 'Cancellation Rate'
FROM cancelled_rides, all_rides;
Does this look right? Note I purposefully ignored including date ranges as the table has only limited entries.
I do not see that a CTE helps at all for this query. Just use conditional aggregation:
select t.Request_id as day, count(*) as total,
sum( status = 'cncld_driver' ) as num_cancelled,
avg( status = 'cncld_driver' ) as cancellation_rate
from trips t
where request_id >= '2017-08-01' and
request_id < '2017-08-04'
group by request_id;
Calling a date "request_id" is rather confusing. You should have a request id that is unique for each row and a separate column with the date/time.
i have a table that looks like this :
what i need is For each day show the accumulated (moving) number of new Droppers in the last 5 days inclusive
Split to US vs. Non-US geos.
Report Columns:
DataTimstamp - upper boundary of a 5-day time frame
Total - number of New Droppers within the time frame
Region_US - number of New Droppers where country =’US’
Region_rest - number of New Droppers where country<>’US’
this is my code :
Create view new_droppers_per_date as Select date,country,count(dropperid) as num_new_customers
From(
Select dropper id,country,min(cast( LoginTimestamp as date)) as date
From droppers) as t1 group by date,country
Select DataTimstamp,region_us,region_rest from(
(Select date as DataTimstamp ,sum(num_new_customers) over(oreder by date range between 5
preceding and 1 preceding) as total
From new_droppers_per_date ) as t1 inner join
(Select date ,sum(num_new_customers) over(oreder by date range between 5 preceding and preceding)
as region_us
From new_droppers_per_date where country=”us”) as t2 on t1.date=t2.date inner join
(Select date, sum(num_new_customers) over(oreder by date range between 5 preceding and 1
preceding)
as region_rest
From new_droppers_per_date where country <>”us”) as t3 on t2.date=t3.date)
i was wondering if there is any easier\smarter way to do so without using so many joins and view
thank you for the help:)
Here is one way to do it using window functions. First assign a flag to the first login of each DropperId, then aggregate by day and count the number of new logins. Finally, make a window sum() with a range frame that spans over the last 5 days.
select
LoginDay,
sum(CntNewDroppers) over(
order by LoginDay
range between interval 5 day preceding and current row
) RunningCntNewDroppers
from
select
date(LoginTimestamp) LoginDay,
sum(rn = 1) CntNewDroppers
from (
select
LoginTimestamp,
row_number() over(partition by DropperId order by LoginTimestamp) rn
from mytable
) t
) t
Employees
EmpID : int(10)
Firstname: varchar(100)
Lastname: varchar(100)
HireDate: timestamp
TerminationDate: timestamp
AnnualReviews
EmpID: int(10)
ReviewDate: timestamp
What is query that returns each employee and for each row/employee include the greatest number of employees that worked for the company at any time during their tenure and the first date that maximum was reached.
So far, this is my query:
select *, (select count(empid) from employees where terminationdate between t.hiredate and t.terminationdate)
from employees as t
group by empid
What you have is close.
But there's more work to do.
We'd to work out the conditions that determine how many employees were "working" at any point in time (i.e. at a given timestamp value.) The condition I'd check:
HireDate <= timestamp < TerminationDate
We'd need to extend that comparison, so that a NULL value for TerminationDate would be handled like it were a point in time after the timestamp value. That's easy enough to do.)
HireDate <= timestamp AND ( timestamp < TerminationDate OR TerminationDate IS NULL
So, something like this:
SELECT COUNT(1)
FROM Employees e
WHERE ( :timestamp >= e.HireDate )
AND ( :timestamp < e.TerminationDate OR e.TerminationDate IS NULL)
That "count" value would remain the same, and would only change for a "hire" or "terminate" event.
If we got a distinct list of all timestamps for all "hire" and "terminate" events, we could get the number of employees at that point in time.
So, this query would give us the employee count every time the employee count might change:
SELECT t.ts AS `as_of`
, COUNT(1) AS `employee_count`
FROM Employees e
JOIN ( SELECT t.TerminationDate AS ts
FROM Employees t
WHERE t.TerminationDate IS NOT NULL
GROUP BY t.TerminationDate
UNION
SELECT h.HireDate AS ts
FROM Employees h
WHERE h.HireDate IS NOT NULL
GROUP BY h.HireDate
) t
ON ( t.ts >= e.HireDate )
AND ( t.ts < e.TerminationDate OR e.TerminationDate IS NULL)
GROUP BY t.ts
We could use that result (as an inline view) and join that to particular Employee, and get just the rows that have an as_of timestamp that matches the period of employment for that employee. Then just pulling out the maximum employee_count. It wouldn't be difficult to identify the earlier of multiple as_of dates, if that maximum employee_count occurred multiple times.
(The wording of the question leaves open a question, the "earliest date" ever that the employee count met or exceeded the maximum that occurred during an employees tenure, or just the earliest date within the employees tenure that the maximum was reached. It's possible to get either result.)
That's just one way to approach the problem.
I am trying to count sales made by a list of sales agents, this count is made every few minutes and updates a screen showing a 'sales leader board' which is updates using a Ajax call in the background.
I have one table which is created and populated every night containing the agent_id and the total sales for the week and month. I create a second, temporary table, on the fly which counts the sales for the day.
I need to combine the two tables to create a current list of sales for all agents in agent_count.
Table agent_count;
agent_id (varchar),
team_id (varchar),
name (varchar),
day(int),
week(int),
month(int)
Table sales;
agent_id (varchar),
day(int)
I can't figure out how to combine these tables. I think I need to use a join as all agents must be returned - even if they don't appear in the agent_count table.
First I make a simple call to get the week and month totals for all agents
SELECT agent_id, team_id, name, week, month FROM agent_count;
the I create a temporary table of todays sales, and then I count the sales for each agent for the day
CREATE TEMPORARY TABLE temp_todays_sales
SELECT s.id, s.agent_id
FROM sales s
WHERE DATEDIFF(s.uploaded, NOW()) = 0
AND s.valid = 1;
SELECT tts.agent_id, COUNT(tts.id) as today
FROM temp_todays_sales tts
GROUP BY tts.agent_id;
What is the best/easiet way to combine these to end up with a resultset such as
agent_id, team_id, name, day, week, month
where week and month also include the daily totals
thanks for any help!
Christy
SELECT s.agent_id, ac.team_id, ac.name,
s.`day` + COALESCE(ac.`day`, 0) AS `day`,
s.`day` + COALESCE(ac.`week`, 0) AS `week`,
s.`day` + COALESCE(ac.`month`, 0) AS `month`
FROM sales s
LEFT JOIN
agent_count ac
ON ac.agent_id = s.agent_id
team_id and name will be NULL if there is no record in agent_count for an agent.
If the agents can be missing from both tables, you normally would need to make a FULL JOIN but since MySQL does not support the latter you may use its poor man's substitution:
SELECT agent_id, MAX(team_id), MAX(name),
SUM(day), SUM(week), SUM(month)
FROM (
SELECT agent_id, NULL AS team_id, NULL AS name, day, day AS week, day AS month
FROM sales
UNION ALL
SELECT *
FROM agent_count
) q
GROUP BY
agent_id