SQL Join with data associated to dates - mysql

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.

Related

How to display values from different tables in certain time frame MYSQL

I have attached the tables that are included in this question for MYSql. My question states:
First and last names of the top 5 authors clients borrowed in 2017. My code so far:
SELECT BookID,BorrowDate COUNT(BookID) AS BookIDCount
FROM Borrower
WHERE BorrowDate = 2017
ORDER BY BookIDCount DESC
LIMIT 5
I think so far my code just displays the top 5 Author ID in 2017 but I can't figure out how to display the names. I see the link between AuthorID and BookAuthor (maybe). Thank you so much for any help you may provide.
Here are the tables:
You can bring the client table with a join. I think that you want:
select c.clientFirstName, c.clientLastName, count(*) no_books
from borrower b
inner join client c on c.clientId = b.clientId
where b.borrowDate >= '2017-01-01' and b.borrowDate < '2018-01-01'
group by c.clientId, c.clientFirstName, c.clientLastName
order by count(*) desc
limit 5
This treats borrowDate as a column of type date (or the-like), because that what it seems to be. If it just a number that represent the year, then you can change back the where clause to your original condition.

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

MySQL GROUP BY w/ ORDER BY not having desired result

I have a query that attempts to retrieve IDs of people, but only if those people have more than one address. I'm also checking that the last time I called them was at least 30 days ago. Finally, I'm trying to order the results, because I want to pull up results with the oldest last_called datetime:
SELECT
p.id,
COUNT(*) AS cnt
FROM
people p
LEFT JOIN addresses a
ON p.id = a.id
WHERE p.last_called <= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY p.id
HAVING COUNT(*) > 1
ORDER BY p.last_called ASC
LIMIT 25
Right now, the results are not excluding people with only one address. I haven't even got to the point where I know if the sort order is correct, but right now I'd just like to know why it is that my query isn't pulling up results where there is at least 2 addresses for the person.
If you don't want to include people with no address then I would recommend using INNER JOIN instead of LEFT JOIN and DISTINCT to get distinct address ids
(just in case if you have duplicate mappings), e.g.:
SELECT
p.id,
COUNT(DISTINCT(a.id)) AS cnt
FROM
people p
JOIN addresses a
ON p.id= a.peopleid
WHERE p.last_called <= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY p.id
HAVING COUNT(DISTINCT(a.id)) > 1
As far as Ordering is concerned, MySQL evaluates GROUP BY before ordering the results and hence, you need to wrap the query inside another query to get the ordered results.
Update
Instead of joining on aid, you need to join on peopleId of an address record to get the people record.

Select even if count=0 mysql

So I have 3 tables:
accident, involve and car.
Car (registration_nr, model,year)
involve (licence_nr,registration_nr (FK of car), raport_nr (FK of accident))
accident(raport_nr, date)
I have to count the number of accidents for each car model from Jan 1,2014 but must select also the cars with count 0 (that from jan 01,2014 haven't been in an accident) This is the difficult part for me , till now I can select only those that have been in an accident.
Select c.model,count(m.registration_nr)
from car as c
join involve as i
on i.registration_nr=c.registration_nr
join accident as a
on i.raport_nr=a.raporti_nr
where a._date between '2014-01-01' and curdate()
group by c.model;
You're clearly stating that you only want rows with accidents:
where a._date between '2014-01-01' and curdate()
Just add an OR clause to allow records where a._date IS NULL.
You can use conditional aggregation:
Select c.model, sum(a._date between '2014-01-01' and curdate() )
from car c left join
involve i
on i.registration_nr = c.registration_nr left join
accident a
on i.raport_nr = a.raporti_nr
group by c.model;
I also added left joins -- just in case some models have no accidents at all.
I have encountered the same problem and it took me very long to figure it out.
It turned out that the aggregate function COUNT will return 0 if the collection is all NULL. So here is how you would put the query:
SELECT model, COUNT(registration_nr)
FROM car LEFT JOIN (involve NATURAL JOIN accident) USING (registration_nr)
WHERE date > '01-01-2014' OR date IS NULL
GROUP BY model
In my case, the output would be something like:
Model Count
Mazda 1
Honda 0

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.