MySQL - Limit Field To 5 Maximum Occurrences












0














Background:



I run a platform which allows users to follow creators and view their content.



The following query successfully displays 50 posts ordered by popularity. There is also some other logic to not show posts the user has already saved/removed, but that is not relevant for this question.



Problem:



If one creator is particularly popular (high popularity), the top 50 posts returned will nearly all be by that creator.



This skews the results as ideally the 50 posts returned will not be in favor of one particular author.



Question:



How can I limit it so the author (which uses the field posted_by) is returned no more than 5 times. It could be less, but definitely no more than 5 times should one particular author be returned.



It should still be finally ordered by popularity DESC



SELECT * 
FROM `source_posts`
WHERE `posted_by` IN (SELECT `username`
FROM `source_accounts`
WHERE `id` IN (SELECT `sourceid`
FROM `user_source_accounts`
WHERE `profileid` = '100'))
AND `id` NOT IN (SELECT `postid`
FROM `user_posts_removed`
WHERE `profileid` = '100')
AND `live` = '1'
AND `added` >= Date_sub(Now(), INTERVAL 1 month)
AND `popularity` > 1
ORDER BY `popularity` DESC
LIMIT 50


Thank you.



Edit:



I am using MySQL version 5.7.24, so unfortunately the row_number() function will not work in this instance.










share|improve this question




















  • 1




    What is the MySQL server version ? Also, there is just too many IN(.. based subqueries. This will not scale well when data gets bigger.
    – Madhur Bhaiya
    Nov 20 at 18:59


















0














Background:



I run a platform which allows users to follow creators and view their content.



The following query successfully displays 50 posts ordered by popularity. There is also some other logic to not show posts the user has already saved/removed, but that is not relevant for this question.



Problem:



If one creator is particularly popular (high popularity), the top 50 posts returned will nearly all be by that creator.



This skews the results as ideally the 50 posts returned will not be in favor of one particular author.



Question:



How can I limit it so the author (which uses the field posted_by) is returned no more than 5 times. It could be less, but definitely no more than 5 times should one particular author be returned.



It should still be finally ordered by popularity DESC



SELECT * 
FROM `source_posts`
WHERE `posted_by` IN (SELECT `username`
FROM `source_accounts`
WHERE `id` IN (SELECT `sourceid`
FROM `user_source_accounts`
WHERE `profileid` = '100'))
AND `id` NOT IN (SELECT `postid`
FROM `user_posts_removed`
WHERE `profileid` = '100')
AND `live` = '1'
AND `added` >= Date_sub(Now(), INTERVAL 1 month)
AND `popularity` > 1
ORDER BY `popularity` DESC
LIMIT 50


Thank you.



Edit:



I am using MySQL version 5.7.24, so unfortunately the row_number() function will not work in this instance.










share|improve this question




















  • 1




    What is the MySQL server version ? Also, there is just too many IN(.. based subqueries. This will not scale well when data gets bigger.
    – Madhur Bhaiya
    Nov 20 at 18:59
















0












0








0







Background:



I run a platform which allows users to follow creators and view their content.



The following query successfully displays 50 posts ordered by popularity. There is also some other logic to not show posts the user has already saved/removed, but that is not relevant for this question.



Problem:



If one creator is particularly popular (high popularity), the top 50 posts returned will nearly all be by that creator.



This skews the results as ideally the 50 posts returned will not be in favor of one particular author.



Question:



How can I limit it so the author (which uses the field posted_by) is returned no more than 5 times. It could be less, but definitely no more than 5 times should one particular author be returned.



It should still be finally ordered by popularity DESC



SELECT * 
FROM `source_posts`
WHERE `posted_by` IN (SELECT `username`
FROM `source_accounts`
WHERE `id` IN (SELECT `sourceid`
FROM `user_source_accounts`
WHERE `profileid` = '100'))
AND `id` NOT IN (SELECT `postid`
FROM `user_posts_removed`
WHERE `profileid` = '100')
AND `live` = '1'
AND `added` >= Date_sub(Now(), INTERVAL 1 month)
AND `popularity` > 1
ORDER BY `popularity` DESC
LIMIT 50


Thank you.



Edit:



I am using MySQL version 5.7.24, so unfortunately the row_number() function will not work in this instance.










share|improve this question















Background:



I run a platform which allows users to follow creators and view their content.



The following query successfully displays 50 posts ordered by popularity. There is also some other logic to not show posts the user has already saved/removed, but that is not relevant for this question.



Problem:



If one creator is particularly popular (high popularity), the top 50 posts returned will nearly all be by that creator.



This skews the results as ideally the 50 posts returned will not be in favor of one particular author.



Question:



How can I limit it so the author (which uses the field posted_by) is returned no more than 5 times. It could be less, but definitely no more than 5 times should one particular author be returned.



It should still be finally ordered by popularity DESC



SELECT * 
FROM `source_posts`
WHERE `posted_by` IN (SELECT `username`
FROM `source_accounts`
WHERE `id` IN (SELECT `sourceid`
FROM `user_source_accounts`
WHERE `profileid` = '100'))
AND `id` NOT IN (SELECT `postid`
FROM `user_posts_removed`
WHERE `profileid` = '100')
AND `live` = '1'
AND `added` >= Date_sub(Now(), INTERVAL 1 month)
AND `popularity` > 1
ORDER BY `popularity` DESC
LIMIT 50


Thank you.



Edit:



I am using MySQL version 5.7.24, so unfortunately the row_number() function will not work in this instance.







mysql sql






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 21 at 10:10

























asked Nov 20 at 18:56









John Cliven

83212




83212








  • 1




    What is the MySQL server version ? Also, there is just too many IN(.. based subqueries. This will not scale well when data gets bigger.
    – Madhur Bhaiya
    Nov 20 at 18:59
















  • 1




    What is the MySQL server version ? Also, there is just too many IN(.. based subqueries. This will not scale well when data gets bigger.
    – Madhur Bhaiya
    Nov 20 at 18:59










1




1




What is the MySQL server version ? Also, there is just too many IN(.. based subqueries. This will not scale well when data gets bigger.
– Madhur Bhaiya
Nov 20 at 18:59






What is the MySQL server version ? Also, there is just too many IN(.. based subqueries. This will not scale well when data gets bigger.
– Madhur Bhaiya
Nov 20 at 18:59














2 Answers
2






active

oldest

votes


















1














In MySQL 8+, you would simply use row_number():



select sp.*
from (select sp.*,
row_number() over (partition by posted_by order by popularity desc) as seqnum
from source_posts sp
) sp
where seqnum <= 5
order by popularity desc
limit 50;


I'm not sure what the rest of your query is doing, because it is not described in your question. You can, of course, add additional filtering criteria or joins.



EDIT:



In earlier versions, you can use variables:



select sp.*
from (select sp.*,
(@rn := if(@p = posted_by, @rn + 1,
if(@p := posted_by, 1, 1)
)
) as rn
from (select sp.*
from source_posts sp
order by posted_by, popularity desc
) sp cross join
(select @p := '', @rn := 0) params
) sp
where rn <= 5
order by popularity desc
limit 50;





share|improve this answer























  • Thank you - this does work locally however the legacy server that runs my database is on MySQL version 5.7.24. Is there any alternative?
    – John Cliven
    Nov 21 at 10:11





















1














Could try the row number function. Using that, it would assign each employee a distinct "id." So if one employee had 50 records, only those with a row_number (named as "rank") less than or equal to 5 would be returned.



Select *
from(
SELECT `source_posts.*`, row_number() over (partition by `username` order by `popularity` desc) as rank
FROM `source_posts`
WHERE `posted_by` IN (SELECT `username`
FROM `source_accounts`
WHERE `id` IN (SELECT `sourceid`
FROM `user_source_accounts`
WHERE `profileid` = '100'))
AND `id` NOT IN (SELECT `postid`
FROM `user_posts_removed`
WHERE `profileid` = '100')
AND `live` = '1'
AND `added` >= Date_sub(Now(), INTERVAL 1 month)
AND `popularity` > 1
ORDER BY `popularity` DESC
LIMIT 50 `enter code here`)
where rank <= 5





share|improve this answer





















  • This works great locally, however I am running this on a legacy server that uses MySQL version 5.7.24. Is there any alternative?
    – John Cliven
    Nov 21 at 10:10










  • Start with this link. stackoverflow.com/questions/3333665/rank-function-in-mysql ... I will play around with my personal mysql database to see how you would include the order by popularity descending.
    – ayellowbusman
    Nov 22 at 3:43













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
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53399742%2fmysql-limit-field-to-5-maximum-occurrences%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























2 Answers
2






active

oldest

votes








2 Answers
2






active

oldest

votes









active

oldest

votes






active

oldest

votes









1














In MySQL 8+, you would simply use row_number():



select sp.*
from (select sp.*,
row_number() over (partition by posted_by order by popularity desc) as seqnum
from source_posts sp
) sp
where seqnum <= 5
order by popularity desc
limit 50;


I'm not sure what the rest of your query is doing, because it is not described in your question. You can, of course, add additional filtering criteria or joins.



EDIT:



In earlier versions, you can use variables:



select sp.*
from (select sp.*,
(@rn := if(@p = posted_by, @rn + 1,
if(@p := posted_by, 1, 1)
)
) as rn
from (select sp.*
from source_posts sp
order by posted_by, popularity desc
) sp cross join
(select @p := '', @rn := 0) params
) sp
where rn <= 5
order by popularity desc
limit 50;





share|improve this answer























  • Thank you - this does work locally however the legacy server that runs my database is on MySQL version 5.7.24. Is there any alternative?
    – John Cliven
    Nov 21 at 10:11


















1














In MySQL 8+, you would simply use row_number():



select sp.*
from (select sp.*,
row_number() over (partition by posted_by order by popularity desc) as seqnum
from source_posts sp
) sp
where seqnum <= 5
order by popularity desc
limit 50;


I'm not sure what the rest of your query is doing, because it is not described in your question. You can, of course, add additional filtering criteria or joins.



EDIT:



In earlier versions, you can use variables:



select sp.*
from (select sp.*,
(@rn := if(@p = posted_by, @rn + 1,
if(@p := posted_by, 1, 1)
)
) as rn
from (select sp.*
from source_posts sp
order by posted_by, popularity desc
) sp cross join
(select @p := '', @rn := 0) params
) sp
where rn <= 5
order by popularity desc
limit 50;





share|improve this answer























  • Thank you - this does work locally however the legacy server that runs my database is on MySQL version 5.7.24. Is there any alternative?
    – John Cliven
    Nov 21 at 10:11
















1












1








1






In MySQL 8+, you would simply use row_number():



select sp.*
from (select sp.*,
row_number() over (partition by posted_by order by popularity desc) as seqnum
from source_posts sp
) sp
where seqnum <= 5
order by popularity desc
limit 50;


I'm not sure what the rest of your query is doing, because it is not described in your question. You can, of course, add additional filtering criteria or joins.



EDIT:



In earlier versions, you can use variables:



select sp.*
from (select sp.*,
(@rn := if(@p = posted_by, @rn + 1,
if(@p := posted_by, 1, 1)
)
) as rn
from (select sp.*
from source_posts sp
order by posted_by, popularity desc
) sp cross join
(select @p := '', @rn := 0) params
) sp
where rn <= 5
order by popularity desc
limit 50;





share|improve this answer














In MySQL 8+, you would simply use row_number():



select sp.*
from (select sp.*,
row_number() over (partition by posted_by order by popularity desc) as seqnum
from source_posts sp
) sp
where seqnum <= 5
order by popularity desc
limit 50;


I'm not sure what the rest of your query is doing, because it is not described in your question. You can, of course, add additional filtering criteria or joins.



EDIT:



In earlier versions, you can use variables:



select sp.*
from (select sp.*,
(@rn := if(@p = posted_by, @rn + 1,
if(@p := posted_by, 1, 1)
)
) as rn
from (select sp.*
from source_posts sp
order by posted_by, popularity desc
) sp cross join
(select @p := '', @rn := 0) params
) sp
where rn <= 5
order by popularity desc
limit 50;






share|improve this answer














share|improve this answer



share|improve this answer








edited Nov 21 at 11:32

























answered Nov 20 at 18:59









Gordon Linoff

755k35290398




755k35290398












  • Thank you - this does work locally however the legacy server that runs my database is on MySQL version 5.7.24. Is there any alternative?
    – John Cliven
    Nov 21 at 10:11




















  • Thank you - this does work locally however the legacy server that runs my database is on MySQL version 5.7.24. Is there any alternative?
    – John Cliven
    Nov 21 at 10:11


















Thank you - this does work locally however the legacy server that runs my database is on MySQL version 5.7.24. Is there any alternative?
– John Cliven
Nov 21 at 10:11






Thank you - this does work locally however the legacy server that runs my database is on MySQL version 5.7.24. Is there any alternative?
– John Cliven
Nov 21 at 10:11















1














Could try the row number function. Using that, it would assign each employee a distinct "id." So if one employee had 50 records, only those with a row_number (named as "rank") less than or equal to 5 would be returned.



Select *
from(
SELECT `source_posts.*`, row_number() over (partition by `username` order by `popularity` desc) as rank
FROM `source_posts`
WHERE `posted_by` IN (SELECT `username`
FROM `source_accounts`
WHERE `id` IN (SELECT `sourceid`
FROM `user_source_accounts`
WHERE `profileid` = '100'))
AND `id` NOT IN (SELECT `postid`
FROM `user_posts_removed`
WHERE `profileid` = '100')
AND `live` = '1'
AND `added` >= Date_sub(Now(), INTERVAL 1 month)
AND `popularity` > 1
ORDER BY `popularity` DESC
LIMIT 50 `enter code here`)
where rank <= 5





share|improve this answer





















  • This works great locally, however I am running this on a legacy server that uses MySQL version 5.7.24. Is there any alternative?
    – John Cliven
    Nov 21 at 10:10










  • Start with this link. stackoverflow.com/questions/3333665/rank-function-in-mysql ... I will play around with my personal mysql database to see how you would include the order by popularity descending.
    – ayellowbusman
    Nov 22 at 3:43


















1














Could try the row number function. Using that, it would assign each employee a distinct "id." So if one employee had 50 records, only those with a row_number (named as "rank") less than or equal to 5 would be returned.



Select *
from(
SELECT `source_posts.*`, row_number() over (partition by `username` order by `popularity` desc) as rank
FROM `source_posts`
WHERE `posted_by` IN (SELECT `username`
FROM `source_accounts`
WHERE `id` IN (SELECT `sourceid`
FROM `user_source_accounts`
WHERE `profileid` = '100'))
AND `id` NOT IN (SELECT `postid`
FROM `user_posts_removed`
WHERE `profileid` = '100')
AND `live` = '1'
AND `added` >= Date_sub(Now(), INTERVAL 1 month)
AND `popularity` > 1
ORDER BY `popularity` DESC
LIMIT 50 `enter code here`)
where rank <= 5





share|improve this answer





















  • This works great locally, however I am running this on a legacy server that uses MySQL version 5.7.24. Is there any alternative?
    – John Cliven
    Nov 21 at 10:10










  • Start with this link. stackoverflow.com/questions/3333665/rank-function-in-mysql ... I will play around with my personal mysql database to see how you would include the order by popularity descending.
    – ayellowbusman
    Nov 22 at 3:43
















1












1








1






Could try the row number function. Using that, it would assign each employee a distinct "id." So if one employee had 50 records, only those with a row_number (named as "rank") less than or equal to 5 would be returned.



Select *
from(
SELECT `source_posts.*`, row_number() over (partition by `username` order by `popularity` desc) as rank
FROM `source_posts`
WHERE `posted_by` IN (SELECT `username`
FROM `source_accounts`
WHERE `id` IN (SELECT `sourceid`
FROM `user_source_accounts`
WHERE `profileid` = '100'))
AND `id` NOT IN (SELECT `postid`
FROM `user_posts_removed`
WHERE `profileid` = '100')
AND `live` = '1'
AND `added` >= Date_sub(Now(), INTERVAL 1 month)
AND `popularity` > 1
ORDER BY `popularity` DESC
LIMIT 50 `enter code here`)
where rank <= 5





share|improve this answer












Could try the row number function. Using that, it would assign each employee a distinct "id." So if one employee had 50 records, only those with a row_number (named as "rank") less than or equal to 5 would be returned.



Select *
from(
SELECT `source_posts.*`, row_number() over (partition by `username` order by `popularity` desc) as rank
FROM `source_posts`
WHERE `posted_by` IN (SELECT `username`
FROM `source_accounts`
WHERE `id` IN (SELECT `sourceid`
FROM `user_source_accounts`
WHERE `profileid` = '100'))
AND `id` NOT IN (SELECT `postid`
FROM `user_posts_removed`
WHERE `profileid` = '100')
AND `live` = '1'
AND `added` >= Date_sub(Now(), INTERVAL 1 month)
AND `popularity` > 1
ORDER BY `popularity` DESC
LIMIT 50 `enter code here`)
where rank <= 5






share|improve this answer












share|improve this answer



share|improve this answer










answered Nov 20 at 19:04









ayellowbusman

288




288












  • This works great locally, however I am running this on a legacy server that uses MySQL version 5.7.24. Is there any alternative?
    – John Cliven
    Nov 21 at 10:10










  • Start with this link. stackoverflow.com/questions/3333665/rank-function-in-mysql ... I will play around with my personal mysql database to see how you would include the order by popularity descending.
    – ayellowbusman
    Nov 22 at 3:43




















  • This works great locally, however I am running this on a legacy server that uses MySQL version 5.7.24. Is there any alternative?
    – John Cliven
    Nov 21 at 10:10










  • Start with this link. stackoverflow.com/questions/3333665/rank-function-in-mysql ... I will play around with my personal mysql database to see how you would include the order by popularity descending.
    – ayellowbusman
    Nov 22 at 3:43


















This works great locally, however I am running this on a legacy server that uses MySQL version 5.7.24. Is there any alternative?
– John Cliven
Nov 21 at 10:10




This works great locally, however I am running this on a legacy server that uses MySQL version 5.7.24. Is there any alternative?
– John Cliven
Nov 21 at 10:10












Start with this link. stackoverflow.com/questions/3333665/rank-function-in-mysql ... I will play around with my personal mysql database to see how you would include the order by popularity descending.
– ayellowbusman
Nov 22 at 3:43






Start with this link. stackoverflow.com/questions/3333665/rank-function-in-mysql ... I will play around with my personal mysql database to see how you would include the order by popularity descending.
– ayellowbusman
Nov 22 at 3:43




















draft saved

draft discarded




















































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.





Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


Please pay close attention to the following guidance:


  • 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.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53399742%2fmysql-limit-field-to-5-maximum-occurrences%23new-answer', 'question_page');
}
);

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







Popular posts from this blog

Wiesbaden

Marschland

Dieringhausen