How to do inner join and group data while limiting related entry to 1 for each of the last two months?
I would like to get a list of all URLs from one table together with two ratings for each URL in second table. The problem is that one rating should be from current month (if it exists), the other rating should be from last month (also if it exists). Is that possible to achieve with MySQL? Both ratings should for each URL row be grouped into one cell.
Database would be populated with new values once or maybe multiple times per month.
Table structure and sample data:
urls
id | url
1 | google.com
2 | apple.com
3 | bing.com
ratings
id | rating | url_id | created_at
1 | 10 | 1 | 2018-10-10
2 | 8 | 2 | 2018-10-10
3 | 11 | 1 | 2018-11-10
4 | 5 | 2 | 2018-11-10
5 | 6 | 3 | 2018-11-25
In the end my goal is to show a simple table of URLs and compare their ratings for each month.
url | previous rating | current rating
google.com | 10 | 11 (rating has increased)
apple.com | 8 | 5 (rating has decreased)
bing.com | - | 6
This is just a beginning of a query, I don't have idea how would I limit 2 ratings for each url as stated above?
SELECT
urls.id,
urls.url,
ratings.rating
FROM urls
INNER JOIN ratings
ON urls.id = ratings.url_id
mysql
add a comment |
I would like to get a list of all URLs from one table together with two ratings for each URL in second table. The problem is that one rating should be from current month (if it exists), the other rating should be from last month (also if it exists). Is that possible to achieve with MySQL? Both ratings should for each URL row be grouped into one cell.
Database would be populated with new values once or maybe multiple times per month.
Table structure and sample data:
urls
id | url
1 | google.com
2 | apple.com
3 | bing.com
ratings
id | rating | url_id | created_at
1 | 10 | 1 | 2018-10-10
2 | 8 | 2 | 2018-10-10
3 | 11 | 1 | 2018-11-10
4 | 5 | 2 | 2018-11-10
5 | 6 | 3 | 2018-11-25
In the end my goal is to show a simple table of URLs and compare their ratings for each month.
url | previous rating | current rating
google.com | 10 | 11 (rating has increased)
apple.com | 8 | 5 (rating has decreased)
bing.com | - | 6
This is just a beginning of a query, I don't have idea how would I limit 2 ratings for each url as stated above?
SELECT
urls.id,
urls.url,
ratings.rating
FROM urls
INNER JOIN ratings
ON urls.id = ratings.url_id
mysql
1
Hint, join to theratings
twice,INNER JOIN rating ratings_previous ... INNER JOIN rating rating_current...
– danblack
Nov 25 '18 at 23:51
add a comment |
I would like to get a list of all URLs from one table together with two ratings for each URL in second table. The problem is that one rating should be from current month (if it exists), the other rating should be from last month (also if it exists). Is that possible to achieve with MySQL? Both ratings should for each URL row be grouped into one cell.
Database would be populated with new values once or maybe multiple times per month.
Table structure and sample data:
urls
id | url
1 | google.com
2 | apple.com
3 | bing.com
ratings
id | rating | url_id | created_at
1 | 10 | 1 | 2018-10-10
2 | 8 | 2 | 2018-10-10
3 | 11 | 1 | 2018-11-10
4 | 5 | 2 | 2018-11-10
5 | 6 | 3 | 2018-11-25
In the end my goal is to show a simple table of URLs and compare their ratings for each month.
url | previous rating | current rating
google.com | 10 | 11 (rating has increased)
apple.com | 8 | 5 (rating has decreased)
bing.com | - | 6
This is just a beginning of a query, I don't have idea how would I limit 2 ratings for each url as stated above?
SELECT
urls.id,
urls.url,
ratings.rating
FROM urls
INNER JOIN ratings
ON urls.id = ratings.url_id
mysql
I would like to get a list of all URLs from one table together with two ratings for each URL in second table. The problem is that one rating should be from current month (if it exists), the other rating should be from last month (also if it exists). Is that possible to achieve with MySQL? Both ratings should for each URL row be grouped into one cell.
Database would be populated with new values once or maybe multiple times per month.
Table structure and sample data:
urls
id | url
1 | google.com
2 | apple.com
3 | bing.com
ratings
id | rating | url_id | created_at
1 | 10 | 1 | 2018-10-10
2 | 8 | 2 | 2018-10-10
3 | 11 | 1 | 2018-11-10
4 | 5 | 2 | 2018-11-10
5 | 6 | 3 | 2018-11-25
In the end my goal is to show a simple table of URLs and compare their ratings for each month.
url | previous rating | current rating
google.com | 10 | 11 (rating has increased)
apple.com | 8 | 5 (rating has decreased)
bing.com | - | 6
This is just a beginning of a query, I don't have idea how would I limit 2 ratings for each url as stated above?
SELECT
urls.id,
urls.url,
ratings.rating
FROM urls
INNER JOIN ratings
ON urls.id = ratings.url_id
mysql
mysql
asked Nov 25 '18 at 23:43
Ivan TopićIvan Topić
79931123
79931123
1
Hint, join to theratings
twice,INNER JOIN rating ratings_previous ... INNER JOIN rating rating_current...
– danblack
Nov 25 '18 at 23:51
add a comment |
1
Hint, join to theratings
twice,INNER JOIN rating ratings_previous ... INNER JOIN rating rating_current...
– danblack
Nov 25 '18 at 23:51
1
1
Hint, join to the
ratings
twice, INNER JOIN rating ratings_previous ... INNER JOIN rating rating_current...
– danblack
Nov 25 '18 at 23:51
Hint, join to the
ratings
twice, INNER JOIN rating ratings_previous ... INNER JOIN rating rating_current...
– danblack
Nov 25 '18 at 23:51
add a comment |
1 Answer
1
active
oldest
votes
You can use conditional aggregation to get the result you want. I have assumed you would like to use the average rating for each month for comparison purposes.
SELECT u.url,
AVG(CASE WHEN MONTH(r.created_at) = MONTH(CURDATE() - INTERVAL 1 MONTH)
AND YEAR(r.created_at) = YEAR(CURDATE() - INTERVAL 1 MONTH) THEN rating END) AS `previous rating`,
AVG(CASE WHEN MONTH(r.created_at) = MONTH(CURDATE())
AND YEAR(r.created_at) = YEAR(CURDATE()) THEN rating END) AS `current rating`
FROM urls u
LEFT JOIN ratings r
ON r.url_id = u.id
GROUP BY u.url
ORDER BY `current rating` DESC
Output:
url previous rating current rating
google.com 10.0000 11.0000
bing.com 6.0000
apple.com 8.0000 5.0000
Update
To only use the last rating from each month (as requested in comments) is a little more complex. It can be done with two JOIN
s to tables which contain the last rating for the previous and current months:
SELECT u.url,
pr.rating AS `previous rating`,
cr.rating AS `current rating`
FROM urls u
LEFT JOIN (SELECT url_id, rating
FROM ratings r1
WHERE created_at = (SELECT MAX(created_at)
FROM ratings r2
WHERE r2.url_id = r1.url_id AND
MONTH(created_at) = MONTH(CURDATE() - INTERVAL 1 MONTH) AND
YEAR(created_at) = YEAR(CURDATE() - INTERVAL 1 MONTH)
)
) pr ON pr.url_id = u.id
LEFT JOIN (SELECT url_id, rating
FROM ratings r1
WHERE created_at = (SELECT MAX(created_at)
FROM ratings r2
WHERE r2.url_id = r1.url_id
)
) cr ON cr.url_id = u.id
ORDER BY `current rating` DESC
Updated demo on dbfiddle
Thank you very much, I don't need averages, just the single last value from current and last month. Hm how can I remove average? :)
– Ivan Topić
Nov 26 '18 at 15:54
@IvanTopić see my edit. The second query will give you the last rating for each month rather than the average. The updated demo includes both queries so you can compare results.
– Nick
Nov 26 '18 at 21:55
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53473129%2fhow-to-do-inner-join-and-group-data-while-limiting-related-entry-to-1-for-each-o%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
You can use conditional aggregation to get the result you want. I have assumed you would like to use the average rating for each month for comparison purposes.
SELECT u.url,
AVG(CASE WHEN MONTH(r.created_at) = MONTH(CURDATE() - INTERVAL 1 MONTH)
AND YEAR(r.created_at) = YEAR(CURDATE() - INTERVAL 1 MONTH) THEN rating END) AS `previous rating`,
AVG(CASE WHEN MONTH(r.created_at) = MONTH(CURDATE())
AND YEAR(r.created_at) = YEAR(CURDATE()) THEN rating END) AS `current rating`
FROM urls u
LEFT JOIN ratings r
ON r.url_id = u.id
GROUP BY u.url
ORDER BY `current rating` DESC
Output:
url previous rating current rating
google.com 10.0000 11.0000
bing.com 6.0000
apple.com 8.0000 5.0000
Update
To only use the last rating from each month (as requested in comments) is a little more complex. It can be done with two JOIN
s to tables which contain the last rating for the previous and current months:
SELECT u.url,
pr.rating AS `previous rating`,
cr.rating AS `current rating`
FROM urls u
LEFT JOIN (SELECT url_id, rating
FROM ratings r1
WHERE created_at = (SELECT MAX(created_at)
FROM ratings r2
WHERE r2.url_id = r1.url_id AND
MONTH(created_at) = MONTH(CURDATE() - INTERVAL 1 MONTH) AND
YEAR(created_at) = YEAR(CURDATE() - INTERVAL 1 MONTH)
)
) pr ON pr.url_id = u.id
LEFT JOIN (SELECT url_id, rating
FROM ratings r1
WHERE created_at = (SELECT MAX(created_at)
FROM ratings r2
WHERE r2.url_id = r1.url_id
)
) cr ON cr.url_id = u.id
ORDER BY `current rating` DESC
Updated demo on dbfiddle
Thank you very much, I don't need averages, just the single last value from current and last month. Hm how can I remove average? :)
– Ivan Topić
Nov 26 '18 at 15:54
@IvanTopić see my edit. The second query will give you the last rating for each month rather than the average. The updated demo includes both queries so you can compare results.
– Nick
Nov 26 '18 at 21:55
add a comment |
You can use conditional aggregation to get the result you want. I have assumed you would like to use the average rating for each month for comparison purposes.
SELECT u.url,
AVG(CASE WHEN MONTH(r.created_at) = MONTH(CURDATE() - INTERVAL 1 MONTH)
AND YEAR(r.created_at) = YEAR(CURDATE() - INTERVAL 1 MONTH) THEN rating END) AS `previous rating`,
AVG(CASE WHEN MONTH(r.created_at) = MONTH(CURDATE())
AND YEAR(r.created_at) = YEAR(CURDATE()) THEN rating END) AS `current rating`
FROM urls u
LEFT JOIN ratings r
ON r.url_id = u.id
GROUP BY u.url
ORDER BY `current rating` DESC
Output:
url previous rating current rating
google.com 10.0000 11.0000
bing.com 6.0000
apple.com 8.0000 5.0000
Update
To only use the last rating from each month (as requested in comments) is a little more complex. It can be done with two JOIN
s to tables which contain the last rating for the previous and current months:
SELECT u.url,
pr.rating AS `previous rating`,
cr.rating AS `current rating`
FROM urls u
LEFT JOIN (SELECT url_id, rating
FROM ratings r1
WHERE created_at = (SELECT MAX(created_at)
FROM ratings r2
WHERE r2.url_id = r1.url_id AND
MONTH(created_at) = MONTH(CURDATE() - INTERVAL 1 MONTH) AND
YEAR(created_at) = YEAR(CURDATE() - INTERVAL 1 MONTH)
)
) pr ON pr.url_id = u.id
LEFT JOIN (SELECT url_id, rating
FROM ratings r1
WHERE created_at = (SELECT MAX(created_at)
FROM ratings r2
WHERE r2.url_id = r1.url_id
)
) cr ON cr.url_id = u.id
ORDER BY `current rating` DESC
Updated demo on dbfiddle
Thank you very much, I don't need averages, just the single last value from current and last month. Hm how can I remove average? :)
– Ivan Topić
Nov 26 '18 at 15:54
@IvanTopić see my edit. The second query will give you the last rating for each month rather than the average. The updated demo includes both queries so you can compare results.
– Nick
Nov 26 '18 at 21:55
add a comment |
You can use conditional aggregation to get the result you want. I have assumed you would like to use the average rating for each month for comparison purposes.
SELECT u.url,
AVG(CASE WHEN MONTH(r.created_at) = MONTH(CURDATE() - INTERVAL 1 MONTH)
AND YEAR(r.created_at) = YEAR(CURDATE() - INTERVAL 1 MONTH) THEN rating END) AS `previous rating`,
AVG(CASE WHEN MONTH(r.created_at) = MONTH(CURDATE())
AND YEAR(r.created_at) = YEAR(CURDATE()) THEN rating END) AS `current rating`
FROM urls u
LEFT JOIN ratings r
ON r.url_id = u.id
GROUP BY u.url
ORDER BY `current rating` DESC
Output:
url previous rating current rating
google.com 10.0000 11.0000
bing.com 6.0000
apple.com 8.0000 5.0000
Update
To only use the last rating from each month (as requested in comments) is a little more complex. It can be done with two JOIN
s to tables which contain the last rating for the previous and current months:
SELECT u.url,
pr.rating AS `previous rating`,
cr.rating AS `current rating`
FROM urls u
LEFT JOIN (SELECT url_id, rating
FROM ratings r1
WHERE created_at = (SELECT MAX(created_at)
FROM ratings r2
WHERE r2.url_id = r1.url_id AND
MONTH(created_at) = MONTH(CURDATE() - INTERVAL 1 MONTH) AND
YEAR(created_at) = YEAR(CURDATE() - INTERVAL 1 MONTH)
)
) pr ON pr.url_id = u.id
LEFT JOIN (SELECT url_id, rating
FROM ratings r1
WHERE created_at = (SELECT MAX(created_at)
FROM ratings r2
WHERE r2.url_id = r1.url_id
)
) cr ON cr.url_id = u.id
ORDER BY `current rating` DESC
Updated demo on dbfiddle
You can use conditional aggregation to get the result you want. I have assumed you would like to use the average rating for each month for comparison purposes.
SELECT u.url,
AVG(CASE WHEN MONTH(r.created_at) = MONTH(CURDATE() - INTERVAL 1 MONTH)
AND YEAR(r.created_at) = YEAR(CURDATE() - INTERVAL 1 MONTH) THEN rating END) AS `previous rating`,
AVG(CASE WHEN MONTH(r.created_at) = MONTH(CURDATE())
AND YEAR(r.created_at) = YEAR(CURDATE()) THEN rating END) AS `current rating`
FROM urls u
LEFT JOIN ratings r
ON r.url_id = u.id
GROUP BY u.url
ORDER BY `current rating` DESC
Output:
url previous rating current rating
google.com 10.0000 11.0000
bing.com 6.0000
apple.com 8.0000 5.0000
Update
To only use the last rating from each month (as requested in comments) is a little more complex. It can be done with two JOIN
s to tables which contain the last rating for the previous and current months:
SELECT u.url,
pr.rating AS `previous rating`,
cr.rating AS `current rating`
FROM urls u
LEFT JOIN (SELECT url_id, rating
FROM ratings r1
WHERE created_at = (SELECT MAX(created_at)
FROM ratings r2
WHERE r2.url_id = r1.url_id AND
MONTH(created_at) = MONTH(CURDATE() - INTERVAL 1 MONTH) AND
YEAR(created_at) = YEAR(CURDATE() - INTERVAL 1 MONTH)
)
) pr ON pr.url_id = u.id
LEFT JOIN (SELECT url_id, rating
FROM ratings r1
WHERE created_at = (SELECT MAX(created_at)
FROM ratings r2
WHERE r2.url_id = r1.url_id
)
) cr ON cr.url_id = u.id
ORDER BY `current rating` DESC
Updated demo on dbfiddle
edited Nov 26 '18 at 21:54
answered Nov 26 '18 at 0:09
NickNick
28k121941
28k121941
Thank you very much, I don't need averages, just the single last value from current and last month. Hm how can I remove average? :)
– Ivan Topić
Nov 26 '18 at 15:54
@IvanTopić see my edit. The second query will give you the last rating for each month rather than the average. The updated demo includes both queries so you can compare results.
– Nick
Nov 26 '18 at 21:55
add a comment |
Thank you very much, I don't need averages, just the single last value from current and last month. Hm how can I remove average? :)
– Ivan Topić
Nov 26 '18 at 15:54
@IvanTopić see my edit. The second query will give you the last rating for each month rather than the average. The updated demo includes both queries so you can compare results.
– Nick
Nov 26 '18 at 21:55
Thank you very much, I don't need averages, just the single last value from current and last month. Hm how can I remove average? :)
– Ivan Topić
Nov 26 '18 at 15:54
Thank you very much, I don't need averages, just the single last value from current and last month. Hm how can I remove average? :)
– Ivan Topić
Nov 26 '18 at 15:54
@IvanTopić see my edit. The second query will give you the last rating for each month rather than the average. The updated demo includes both queries so you can compare results.
– Nick
Nov 26 '18 at 21:55
@IvanTopić see my edit. The second query will give you the last rating for each month rather than the average. The updated demo includes both queries so you can compare results.
– Nick
Nov 26 '18 at 21:55
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53473129%2fhow-to-do-inner-join-and-group-data-while-limiting-related-entry-to-1-for-each-o%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
Hint, join to the
ratings
twice,INNER JOIN rating ratings_previous ... INNER JOIN rating rating_current...
– danblack
Nov 25 '18 at 23:51