Trouble Understanding Joining Tables SQL - mysql

Please see attached Tables image.
The question I have:
Find the top 5 occupations that borrowed the most in 2016
The code I have:
select c.occupation, count(*) no_mostborrow
from client c
Inner Join client c on c.clientID = b.clientID
where b.borrowDate >= '2016-01-01' and b.borrowDate < '2017-01-01'
group by c.clientoccupation, c.clientid
order by count(*) asc
limit 5
I feel like I am missing something here but I am not sure what. I am sure I am completely off. Thank you so much for your time.

To answer your question, you only want occupation in the group by. And the join needs to be correct:
select c.occupation, count(*) as no_mostborrow
from client c join
borrower b
on c.clientid = b.clientid
where b.borrowDate >= '2016-01-01' and b.borrowDate < '2017-01-01'
group by c.clientoccupation
order by count(*) asc
limit 5

Related

SQL query gets stuck if adding ORDER BY

I am working with a query, which looks like this
SELECT s.c1, s.t, s.u, s.dt, t.temp, t.dt AS dt2
FROM `systemusage` AS s
INNER JOIN temperature AS t ON s.did=t.did
WHERE t.did = (SELECT id FROM devices WHERE m = 1)
LIMIT 1
Which works just fine, however if I add ORDER BY s.id, then the query gets totally stuck, can someone guide me on why? the id field is primary, so it should be indexed no?
Add an index on the column temperature.did so that the WHERE clause can be implemented efficiently.
It also may help to replace WHERE t.did = (SELECT ...) with a JOIN.
SELECT s.c1, s.t, s.u, t.temp
FROM `systemusage` AS s
INNER JOIN temperature AS t ON s.did=t.did
INNER JOIN devices AS d ON d.id = t.did
WHERE d.m = 1
ORDER BY s.id DESC, t.id DESC
LIMIT 1

How to limit record before group by for pagination?

I have this query that will LEFT JOIN and GROUP BY to get SUM of column.
SELECT
c.id,
SUM(
r.score
) AS score_sum,
SUM(
CASE WHEN r.is_active = '0' THEN r.negative ELSE 0 END
) AS negative_sum
FROM comments AS c
LEFT JOIN rates AS r ON (r.comment_id = c.id)
WHERE r.comment_id = c.id
GROUP BY c.id
DB Fiddle link:
https://dbfiddle.uk/?rdbms=mysql_5.7&fiddle=fadba795d8426f91471fa4db83845b6f
The query works, but if the comments records is large (10K for example), I need to implement pagination, how do I modify this query to limit the comments records first before GROUP BY?
In short:
Get the first 5 comments by limit to 5
Left join the table rates
Get the SUM by group by
Example, show the first 4 comments SUM
Thanks
You can use subquery to "select c.id from comments limit N" in the FROM clause.
select c.id,
sum(r.score) as score_sum,
SUM(
CASE WHEN r.is_active = '0' THEN r.negative ELSE 0 END
) AS negative_sum
from ( select c.id from comments c limit 2) c
LEFT JOIN rates AS r ON (r.comment_id = c.id)
GROUP BY c.id;
You may apply order by in the subquery to determine order in which you want to select the comments (Top N).
DB Fiddle link
Try the following:
SELECT
c.id,
SUM(
r.score
) AS score_sum,
SUM(
CASE WHEN r.is_active = '0' THEN r.negative ELSE 0 END
) AS negative_sum
FROM comments AS c
LEFT JOIN rates AS r ON (r.comment_id = c.id)
WHERE r.comment_id = c.id
GROUP BY c.id
ORDER BY c.id ASC
LIMIT 5
The rationale behind the above query is that id is the Primary key (hence indexed) in your comments table. Also, your GROUP BY and ORDER BY is on the same column, that is, id; so MySQL will first utilize the index on id and get first 5 rows (due to LIMIT), and then proceed forward to JOIN with other tables and do aggregation etc.
Give it a Try!! More details here: https://dev.mysql.com/doc/refman/5.7/en/order-by-optimization.html
We can confirm the same using EXPLAIN .. on this query.

SQL Date interval not working with AND clause

I am trying to count views in time interval. The request below works only without the AND clause
If I take out the AND s.timestamp < 2019-01-31 it works just fine.
SELECT s.category_id, c.name, count(s.category_id) AS ViewCount
FROM stat_product_category s
JOIN category c ON s.category_id = c.id
WHERE s.timestamp > 2019-01-01 AND s.timestamp < 2019-01-31
GROUP BY s.category_id
ORDER By ViewCount Desc
LIMIT 10
you need single quote on date value
SELECT s.category_id, c.name, count(s.category_id) AS ViewCount
FROM stat_product_category s
JOIN category c ON s.category_id = c.id
WHERE s.timestamp > '2019-01-01' AND s.timestamp <'2019-01-31'
GROUP BY s.category_id ,c.name
ORDER By ViewCount Desc
LIMIT 10
Those aren't timestamps - without quotes, you just have a couple of ints you're subtracting. 2019-01-01 is evaluated as 2019-1-1, or 2017. 2019-01-31 is evaluated as 2019-1-31, or 1987. There is no number that's greater than 2017 but smaller than 1987, so you get no results. Surrounding these values with quotes will make them a string literal, and allow the database to perform an implicit conversion to a date:
SELECT s.category_id, c.name, count(s.category_id) AS ViewCount
FROM stat_product_category s
JOIN category c ON s.category_id = c.id
WHERE s.timestamp > '2019-01-01' AND s.timestamp < '2019-01-31'
-- Here ------------^----------^-------------------^----------^
GROUP BY s.category_id
ORDER By ViewCount Desc
LIMIT 10
that script is looking good you just need to pop qoutes around your dates so that SQL knows where the values start & stops.
SELECT s.category_id, c.name, count(s.category_id) AS ViewCount
FROM stat_product_category s
JOIN category c ON s.category_id = c.id
WHERE s.timestamp > '2019-01-01' AND s.timestamp < '2019-01-31'
GROUP BY s.category_id
ORDER By ViewCount Desc
LIMIT 10
(tested & working!)
Good luck with the project!

MySQL 5.7 | GROUP BY | Non Aggregated Column Error

I upgraded our mysql db from 5.6 to 5.7 and am in the process of fixing some queries which are throwing some errors. One of the queries I am working involves a GROUP BY with a COALESCE.
Here is the query (abstracted) that works:
SELECT
MAX(a.id),
a.entered,
count(*) AS teh_count
FROM
a
INNER JOIN
b ON b.id = a.link_to_b_id
INNER JOIN
c ON c.link_to_b_id = b.id
WHERE
b.revision_id > 0
AND
c.terminated_at = '0000-00-00 00:00:00'
AND
a.created_at > date_sub(NOW(), INTERVAL 8 HOUR)
GROUP BY
a.entered
ORDER BY
teh_count DESC
LIMIT
6;
But I need to COALESCE a.entered with c.override, so I tried the following:
SELECT
MAX(a.id),
a.entered,
COALESCE(c.override, a.entered) AS appearance,
count(*) AS teh_count
FROM
a
INNER JOIN
b ON b.id = a.link_to_b_id
INNER JOIN
c ON c.link_link_to_b_id = b.id
WHERE
b.revision_id > 0
AND
c.terminated_at = '0000-00-00 00:00:00'
AND
a.created_at > date_sub(NOW(), INTERVAL 8 HOUR)
GROUP BY
a.entered
ORDER BY
teh_count DESC
LIMIT
6;
But MySQL 5.7 now throws the following error: Expression #2 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'st_core.tuc.code_appearance_override' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
I assume I can can change the sql_mode, but I'd prefer not too. What the error is telling me makes sense, in that the COALESCE column is not aggregated, so as a test I wrapped it with MAX and it works, however it seems kind of hacky to me.
Is there a more elegant solution?
You should also include a.entered in your group by clause and that's what the error saying. Though not sure why you are grouping by an different column a.code_entered?
Your query should look like
SELECT
MAX(a.id),
a.entered,
COALESCE(c.override, a.entered) AS appearance,
count(*) AS teh_count
FROM
a
INNER JOIN
b ON b.id = a.link_to_b_id
INNER JOIN
c ON c.link_link_to_b_id = b.id
WHERE
b.revision_id > 0
AND
c.terminated_at = '0000-00-00 00:00:00'
AND
a.created_at > date_sub(NOW(), INTERVAL 8 HOUR)
GROUP BY
a.entered,
COALESCE(c.override, a.entered)
ORDER BY
teh_count DESC
LIMIT
6;
I think you intend something like this:
SELECT MAX(a.id),
COALESCE(c.override, a.entered) AS appearance,
count(*) AS the_count
FROM a INNER JOIN
b
ON b.id = a.link_to_b_id INNER JOIN
c
ON c.link_link_to_b_id = b.id
WHERE b.revision_id > 0 AND
c.terminated_at = '0000-00-00 00:00:00' AND
a.created_at > date_sub(NOW(), INTERVAL 8 HOUR)
GROUP BY appearance
ORDER BY the_count DESC
LIMIT 6;
This removes a.entered from the SELECT list so there is only one column for grouping. That column can be referenced by table alias in the GROUP BY.

How do you substitute group by's with left joins?

I wrote this code:
SELECT DISTINCT c.deptid Dept,
c.id Course,
t.startdate
FROM course c
LEFT JOIN section s
ON courseid = c.id
LEFT JOIN term t
ON termid = t.id
AND t.startdate < '2011-4-1'
GROUP BY course
HAVING t.startdate IS NULL
ORDER BY dept,
course;
and I need to get rid of the group by and aggregate functions by using only join or left join. The left join is creating an extra null row and I don't know how to account for that without group by. Any help on re-writing this would be greatly appreciated.
EDIT: This code works. That is not the issue. The issue is I am not allowed to use a group by or any aggregate functions.
I am not sure if this fixes your problem, but this what your query should look like:
SELECT c.deptId as Dept, c.id as Course, startDate
FROM Course c LEFT JOIN
Section s
ON courseId = c.id LEFT JOIN
Term t
ON termId = t.id AND startDate < '2011-04-01'
WHERE startDate IS NULL
GROUP BY course
ORDER BY Dept, Course;
Here are the changes:
The distinct is unnecessary because you are doing a group by
The dates are in YYYY-MM-DD format (an ISO standard)
The comparison for NULL date is moved to a where clause
The group by statement uses the id column for course
The column aliases are introduced by as (for readability)
You shouldn't have a group by clause and your having clause should be a where clause. Try this:
SELECT DISTINCT c.deptid Dept,
c.id Course,
t.startdate
FROM course c
LEFT JOIN section s
ON courseid = c.id
LEFT JOIN term t
ON termid = t.id
AND t.startdate < '2011-04-01'
WHERE t.startdate IS NULL
ORDER BY dept,
course;
This will find all course selections that don't have a term.