Find what does not exist in another table on a certain date - mysql

I need to find which user did not login today.
I have 3 tables (users, logs, absence), the log just registers that a user identified by userID has logged in with a timestamp of the login.
So im trying run a script every night that creates an entry in absence with the users and the time of the absence.
I want to compare userID from table(users) with table(logs) and find which user is missing today by looking at the timestamp in the log.createdAt column.
the problem here is "today" , i can find examples here on how to just compare the two tables like this.
SELECT userID
FROM users
WHERE NOT EXIST (
SELECT userID
FROM logs
WHERE userID = users.userID
)
or like this
SELECT userID
FROM users LEFT JOIN logs ON users.userID = logs.userID
WHERE logs.UID is null
But this just returns the people who have never been logged at all.
I'm using sequelize raw query so either a raw query answer or sequelize answer is good

SELECT userID
FROM users
LEFT JOIN logs ON users.userID = logs.userID and DATE(log.createdAt )=DATE(SYSDATE())
WHERE logs.UID is null
Just try above code hope this will helps.

Related

Laravel Left Join with a tricky filter

I am assigning unique users to vouchers on my website.1 user may have more than one voucher assigned to them but cannot be assigned the same voucher twice.I have 2 mysql tables that I am fetching data from.
tbl_users
tbl_voucher_users
When a user click on a button on my website, they pass along a voucher_id with which I use to display eligible users that can be assigned this voucher ( I.e Users that have not been assigned to this voucher ).
Below is how I am getting the users where voucher_id = 8
$user_data = DB::table('users')
->leftJoin('voucher_users', 'users.id', '=', 'voucher_users.user_id')
->where('voucher_users.voucher_id','!=',8) //User not assigned this voucher
->select('users.*','users.id as userID','voucher_users.*')
->get();
My problem
I am able to left join without the where clause and get results from both Users table and Voucher_users table having eliminated all users assigned voucher_id=8.
However, the results also include users who are assigned other vouchers but also the voucher I am assigning.
i.e
Expected resulsts would be users: 8,11,12,13,14 having eliminated users: 1,4
But my current results are:4,8,11,12,13,14
How do I get rid of the user 4 to prevent double assignment?
Thanks to the suggestion above by #Kevin Lynch to use NOT EXIST .. I simplified the code to:
SELECT users.*
FROM
users
WHERE
NOT EXISTS(SELECT user_id FROM voucher_users WHERE voucher_users.user_id = users.id AND voucher_id=9)
It works so far, I can then covert it to Laravel style
NOT EXISTS would be a good solution if this were to remain a small project (or if you could put more constraints on the users to keep the dataset small perhaps by limiting based on user created date or similar).
However if you can't do such a thing, this query will eventually give you problems because under the hood, mysql will be running that sub-query for each record returned in the main query. You can check https://dev.mysql.com/doc/refman/5.6/en/subquery-materialization.html for more information.
A different solution which would handle the scaling quite a bit better would be to generate a temporary table of users that have the voucher you are looking to remove...
create temporary table tmp_voucher_user (user_id int not null, primary key (user_id)) as
select distinct user_id from voucher_users where voucher_id = 8;
Now that we have a table of users we which to remove, all we need to do is worry about a simple left join...
select users.*, user_voucher.*
from users
inner join user_voucher on users.id = user_voucher.user_id
left join tmp_voucher_user on users.id = tmp_voucher_user.user_id
where tmp_voucher_user.user_id is null -- this part is important, it's only going to grab users where there isn't a match on tmp_user_voucher
Unfortunately this isn't as clean as just doing a NOT EXISTS and I don't believe Laravel supports a way to build temporary tables outside of just writing a raw query but it should scale quite a bit better.

Getting the number of posts that a user has posted?

How can I get the number of posts that a user has posted using one MySQL query?
I can't really think of anything but this, but there is no aggregate function on the join. So I'm not sure how to proceed. I am positive that joins will not accomplish what I need.
select a1.username as Username
from `logs` as a1
left join `logs` as a2
on a1.username = a2.username
For example, my logs table is filled with information about posts people have made. I want to find how many posts each user has made, i.e.
Username, Posts
User1 100
User2 200
etc
EDIT: Sorry for not providing enough information.
I have a table called logs and it has two columns. One column is called username and another column is called msg. It basically holds information about posts that people have posted.
For example, let's say someone named Red posts Hello world. It will be saved to the table logs and a new row will be created. username will be Red, and msg will be Hello world
I basically want to get the number of messages that EVERY SINGLE user has posted by their username. I.e. here is an example of what I want
Username Posts
Red 1
Blue 10
Sally 30
try this
SELECT Username, count(Posts)
FROM `logs`
GROUP BY Username;
Good luck.
I'm assuming that when you say you "can't use count(*) in a join", you mean that you tried and saw that it didn't work, rather than you can't use COUNT at all. So I'm using it here.
You're right that a JOIN is the wrong place for a COUNT. You want it up in the SELECT column list, and a GROUP BY down below. Aggregate by Username, and count the number of entries in each aggregate.
SELECT Username, COUNT(*) AS Count
FROM logs
GROUP BY Username
This query may help you, change this query as for your requirement
SELECT users.*, count( posts.user_id )
FROM posts LEFT JOIN users ON users.id=posts.user_id
GROUP BY posts.user_id
may be like
SELECT users.username, count( logs.username ) as posts
FROM users LEFT JOIN logs ON users.username=posts.username
GROUP BY users.username

2-step SQL Query..something seems wrong

I'm writing what is a pretty simple 2-step SQL Query.
I have one table called Users and another called ProfileCharacteristics.
**Users Table:**
UserId [PK]
UserName
**ProfileCharacteristics Table:**
UserId [FK]
.....(other data)
I'm trying to get access to (other data), but I only have the UserName available. So what I'm presently doing is running one SQL Query that matches the UserName to the UserId and stores the UserId value.
Then, I'm pulling all values that match to UserId in ProfileCharacteristics in a separate query. I have a gut feeling that I could combine these two queries into one, but I'm not sure how.
Any pointers?
EDIT: The start of a JOIN?
SELECT * FROM ProfileCharacteristics
INNER JOIN Users
ON ....
What you're looking for is an INNER JOIN:
SELECT pc.*
FROM ProfileCharacteristics pc
JOIN Users u ON pc.UserId = u.UserId
WHERE U.UserName = 'someuser'
A Visual Explanation of SQL Joins

SQL return usernames in 1 table where there is no matching user in table 2

I have a timesheet and several people use it
users has the username and id of the user, this is lined to timesheet which stores the date and user_id, user_id and id are the link so table 2 might have a new entry for everyday.
Now what im trying to do is gather a list of people who havent updated their timesheet for today. Here is my newbie attempt
select * from users left join timesheet ON users.id =
timesheet.user_id WHERE timesheet.user_id is null AND timesheet.value
is null
This returns all the users who dont have a timesheet entry which is perfect but im querying a specific day so if they filled it out yesterday theyll have a corresponding entry in timesheet but it still means they havent filled it out today.
I tried
select * from users left join timesheet ON users.id =
timesheet.user_id WHERE timesheet.user_id is null AND timesheet.value
is null AND timesheet.date != '12-09-05'
I can see the flaw in that logic, i already excluded the people i want to further filter by date but i cant get my head around how to get the result i want. I really want it to filter by date first and then run that querybut im not sure how to do that as im not too familiar with MySQL and google said he didnt know the answer
Move the date condition to the ON clause:
SELECT * FROM `users`
LEFT JOIN `timesheet`
ON `users`.`id`=`timesheet`.`user_id` AND `timesheet`.`date`=CURDATE()
WHERE `timesheet`.`user_id` IS NULL AND `timesheet`.`value` IS NULL

MySQL query to fetch most recent record

I've got four tables. The structure of these tables is shown below (I am only showing the relevant column names).
User (user_id)
User_RecordType (user_id, recordType_id)
RecordType (recordType_id)
Record (recordType_id, record_timestamp, record_value)
I need to find the most recent record_value for each RecordType that a given user has access to. Timestamps are stored as seconds since the epoch.
I can get the RecordTypes that the user has access to with the query:
SELECT recordType_id
FROM User, User_RecordType, RecordType
WHERE User.user_id=User_RecordType.user_id
AND User_RecordType.recordType_id=RecordType.recordType_id;
What this query doesn't do is also fetch the most recent Record for each RecordType that the user has access to. Ideally, I'd like to do this all in a single query and without using any stored procedures.
So, can somebody please lend me some of their SQL-fu? Thanks!
SELECT
Record.recordType_id,
Record.record_value
FROM
Record
INNER JOIN
(
SELECT
recordType_id,
MAX(record_timestamp) AS `record_timestamp`
FROM
Record
GROUP BY
recordType_id
) max_values
ON
max_values.recordType_id = Record.recordType_id
AND
max_values.record_timestamp = Record.record_timestamp
INNER JOIN
User_RecordType
ON
UserRecordType.recordType_id = RecordType.recordType_id
WHERE
User_RecordType.user_id = ?