Using joins in one table - mysql

I don't know yet how to use joins and whenever I try to use them, my queries won't work.
What I want to to is to show users that have signed up grouped by days.
TABLE USER
user_id
register_date
But what happens if there is a day where there are no sign ups? That day is not shown but I want it to be showed.
select COUNT(a.user_id) as count1, a.register_date as count2
from user a
left outer join user b on a.user_id = b.user_id
GROUP BY a.register_date
I tried to adapt what I wanted from some examples but the previous query does not work since it does not show all the dates.
What I what is the following:
COUNT -> DATE
1 ------- 01-01-2013
0 ------- 02-01-2013
5 ------- 03-01-2013
0 ------- 04-01-2013
0 ------- 05-01-2013

The JOIN that you are doing is unnecessary, as it is not giving you any additional information than if you would do:
select COUNT(a.user_id) as count1,
a.register_date
from user a
group by a.register_date
This should give you the number of users in every register_date that you have in your table. If there are no signups, something like this won't work.
You would have to feed it the dates somehow and then it would be appropriate to use a LEFT JOIN with the table USERS to get the COUNT. Having for example, a table with all the dates you want to query called dateTable you would do:
SELECT t1.date,COUNT(a.user_id)
FROM dateTable t1
LEFT JOIN users a ON t1.date = a.register_date
GROUP BY t1.date;
See this question for instructions on how to create a calendar table, or google sql calendar table on google. There are lots of useful information about this topic.

Related

Use SELECT through three table

I tried to write a query, but unfortunately I didn't succeed.
I want to know how many packages delivered over a given period by a person.
So I want to know how many packages were delivered by John (user_id = 1) between 01-02-18 and 28-02-18. John drives another car (another plate_id) every day.
(orders_drivers.user_id, plates.plate_name, orders.delivery_date, orders.package_amount)
I have 3 table:
orders with plate_id delivery_date package_amount
plates with plate_id plate_name
orders_drivers with plate_id plate_date user_id
I tried some solutions but didn't get the expected result. Thanks!
Try using JOINS as shown below:
SELECT SUM(o.package_amount)
FROM orders o INNER JOIN orders_drivers od
ON o.plate_id=od.plate_id
WHERE od.user_id=<the_user_id>;
See MySQL Join Made Easy for insight.
You can also use a subquery:
SELECT SUM(o.package_amount)
FROM orders o
WHERE EXISTS (SELECT 1
FROM orders_drivers od
WHERE user_id=<user_id> AND o.plate_id=od.plate_id);
SELECT sum(orders.package_amount) AS amount
FROM orders
LEFT JOIN plates ON orders.plate_id = orders_drivers.plate_id
LEFT JOIN orders_driver ON orders.plate_id = orders_drivers.plate_id
WHERE orders.delivery_date > date1 AND orders.delivery_date < date2 AND orders_driver.user_id = userid
GROUP BY orders_drivers.user_id
But seriously, you need to ask questions that makes more sense.
sum is a function to add all values that has been grouped by GROUP BY.
LEFT JOIN connects all tables by id = id. Any other join can do this in this case, as all ids are unique (at least I hope).
WHERE, where you give the dates and user.
And GROUP BY userid, so if there are more records of the same id, they are returned as one (and summed by their pack amount.)
With the AS, your result is returned under the name 'amount',
If you want the total of packageamount by user in a period, you can use this query:
UPDATE: add a where clause on user_id, to retrieve John related data
SELECT od.user_id
, p.plate_name
, SUM(o.package_amount) AS TotalPackageAmount
FROM orders_drivers od
JOIN plates p
ON o.plate_id = od.plate_id
JOIN orders o
ON o.plate_id = od.plate_id
WHERE o.delivery_date BETWEEN convert(datetime,01/02/2018,103) AND convert(datetime,28/02/2018,103)
AND od.user_id = 1
GROUP BY od.user_id
, p.plate_name
It groups rows on user_id and plate_name, filter a period of delivery_date(s) and then calculate the sum of packageamount for the group

Counting distinct new users in purchase table (MySQL)

I'm using metabase to generate client reports on multiple metrics. I have beginner knowledge in SQL but the GUI has helped in building most queries except a few.
I have two tables
User table including timestamp for when user registered and user ID
Purchase table including user IDs (seen in user table)
What I need: Table showing cumulative new users (registered in current month) who have made a purchase in the current month.
I assume joining both tables and then counting distinct ids grouped by day while checking register date should solve this, but I have a very sketchy idea of what the actual query will look like.
One part of the question has been answered here in my opinion - Cumulative distinct count
But how to check against registration date is not coming to me. Any help is appreciated.
User Table Columns
ID, Email, Timestamp (timestamp records date of onboarding)
Purchase Table Columns
ID, User ID, User Email, Product ID, Timestamp (timestamp here is time of purchase)
Edit
Thanks for the comments so far, I've been able to get new user IDs on every day making a purchase in the current month. Now I need to a row with the cumulative sum of these IDs so at the end of the time period, I know how many new users were added.
Here's my current code
SELECT count(DISTINCT p.`user_id`) Users_Activated, date(p.`timestamp`) Day
FROM `purchase` p
INNER JOIN `user` u ON u.`id` = p.`user_id`
WHERE date(u.`timestamp`) BETWEEN {{date1}}
AND {{date2}} AND date(p.`timestamp`) BETWEEN {{date3}} AND {{date4}}
GROUP BY date(p.`timestamp`)
ORDER BY date(p.`timestamp`) ASC
Not knowing the full structure of your tables, I'll give it my best shot.
SELECT u.* -- This will give you all info on all users
FROM users u
INNER JOIN purchases p
ON p.userID = u.ID
WHERE u.registrationDate BETWEEN NOW() - INTERVAL 30 DAY AND NOW()
I think this'll work, given the limited info available. The time interval bit might be a bit different but likely not.
How about
SELECT DISTINCT u.id, u.whatever
FROM user_table u INNER JOIN purchase_table p
ON (u.id = p.userid)
WHERE u.signup >= DATE_FORMAT(NOW() ,'%Y-%m-01')
This should get you the current calendar month's signups who have made a purchase.

SQL Join with data associated to dates

Currently I have a simple SQL request to get aall group departure date and the associated group size (teamLength) between 2 dates but it doesn't work properly.
SELECT `groups`.`departure`, COUNT(`group_users`.`group_id`) as 'teamLength'
FROM `groups`
INNER JOIN `group_users`
ON `groups`.`id` = `group_users`.`group_id`
WHERE departure BETWEEN '2017-03-01' AND '2017-03-31'
In fact, if I have more than 1 group between the 2 dates, only 1 date will be recovered in association with the total number of teamLength.
For exemple, if I have 2 groups in the same interval with, for group 1, 2 people and for group 2, 1 people, the result will be:
Here are 2 screenshots of the current state of my groups and group_users tables:
Is it even possible to do what I want in only 1 SQL request ? Thanks
In addition to what jarlh commented (JOIN with ON). Don't ever group data without an explicit GROUP BY. I don't know why MYSQL still allows this...
Change your query to something like this and you should get the result you are looking for. Currently, the other departure dates get lost in the aggregation.
SELECT
groups.departure,
COUNT(1) as team_length
FROM
groups
INNER JOIN group_users
ON groups.id = group_users.group_id
WHERE
groups.departure BETWEEN '2017-03-01' AND '2017-03-31'
GROUP BY
groups.departure
I think that you have a syntax issue in your query. You are missing the ON statement so your database could be trying to get a cartesian product since there is no join clause.
SELECT `groups`.`departure`, COUNT(`group_users`.`id`) as 'teamLength'
FROM `groups`
INNER JOIN `group_users` ON `groups`.`id` = `group_users`.`group_id`
WHERE departure BETWEEN '2017-03-01' AND '2017-03-31'
GROUP BY `groups`.`departure`
You also are missing the GROUP BYclause which is not mandatory in all RDBS but it is a good practice to set it.

MySQL getting SUM of balance grouped by user's job location

Im trying to get the SUM of all user balances in a specific month, and grouped by the user's region, which depends on the Point of Sell they work at.
balance
id_balance
date
id_user
value ($$$)
user
id_user
id_pos
name (not relevant)
pos (Point of Sell)
id_pos
id_region
name (not relevant)
location_region
id_region
name (Florida, Texas, etc)
Basically, I would need it to present this data (filtered by month):
location_region.name | SUM(balance.value)
---------------------|-------------------
Florida | 45730
Texas | 43995
I've tried a few approaches with no luck. This was my closest attempt.
SELECT location_region.name, SUM(balance.value) AS money
FROM balance
LEFT JOIN user ON user.id_user
LEFT JOIN pos ON pos.id_pos = user.id_pos
LEFT JOIN location_region ON location_region.id_region = pos.id_region
WHERE balance.date BETWEEN '2014-02-01' AND DATE_ADD('2014-02-01', INTERVAL 1 MONTH)
GROUP BY location_region.id_region
ORDER BY money DESC
Any ideas? Thanks!
Your current query has a logical error, JOIN condition between balance and user tables is incomplete (missing balance.id_user). So instead of balance LEFT JOIN user ON user.id_user you should have balance LEFT JOIN user ON user.id_user=balance.id_user. This is causing the JOINed table to have more rows (number of rows in balance times number of rows in user table). So the final SUM is bringing a way too higher value.
I tried the following query on your sample data (I changed some values) and it seems to be working fine:
SELECT location_region.name, SUM(balance.value) AS money
FROM balance
LEFT JOIN user USING(id_user)
LEFT JOIN pos USING(id_pos)
LEFT JOIN location_region USING(id_region)
WHERE balance.date BETWEEN '2014-02-01' AND DATE_ADD('2014-02-01', INTERVAL 1 MONTH)
GROUP BY location_region.id_region
ORDER BY money DESC
Working demo: http://sqlfiddle.com/#!2/dda28/3
On having a detailed look at your table structure and the query that you gave, what I feel is this mismatch could happen because of duplicate number of rows that might be cropping up due to the JOIN. What I suggest in this case is to find the DISTINCT rows and summing it up so that you get an exact result. Now since SUM DISTINCT is not available in MySQL, you could try this different approach to accomplish what you want:
SELECT location_region.name,
SUM(balance.value)*COUNT(DISTINCT id_user)/COUNT(id_user) AS money
FROM balance
LEFT JOIN user ON user.id_user = balance.id_user
LEFT JOIN pos ON pos.id_pos = user.id_pos
LEFT JOIN location_region ON location_region.id_region = pos.id_region
WHERE balance.date BETWEEN '2014-02-01' AND DATE_ADD('2014-02-01', INTERVAL 1 MONTH)
GROUP BY location_region.id_region
ORDER BY money DESC
In my comment, was wondering why u did not JOIN user table with ON clause as user.id_user = balance.id_user. I have added that however in my query. Hope this helps.

MySQL COUNT Filtering

This is my first post on stackoverflow, although I've used it as a reference for a long time. So thanks for all the guidance you've provided me in the past. I really appreciate it. Here is my issue:
I have two tables, one called USERS and one called SIGNIN
USERS
-----------------
userid | password
SIGNIN
--------------
suserid | date
I would like to count the number of times that a user has signed in over the period of the last week.
Here's what I've got:
SELECT userid, password FROM USERS JOIN (SELECT suserid, COUNT(*) AS logins
FROM signin
WHERE WEEKOFYEAR(date) = WEEKOFYEAR(CURDATE()) && signin.suserid = users.userid)
GROUP BY userid
I just can't wrap my head around how to JOIN a subquery and make it count each individuals' logins (date column) and return an individualized number for each individual. I know that query is totally jacked up but I'm just at that point, you know, where I've become so confused that I just need some guidance.
Any help and direction would be fantastic! I've read so many pages to no avail. Thanks, in advance!
** and thank you for the edits. They'll help in the future when I post again.
Don't try to do a subquery, just do the join and group by user id. Also, you want YEARWEEK so you are matching both the year and week, not just the week from any year.
select userid, count(*)
from users inner join signin on userid=suserid and yearweek(`date`) = yearweek(curdate())
group by userid;
left join instead of inner join if you want results of 0 for users who haven't signed in.
This follows what I assume is your intent of "last week" meaning the week of the current date (by default starting Sunday, but see the optional mode parameter to yearweek).
Note that the users table here is completely optional; you could just do:
select suserid, count(*) from signin
where yearweek(`date`) = yearweek(curdate())
group by suserid;
but I'm guessing you may be getting other columns from user or wanting to exclude signins for deleted users.
sThis should be it:
select suserid, count(suserid)
from signin
where date > NOW() - INTERVAL 1 WEEK
group by suserid;
You don't need a subquery if I understand you correctly.
Something like:
SELECT a.userid, a.password, count(b.date) AS logins
FROM users a
INNER JOIN signin b ON a.userid = b.suserid and WEEKOFYEAR(date) = WEEKOFYEAR(CURDATE())
GROUP BY a.userid, a.password
Sorry - haven't got time to try the query to double check syntax, but hopefully will set you in right direction
Edit: If you want to include users with 0 logins, change to LEFT OUTER JOIN
you don't need a join here. what you need is
SELECT userid, password, (SELECT COUNT(*)
FROM SIGNIN
WHERE WEEKOFYEAR(date) = WEEKOFYEAR(CURDATE()) AND signin.suserid = users.userid) AS logins
FROM USERS WHERE 1 GROUP BY userid;