I have entries which might or might not be assigned to a topic.
I want to select entries with the highest score by summing up the score of those, which belong to the same topic. In addition, I want to get a number of similar entries, which were grouped together.
SELECT *, COUNT(`topic_id`) FROM `entries`
GROUP BY `topic_id` HAVING `topic_id` > 0
ORDER BY SUM(`score`) DESC LIMIT 30
This query misses a few things. Firstly, I want entries without topic_id (topic_id = 0) not to be grouped, but to be treated individually. Secondly, COUNT(topic_id) does not always return a real number of entries belonging to the same topic.
SELECT * FROM
(
SELECT topic_id,COUNT(*),SUM(`score`) AS sum
FROM `entries`
WHERE `topic_id` > 0
GROUP BY `topic_id`
UNION
SELECT topic_id, 1,1 AS sum
FROM `entries`
WHERE `topic_id` = 0
)
ORDER BY sum DESC LIMIT 30
SELECT
`topic_id` > 0 IsGroupedTopid,
if(`topic_id`>0, `topic_id`, id) TopicOrEntryId,
COUNT(*) CountEntries,
SUM(`score`) TotalScore
FROM `entries`
GROUP BY
`topic_id` > 0,
if(`topic_id`>0, `topic_id`, id)
ORDER BY SUM(`score`) DESC
LIMIT 30;
Related
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.
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;
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
This query
SELECT *
FROM `rounds`
WHERE `user` = 18956 AND `handicapDifferential` IS NOT NULL
ORDER BY `handicapDifferential` DESC, `date` DESC
LIMIT 0, 2
Gives expected result of two rows with -1.4 and -5.1 in handicapDifferential. The field datatype is FLOAT.
Using AVG() on the same result with this query
SELECT AVG(`handicapDifferential`) as `avg`
FROM `rounds`
WHERE `user` = 18956 AND `handicapDifferential` IS NOT NULL
ORDER BY `handicapDifferential` DESC, `date` DESC
LIMIT 0, 2
Gives -9.485714214188713 where I expect -3.25.
Here is a fiddle demonstrating the result and the issue: http://sqlfiddle.com/#!9/32acd/3
What am I doing incorrectly here?
Alter your query to
SELECT
AVG(items.avg)
FROM
(SELECT
`handicapDifferential` AS `avg`
FROM
`rounds`
WHERE `user` = 18956
AND `handicapDifferential` IS NOT NULL
ORDER BY `handicapDifferential` DESC,
`date` DESC
LIMIT 0, 2) items
refer link
Explanation : AVG is a an aggregate keyword in MySQL and you should have given a result set as the input as per your requirement. But u called AVG as a member in your query.
I've got 2 tables: members and member_logs.
Members can belong to groups, which are in the members table. Given a date range and a group I'm trying to figure out how to get the 10 days with the highest number of successful logins. What I have so far is a massive nest of subquery terror.
SELECT count(member_id) AS `num_users`,
DATE_FORMAT(`login_date`,'%Y-%m-%d') AS `reg_date`
FROM member_logs
WHERE `login_success` = 1
and `reg_date` IN
(SELECT DISTINCT DATE_FORMAT(`login_date`,'%Y-%m-%d') AS `reg_date`
FROM member_logs
WHERE `login_success` = 1
and (DATE_FORMAT(`login_date`,'%Y-%m-%d') BETWEEN '2012-02-25' and '2014-03-04'))
and `member_id` IN
(SELECT `member_id`
FROM members
WHERE `group_id` = 'XXXXXXX'
and `deleted` = 0)
ORDER BY `num_users` desc
LIMIT 0, 10
As far as I understand what is happening is that the WHERE clause is evaluating before the subqueries generate, and that I also should be using joins. If anyone can help me out or point me in the right direction that would be incredible.
EDIT: Limit was wrong, fixed it
The first subquery is totally unnecessary because you can filter by dates directly in the current table member_logs. I also prefer a JOIN for the second subquery. Then what you are missing is grouping by date (day).
A query like the following one (not tested) will do the job you want:
SELECT COUNT(ml.member_id) AS `num_users`,
DATE_FORMAT(`login_date`,'%Y-%m-%d') AS `reg_date`
FROM member_logs ml
INNER JOIN members m ON ml.member_id = m.member_id
WHERE `login_success` = 1
AND DATE_FORMAT(`login_date`,'%Y-%m-%d') BETWEEN '2012-02-25' AND '2014-03-04'
AND `group_id` = 'XXXXXXX'
AND `deleted` = 0
GROUP BY `reg_date`
ORDER BY `num_users` desc
LIMIT 10
SELECT count(member_id) AS `num_users`,
DATE_FORMAT(`login_date`,'%Y-%m-%d') AS `reg_date`
FROM member_logs
WHERE `login_success` = 1
and `login_date` IN
(SELECT `login_date`
FROM member_logs
WHERE `login_success` = 1
and (DATE_FORMAT(`login_date`,'%Y-%m-%d') BETWEEN '2012-02-25' and '2014-03-04'))
and `member_id` IN
(SELECT `member_id`
FROM members
WHERE `group_id` = 'XXXXXXX'
and `deleted` = 0)
Group by `login_date`
ORDER BY `num_users` desc
LIMIT 0, 10
As a slightly more index friendly version of the previous answers;
To make the query index friendly, you shouldn't do per row calculations in the search conditions. This query removes the per row calculation of the string format date in the WHERE, so it should be faster if there are many rows to eliminate by date range;
SELECT COUNT(*) num_users, DATE(login_date) reg_date
FROM member_logs JOIN members ON member_logs.member_id = members.member_id
WHERE login_success = 1 AND group_id = 'XXX' AND deleted = 0
AND login_date >= '2012-02-25'
AND login_date < DATE_ADD('2014-03-04', INTERVAL 1 DAY)
GROUP BY DATE(login_date)
ORDER BY num_users DESC
LIMIT 10
I have a list of login logs from our website, however I am needing to see which user ID has had the most IP's logged into it. Our table is as follows:
userid, ip, date (unix)
I need it to output which userid's have had the most IP's logged into them.
I've tried something such as:
SELECT
userID
FROM loginLogs
GROUP BY userID
HAVING COUNT( DISTINCT ip ) > 1
But that just shows a list of user ID's.
Select userID, count(distinct ip)
from loginLogs
Group by 1
Order by 2 desc
Maybe like this?
SELECT `userID`, count(`ip`) cnt FROM `loginLogs` GROUP BY `userID` HAVING cnt > 1
You can just order by distinct values, descending;
SELECT userID, COUNT(DISTINCT ip) `distinct IP#s`
FROM loginLogs
GROUP BY userID
ORDER BY `distinct IP#s` DESC;
An SQLfiddle to test with.
SELECT userID, COUNT(*) AS count FROM loginLogs
GROUP BY userId ORDER BY count DESC
This will give you all of your users from most logged in to the least. Use LIMIT 1 if you want to limit the results.
You have to order those results order by COUNT( DISTINCT ip ) desc and take the first Limit 0, 1
SELECT `userID`
FROM `loginLogs`
GROUP BY `userID`
ORDER BY COUNT( DISTINCT `ip` ) desc
LIMIT 0, 1
You could wrap what you have in a subquery to get the list of userIDs and distinct IPs, as well.
SELECT DISTINCT ll.`userID`, ll.`ip`
FROM ( SELECT `userID`, COUNT( 1 ) AS Cnt
FROM `loginLogs`
GROUP BY `userID`
HAVING COUNT( DISTINCT `ip` ) > 1 ) id
LEFT JOIN `loginLogs` ll
ON id.`userID` = ll.`userID`
ORDER BY id.`Cnt`;
If you just want to see the user with the most ips and you also want to see the list of ips, you can use GROUP_CONCAT():
SELECT `userID`, group_concat(DISTINCT `ip`)
FROM `loginLogs`
GROUP BY `userID`
ORDER BY COUNT( DISTINCT `ip` ) DESC
LIMIT 1
I have the following MYSQL:
SELECT DISTINCT(`user_id`), `type`, `link`, `add_text`
FROM `activity` WHERE `id` != 0 AND `type` !=6 AND (`type` = 4 OR `type` = 5)
ORDER BY `id` DESC LIMIT 4
I only want unique user_ids but I am getting repetitions in my sql results. What is the correct modification to make to this sql so that no repetitions of user_ids
If you don't care about the types, links or add text for each user you could use min/max and group by.
SELECT user_id, max(type), max(link), max(add_text)
FROM activity
WHERE id != 0 A
AND type in (4,5)
Group by User_Id
ORDER BY id DESC LIMIT 4
If you do care you could group_concat the type, link and add text
SELECT user_id, Group_Concat(type), Group_Concat(link), Group_Concat(add_text)
FROM activity
WHERE id != 0 A
AND type in (4,5)
Group by User_Id
ORDER BY id DESC LIMIT 4
edied group by shoudl only be on user_ID
SELECT GROUP_CONCAT(DISTINCT `user_id`) user_ids, `type`, `link`, `add_text`
FROM `activity`
WHERE `id` != 0 AND `type` IN (4,5)
GROUP BY `type`, `link`, `add_text` DESC LIMIT 4;