Gereralise the MYSQL query for all user_id - mysql

I have a mysql query
select sum(duration),app_name,app_id,user_id
from timesheet
where user_id=164
group by app_id
order by sum(duration) desc
limit 5;
The result is
sum(duration), app_name, app_id, user_id
'626919371', 'Idle.exe', '0', '164'
'38220511', 'LockApp.exe', '2204', '164'
'36675000', '', '1', '164'
'27713000', 'LockApp.exe', '8148', '164'
'16698661', 'chrome.exe', '8548', '164'
However I want the top 5 app for every user_id instead of just 164. Can you pelase guide me as to how this can be achieved in a single query. Or do I need to fire it for every individual user

This should work:
SELECT *
FROM (
SELECT *, rank() over (PARTITION BY user_id ORDER BY duration desc) AS rank
FROM (
SELECT user_id, app_id, sum(duration) AS duration
FROM timesheet
GROUP BY user_id, app_id
) sub2
) sub
WHERE rank < 6
If you want the query to always return 5 rows per user (even if there are some top applications with the same rank) replace rank() with row_number()

Related

MYSQL sum of max score taken users list

I have following table
CREATE TABLE Table1
(`userid` varchar(11), `score` int, `type` varchar(22));
INSERT INTO Table1
(`userid`, `score`,`type`)
VALUES
(11, 2,'leader'),
(11, 6,'leader'),
(13, 6,'leader'),
(15, 4,'leader'),
(15, 4,'leader'),
(12, 1,'leader'),
(14, 1,'leader');
I need to get userid of the maximum score take user.
if the max score is the same for two or more user need to get that userid also.
I have try following query
SELECT userid, sum(score) as totalScore
FROM Table1 WHERE type = "leader" GROUP BY userid
ORDER BY totalScore DESC;
But it gets all user data, cant get the max score take the first two users id.
But I need to get only first two row of data ..
Please help me
On MySQL 8+, I suggest using the RANK() analytic function:
WITH cte AS (
SELECT userid, SUM(score) AS totalScore,
RANK() OVER (ORDER BY SUM(score) DESC) rnk
FROM Table1
WHERE type = 'leader'
GROUP BY userid
)
SELECT userid, totalScore
FROM cte
WHERE rnk = 1;
if you need just top 2 records add limit in your query :
SELECT userid, sum(score) as totalScore
FROM Table1 WHERE type = "leader" GROUP BY userid
ORDER BY totalScore DESC LIMIT 2;

get the most common value for each column

I'm attempting to create an SQL query that retrieves the total_cost for every row in a table. Alongside that, I also need to collect the most dominant value for both columnA and columnB, with their respective values.
For example, with the following table contents:
cost
columnA
columnB
target
250
Foo
Bar
XYZ
200
Foo
Bar
XYZ
150
Bar
Bar
ABC
250
Foo
Bar
ABC
The result would need to be:
total_cost
columnA_dominant
columnB_dominant
columnA_value
columnB_value
850
Foo
Bar
250
400
Now I can get as far as calculating the total cost - that's no issue. I can also get the most dominant value for columnA using this answer. But after this, I'm not sure how to also get the dominant value for columnB and the values too.
This is my current SQL:
SELECT
SUM(`cost`) AS `total_cost`,
COUNT(`columnA`) AS `columnA_dominant`
FROM `table`
GROUP BY `columnA_dominant`
ORDER BY `columnA_dominant` DESC
WHERE `target` = "ABC"
UPDATE: Thanks to #Barmar for the idea of using a subquery, I managed to get the dominant values for columnA and columnB:
SELECT
-- Retrieve total cost.
SUM(`cost`) AS `total_cost`,
-- Get dominant values.
(
SELECT `columnA`
FROM `table`
GROUP BY `columnA`
ORDER BY COUNT(*) DESC
LIMIT 1
) AS `columnA_dominant`,
(
SELECT `columnB`
FROM `table`
GROUP BY `columnB`
ORDER BY COUNT(*) DESC
LIMIT 1
) AS `columnB_dominant`
FROM `table`
WHERE `target` = "XYZ"
However, I'm still having issues figuring out how to calculate the respective values.
You might get close, if we want to get percentage values we can try to add COUNT(*) at subquery to get max count by columnA and columnB then do division by total count
SELECT
SUM(cost),
(
SELECT tt.columnA
FROM T tt
GROUP BY tt.columnA
ORDER BY COUNT(*) DESC
LIMIT 1
) AS columnA_dominant,
(
SELECT tt.columnB
FROM T tt
GROUP BY tt.columnB
ORDER BY COUNT(*) DESC
LIMIT 1
) AS columnB_dominant,
(
SELECT COUNT(*)
FROM T tt
GROUP BY tt.columnA
ORDER BY COUNT(*) DESC
LIMIT 1
) / COUNT(*) AS columnA_percentage,
(
SELECT COUNT(*)
FROM T tt
GROUP BY tt.columnB
ORDER BY COUNT(*) DESC
LIMIT 1
) / COUNT(*) AS columnB_percentage
FROM T t1
If your MySQL version supports the window function, there is another way which reduce table scan might get better performance than a correlated subquery
SELECT SUM(cost) OVER(),
FIRST_VALUE(columnA) OVER (ORDER BY counter1 DESC) columnA_dominant,
FIRST_VALUE(columnB) OVER (ORDER BY counter2 DESC) columnB_dominant,
FIRST_VALUE(counter1) OVER (ORDER BY counter1 DESC) / COUNT(*) OVER() columnA_percentage,
FIRST_VALUE(counter2) OVER (ORDER BY counter2 DESC) / COUNT(*) OVER() columnB_percentage
FROM (
SELECT *,
COUNT(*) OVER (PARTITION BY columnA) counter1,
COUNT(*) OVER (PARTITION BY columnB) counter2
FROM T
) t1
LIMIT 1
sqlfiddle
try this query
select sum(cost) as total_cost,p.columnA,q.columnB,p.columnA_percentage,q.columnB_percentage
from get_common,(
select top 1 columnA,columnA_percentage
from(
select columnA,count(columnA) as count_columnA,cast(count(columnA) as float)/(select count(columnA) from get_common) as columnA_percentage
from get_common
group by columnA)s
order by count_columnA desc
)p,
(select top 1 columnB,columnB_percentage
from (
select columnB,count(columnB) as count_columnB, cast(count(columnB) as float)/(select count(columnB) from get_common) as columnB_percentage
from get_common
group by columnB) t
order by count_columnB desc)q
group by p.columnA,q.columnB,p.columnA_percentage,q.columnB_percentage
so if you want to get the percent and dominant value you must make their own query like this
select top 1 columnA,columnA_percentage
from(
select columnA,count(columnA) as count_columnA,cast(count(columnA) as float)/(select count(columnA) from get_common) as columnA_percentage
from get_common
group by columnA)s
order by count_columnA desc
then you can join with the sum query to get all value you want
hope this can help you

SQL query not returning distinct values

I have a game leaderboard comprised of 500 rows of data and I wrote a script to return that data and have no duplicate scores. However, I am getting duplicate scores returned to me. Here is my script.
SELECT DISTINCT
username, score,
FIND_IN_SET(score, (SELECT DISTINCT GROUP_CONCAT(score ORDER BY score DESC)
FROM TPS_STATS)) AS rank
FROM
TPS_STATS
ORDER BY
rank ASC
LIMIT 100;
An example of the duplicate results I am seeing is posted as an image.
If your version of MySql is 8.0 then you can use row_number():
SELECT
username,
score,
row_number() OVER (ORDER BY score desc, username) rn
FROM TPS_STATS
ORDER BY score desc, username
LIMIT 100
See the demo.
If it is lower:
select
username,
score,
(select count(*) from TPS_STATS where score > t.score) +
(select count(*) from TPS_STATS where score = t.score and username < t.username) + 1
rank
from TPS_STATS t
order by rank, username
limit 100
See the demo

MySQL select most used IP's from user ID's

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

mysql 2 set of sum using groupby rollup

I have a query
select user_id,sum(hours),date, task_id from table where used_id = 'x' and date >='' and date<= '' group by user_id, date, task_id with roll up
The query works fine. But I also need to find a second sum(hours) where the group by order is changed.
select user_id,sum(hours),date, task_id from table where used_id = 'x' group by user_id,task_id
(The actual where condition is much longer.)
Is it possible to get both the sum in a single query since the where condition almost the same?
SELECT * FROM (
SELECT 1 AS list_id
, user_id
, sum(hours) AS total_hours
, `date`
, task_id
FROM table WHERE used_id = 'x' AND `date` BETWEEN #thisdate AND #thatdate
GROUP BY user_id, `date`, task_id /*WITH ROLLUP*/
UNION ALL
SELECT 2 AS list_id
, user_id
, sum(hours) AS total_hours
, `date`
, task_id
FROM table
WHERE used_id = 'x'
GROUP BY user_id,task_id WITH ROLLUP ) q
/*ORDER BY q.list_id, q.user_id, q.`date`, q.task_id*/
Depending on your needs, you should only need one with rollup, or two.