Comparing Datetime / Timestamp Values From Two Databases for Employee Scheduling (MySQL) - mysql

My goal is to compare a table of employee schedules from one database, to their actual clock in times in another.
Here is the query I'm starting with to simply find a singular clock-in time when searching the clock-in database:
SELECT *
FROM `users_log`
WHERE `user` = 'Employee'
AND `type` = 'Login'
ORDER BY ABS(`logintime` - UNIX_TIMESTAMP(`scheduled_logintime_from_2nd_database`)))
LIMIT 1
Not sure on the best way to do this (specifically because using the ORDER BY in a subquery is iffy). But ultimately I'd want some sort of parent query / join that would join the two databases on the Employee Name (user), and show both the scheduled logintime, and actual logintime from the two databases.
In the above example, for scheduled_logintime_from_2nd_database I'm just using a text string for testing, eventually that'd be the actual column from the other database. (note one is a timestamp, the other a datetime, hence the UNIX_TIMESTAMP function).

You're very close.
I suggest you proceed as follows:
First, do an appropriate JOIN operation to gather your two sources of data together. Something like this:
SELECT DATE(ul.logintime) day,
ul.name, ul.logintime,
UNIX_TIMESTAMP(sch.scheduled_logintime) sched_time
FROM users_log ul
JOIN schedule sch ON ul.name = sch.name
AND DATE(ul.logintime) = DATE(sch.scheduled_logintime)
This should get you a bunch of rows, one or more for each day and user, showing scheduled and actual times.
Then you can use that as a subquery, perhaps doing something like this:
SELECT name, day,
MAX(ABS(logintime - sched_time)),
MIN(ABS(logintime - sched_time))
FROM ( SELECT DATE(ul.logintime) day,
ul.name, ul.logintime,
UNIX_TIMESTAMP(sch.scheduled_logintime) sched_time
FROM users_log ul
JOIN schedule sch ON ul.name = sch.name
AND DATE(ul.logintime) = DATE(sch.scheduled_logintime)
)
GROUP BY name, day
That should give you each name's best and worst adherence to a schedule in each day.

Related

MySQL INNER JOIN with GROUP BY and COUNT(*)

I've never been able to get my head around INNER JOINs (or any other JOIN types for that matter) so I'm struggling to work out how to use it in my specific situation. In fact, I'm not even sure if it's what I need. I've looked at other examples and read tutorials but my brain just doesn't seem to work the way needed to truly get it (or it doesn't function at all).
Here's the scenario:
I have two tables -
phone_numbers - this table has a list of phone numbers that
belong to lots of different customers. A single customer can have
multiple numbers. For simplicity's sake, we'll say the fields are
'number_id', 'customer_id', 'phone_number'.
call_history - this table has a record of every single call that one of these
numbers in the first table could have had. There's a record for
every individual call going back years. Again, for simplicity,
we'll say the relevant fields are customer_id, phone_number,
call_start_time.
What I'm trying to accomplish is to find all of the numbers that belong to a particular customer_id in the phone numbers table and use that information to search through the call_history table and find the number of calls each phone number has received, and group that by the number of calls for each number, preferably also showing zeros where a number hasn't received any calls at all.
The reason the zero calls is important is because that's the data I'm interested in. Otherwise, I could just get all the information out of the call_history table. But what I'm trying to achieve is find the numbers with no activity.
All I've been able to accomplish is run one query to get all of the numbers belonging to one customer:
SELECT customer_id, phone_number FROM phone_numbers WHERE customer_id = Y;
Then run a second query to get all phone calls for that customer_id for a set duration:
SELECT customer_id, phone_number, COUNT(*) FROM call_history WHERE customer_id = Y and call_start_time >= DATE_SUB(SYSDATE(), INTERVAL 30 DAY) GROUP BY phone_number;
I've then had to use the data returned from both queries and use a VLOOKUP function in Excel to match number of calls for each individual number from the second query to the list of all numbers from the first query, thus leaving blanks in my "all numbers" table and identifying those numbers that had no calls for that time period.
I'm hoping there's some way to do all of this with a single query and return a table of results, listing the zero number of calls with it and eliminate the whole manual Excel bit as it's not overly efficient and prone to human error.
Without at least a workable example from you, it's not easy to re-create your situation. Anyway, INNER JOIN might not return the result as how you expected. In my short time with MySQL, I mainly use 2 types of JOIN; one is already mentioned and the other is LEFT JOIN. From what I can understand in your question, what you want to achieve can be done by using LEFT JOIN instead of INNER JOIN. I may not be the best person to explain this to you but this is how I understand it:
INNER JOIN - only return anything that match in ON clause between two (or more) tables.
LEFT JOIN - will return everything from the table on the left side of the join and return NULL if ON get no match in the table on the right side of the join .. unless you specify some WHERE condition from something on the right table.
Now, here is my query suggestion and hopefully it'll be useful for you:
SELECT A.customer_id, A.phone_number,
SUM(CASE WHEN call_start_time >= DATE_SUB(SYSDATE(), INTERVAL 30 DAY)
THEN 1 ELSE 0 END) AS Total
FROM phone_numbers A
LEFT JOIN call_history B
ON A.customer_id=B.customer_id
GROUP BY A.customer_id,A.phone_number;
What I did here is I LEFT JOIN phone_numbers table with call_history on customer_id and I re-position the WHERE call_start_time >= .. condition into a CASE expression in the SELECT since putting it at WHERE will turn this into a normal join or inner join instead.
Here is an example fiddle : https://www.db-fiddle.com/f/hriFWqVy5RGbnsdj8i3aVG/1
For Inner join You should have to do like this way..
SELECT customer_id,phone_number FROM phone_numbers as pn,call_history as ch where pn.customer_id = ch.customer_id and call_start_time >= DATE_SUB(SYSDATE(), INTERVAL 30 DAY) GROUP BY phone_number;
Just add table name whatever you want to join and add condition

SQL with JOIN where column of one table is null if global WHERE is not fullfilled

it's been quite a while since I had to write SQL for the last time. I'm struggling with a select and I was hoping you can point out what I am missing.
I have a rather simple table structure with 2 tables:
Users
user_id
name
Estimates
user_id
estimate
estimation_date
The goal is to use a start and end date to show all estimations of all users. For users who don't have an estimate for that particular time range, I want the SQL to return me a null value.
In reality there are a few more tables and columns involved as well as some user role stuff but that is not necessary for this example.
I tried to setup my select like this:
SELECT a.id, a.name, b.estimate, b.estimation_date
FROM users a
LEFT OUTER JOIN estimates b on a.id = b.id
WHERE b.estimation_date BETWEEN $startdate AND $enddate OR b.estimation_date IS NULL
As long as a user has not made any estimate at all, the result is what I want. Each row is a user, same user can occur multiple times - once for each estimate in the date range - and a user without an estimate has NULL in b.estimate and b.estimation_date.
However, as soon as a user has made an estimation, even if it is not inside the range, the user does not show up in the result anymore.
What would be the best solution to input a start and end date and get all users and for each user who has no estimation inside this range get a NULL in those columns?
Put the date range condition in the on clause:
SELECT u.id, u.name, e.estimate, e.estimation_date
FROM users u LEFT OUTER JOIN
estimates e
ON u.id = e.id AND
e.estimation_date >= $startdate AND
(e.estimation_date <= $enddate OR e.estimation_date IS NULL)
Note that I also fixed the table aliases so they are not meaningless letters, but are abbreviations for the table name.

MySQL: SQL JOIN, COUNT, and WHERE statements

Currently, I am trying to join two tables (named pageviews and sessions) and use the COUNT and WHERE conditions in order to identify new users that used the site (on a specific page) from the previous two weeks. For reference, the two tables date back from the previous 12 months. In order to do so, I have written the following query
SELECT pageviews.pageType
, sessions.sessionDate
, sessions.deviceType
, COUNT(visitorId)
FROM sessions
INNER JOIN pageviews
on sessions.sessionId = pageviews.sessionId
WHERE pageviews.pageType = 'Page1'
AND sessionDate BETWEEN '2018-04-26' AND '2018-05-08'
=! visitorId BETWEEN '2017-05-10' AND '2018-04-27'
ORDER BY sessionsDate;
I decided to join the two tables by session ID so I can dedicate COUNT to visitorId. Is there a more efficient way to write this?
You should make a group by pageviews.pageType and do the count in the visits, you will have the result per page

MySQL joined query with max dates from two tables

I got two tables that contain date and time stamps and trying to extract records by the latest date in both.
Table 1 (sessions):
id---login_date------------ip
01---2014-01-02 23:58:40---127.0.0.1
03---2014-01-01 13:20:16---127.0.0.1
01---2014-01-01 17:06:15---127.0.0.1
02---2013-12-30 14:34:39---127.0.0.1
*also multiple other non-date columns which are not playing part in this solution
Table 2 (reminders):
id---last_reminder---------next_reminder
03---2013-12-29 22:50:18---2014-01-07 22:50:18
02---2014-01-01 15:15:15---2014-01-09 15:15:15
02---2013-11-16 08:54:23---2013-11-23 08:54:23
Now this is the way I get all the latest logins from the first table for each user ID:
SELECT a.id, a.login_date
FROM sessions a
WHERE a.login_date = (
SELECT max(login_date) as login_date
FROM sessions
WHERE id = a.id
LIMIT 1
)
GROUP BY a.id
What I would like to get is not only the last login date for each user ID, but also the last sent reminder (if any). As this involves selecting two max dates I never get correct results.
Desired Result:
id---login_date------------last_reminder---------next_reminder------
01---2014-01-02 23:58:40---NULL------------------NULL---------------
02---2013-12-30 14:34:39---2014-01-01 15:15:15---2014-01-09 15:15:15
03---2014-01-01 13:20:16---2013-12-29 22:50:18---2014-01-07 22:50:18
Would anybody please help me out with this.
Thanks,
Simon
////////////////////////UPDATED 2014-01-04 WITH EXTRA COLUMNS////////////////////////////
Based on the request the above table structure was updated to contain extra fields, required to produce correct query results.
*note that next_reminder field will always have a value as it's calculated based on the last reminder value. The query will eventually check whether the next reminder is within certain timeframe too.
P.S. StackOverflow is full of very good answers when only one table contains the date and time stamp (from which I have built what I have got so far), however I could not locate any similar examples that would involve joining two tables and selecting max dates from each.
if you have only two columns per table, you can directly join it and use MAX() to get the latest record for each ID.
SELECT a.id,
MAX(login_date) latest_login_date,
MAX(last_reminder) latest_reminder
FROM sessions a
LEFT JOIN reminders b
ON a.id = b.id
GROUP BY a.id
SQLFiddle Demo

Complex SQL query

I have need to write a query which is a little complicated for me to put together. The basic idea is to match a couple of fields from different tables and then edit another table based on the result.
There are three tables involved:
Schedules: sch_id, date, schedule, event_id
Link_Location_Schedules: id, loc_id, sch_id
Link_Location_Events: id, loc_id, event_id
Now what I need to try and do is:
find schedules that are set after todays date in "Schedules".
for these schedules get location ids from Link_Location_Events where event_ids equal the schedule event id.
for each of the matched schedules (sch_id) and returned locations (loc_id) check if the pair already exist in the Link_Location_Schedules, if not insert them.
Here are some SQL queries I have done for the above, I just need to combine them some how:
SELECT sch_id FROM 'Schedules' WHERE DATE_FORMAT(sports_schedule_insert_date_time, "%Y-%m-%d") >= '2012-11-14';
SELECT loc_id from Link_Location_Events, Schedules WHERE Link_Location_Events.event_id = Schedules.event_id;
sounds like a simple insert from select statement...
insert into Link_Location_Schedules
( loc_id,
sch_id )
select
PreQuery.loc_id,
PreQuery.sch_id
from
( select
s.sch_id,
lle.loc_id
from
Schedules s
join Link_Location_Events lle
on s.event_id = lle.event_id ) PreQuery
LEFT JOIN Link_Location_Schedules lls
on PreQuery.loc_id = lls.loc_id
and PreQuery.sch_id = lls.sch_id
where
lls.loc_id is null
The innermost prequery is to get all possible schedule / location IDs. From that, left-join to the existing location/schedules on those found. Then, the WHERE clause will return only those where NO MATCH WAS FOUND (thus the lls.loc_id is null). Take that result and insert directly into the schedule / location table.