SQL writing custom query - mysql

I need to write a SQL Query which generates the name of the most popular story for each user (according to total reading counts). Here is some sample data:
story_name | user | age | reading_counts
-----------|-------|-----|---------------
story 1 | user1 | 4 | 12
story 2 | user2 | 6 | 14
story 4 | user1 | 4 | 15
This is what I have so far but I don't think it's correct:
Select *
From mytable
where (story_name,reading_counts)
IN (Select id, Max(reading_counts)
FROM mytable
Group BY user
)

In a Derived Table, you can first determine the maximum reading_counts for every user (Group By with Max())
Now, simply join this result-set to the main table on user and reading_counts, to get the row corresponding to maximum reading_counts for a user.
Try the following query:
SELECT
t1.*
FROM mytable AS t1
JOIN
(
SELECT t2.user,
MAX(t2.reading_counts) AS max_count
FROM mytable AS t2
GROUP BY t2.user
) AS dt
ON dt.user = t1.user AND
dt.max_count = t1.reading_counts

SELECT *
FROM mytable
WHERE user IN
(SELECT user, max(reading_counts)
FROM mytable
GROUP BY user)

Related

SQL get max of columns where a row equals something

If I have Table with 3-columns:
Date | Name | Num
oct1 | Bob | 2
oct2 | Zayne | 1
oct1 | Test | 5
oct2 | Apple | 7
I want to retrieve the rows where Num is MAX,
WHERE Date = oct1 or Date = oct2
So I want result to be:
oct1 Test 5
oct2 Apple 7
MYSQL is preferred. But SQL answer be given also. Thanks.
You can try below using correlated subquery
select * from tablename a
where num in (select max(num) from tablename b where a.date=b.date)
and date in ('oct1', 'oct2')
It sounds like you want this query:
SELECT t1.*
FROM yourTable t1
INNER JOIN
(
SELECT Date, MAX(Num) AS max_num
FROM yourTable
WHERE Date IN ('oct1', 'oct2')
GROUP BY Date
) t2
ON t1.Date = t2.Date AND t1.Num = t2.max_num
WHERE t1.Date IN ('oct1', 'oct2');
By the way, you should seriously consider storing proper date data in an actual date or datetime column in MySQL. It appears you are just storing text right now, which would be hard to work with.
You can try to use correctly subquery
Schema (MySQL v5.7)
CREATE TABLE T(
Date VARCHAR(50),
Name VARCHAR(50),
Num INT
);
INSERT INTO T VALUES ('oct1','Bob',2);
INSERT INTO T VALUES ('oct2','Zayne',1);
INSERT INTO T VALUES ('oct1','Test',5);
INSERT INTO T VALUES ('oct2','Apple',7);
Query #1
SELECT *
FROM T t1
WHERE Num = (SELECT MAX(Num) FROM T tt WHERE t1.Date = tt.Date)
AND
t1.Date in ('oct1','oct2')
| Date | Name | Num |
| ---- | ----- | --- |
| oct1 | Test | 5 |
| oct2 | Apple | 7 |
View on DB Fiddle
As you where asking for a standard way to do this: All the answers given so far comply with the SQL standard. One more possible approach in standard SQL is to use a window function. This is only featured in MySQL as of version 8, however.
select date, name, num
from
(
select date, name, num, max(num) over (partition by date) as max_num
from mytable
) analyzed
where num = maxnum
order by date;
This only reads the table once, which can (but not necessarily does) speed up the query.
You can use corelated subquery just like below
SELECT *
FROM T t1
WHERE Num = (SELECT MAX(Num) FROM T t2 WHERE t2.Date = t1.Date)
Fiddle link
Date Name Num
oct1 Test 5
oct2 Apple 7

MySQL Query with the count, group by

Table: statistics
id | user | Message
----------------------
1 | user1 |message1
2 | user2 |message2
3 | user1 |message3
I am able to find the count of messages sent by each user using this query.
select user, count(*) from statistics group by user;
How to show message column data along with the count? For example
user | count | message
------------------------
user1| 2 |message1
|message3
user2| 1 |message2
You seem to want to show Count by user, which message sent by user.
If your mysql version didn't support window functions, you can do subquery to make row_number in select subquery, then only display rn=1 users and count
CREATE TABLE T(
id INT,
user VARCHAR(50),
Message VARCHAR(100)
);
INSERT INTO T VALUES(1,'user1' ,'message1');
INSERT INTO T VALUES(2,'user2' ,'message2');
INSERT INTO T VALUES(3,'user1' ,'message3');
Query 1:
SELECT (case when rn = 1 then user else '' end) 'users',
(case when rn = 1 then cnt else '' end) 'count',
message
FROM (
select
t1.user,
t2.cnt,
t1.message,
(SELECT COUNT(*) from t tt WHERE tt.user = t1.user and t1.id >= tt.id) rn
from T t1
join (
select user, count(*) cnt
from T
group by user
) t2 on t1.user = t2.user
) t1
order by user,message
Results:
| users | count | message |
|-------|-------|----------|
| user1 | 2 | message1 |
| | | message3 |
| user2 | 1 | message2 |
select user, count(*) as 'total' , group_concat(message) from statistics group by user;
You could join the result of your group by with the full table (or vice versa)?
Or, depending on what you want, you could use group_concat() using \n as separator.
Use Group_concat
select user, count(0) as ct,group_concat(Message) from statistics group by user;
This will give you message in csv format
NOTE: GROUP_CONCAT has size limit of 1024 characters by default in mysql.
For UTF it goes to 1024/3 and utfmb4 255(1024/4).
You can use group_concat_max_len global variable to set its max length as per need but take into account memory considerations on production environment
SET group_concat_max_len=100000000
Update:
You can use any separator in group_concat
Group_concat(Message SEPARATOR '----')
Try grouping with self-join:
select s1.user, s2.cnt, s1.message
from statistics s1
join (
select user, count(*) cnt
from statistics
group by user
) s2 on s1.user = s2.user

MySQL - Table Query Inner Joining to itself

Consider the above query result,
Is there a way I can join the table itself to get the following results:-
POH_ID | JOH_ID | .............
-------------------------------------------
NULL | JOH_00000002 | .............
POH_00000002 | JOH_00000001 | .............
POH_00000001 | JOH_00000001 | .............
Meaning, if there's only a single JOH_ID, I retrieve that particular row, if there's more than one of the same JOH_ID, I retrieve the ones with POH_ID.
The result in the photo is a result of a query
You could find count of rows with same joh_id, join it with main table to filter the rows which have either only one row per joh_id or non-null poh_id
select t.*
from your_table t
join (
select joh_id, count(*) as cnt
from your_table
group by joh_id
) t2 on t.joh_id = t2.joh_id
where t2.cnt = 1 or t.poh_id is not null;

Select inner join with n_m table

I have three tables as following:
USERS TABLE
id_user| name |
---------------
1 | ...
2 | ...
SERVICES TABLE
id_service | name |
-------------------
1 | ...
2 | ...
3 | ...
USER_SERVICES TABLE (n-m)
id_user | id_service
--------------------
1 | 1
1 | 2
2 | 1
And I need to do a SELECT starting from "SELECT * FROM users" and then, getting the users by services. Ex. I need to get every user with services = 1 and services = 2 (and maybe he has other more services, but 1 and 2 for sure).
I did the following:
SELECT *
FROM `users`
INNER JOIN user_services ON users.id_user = user_services.id_user
WHERE id_service=1 AND id_service=2
But this, of course dont works since there is not a single record matching service = 1 and service = 2.
What can I do?
Add an extra join for the other service you want to check:-
SELECT *
FROM `users`
INNER JOIN user_services us1 ON users.id_user = us1.id_user AND us1.id_service=1
INNER JOIN user_services us2 ON users.id_user = us2.id_user AND us2.id_service=2
select t.*,
(select count(*) from user_services where id_user = t.id_user) how_much
from users t;
Is this what you want???
It shows the data of the users and how much services are in the services table. Other possibility is this:
select t.*,
(case when (select count(*)
from user_services where id_user = 1) > 0
then 'service1'
else 'null'
end) has_service_1
from users t;
The problem with this select is that you have to repeat this case...end as much times as id_services you have, so it doesn't make sense if the number of services is increasing over time. On the contrary, if it is a somewhat fixed number, and it is not a big number, this could be a solution.

MySQL getting the lowest ID for a certain user -or- the ID of the entry with the highest urgency for each row

I have the following database
id | user | urgency | problem | solved
The information in there has different users, but these users all have multiple entries
1 | marco | 0 | MySQL problem | n
2 | marco | 0 | Email problem | n
3 | eddy | 0 | Email problem | n
4 | eddy | 1 | MTV doesn't work | n
5 | frank | 0 | out of coffee | y
What I want to do is this: Normally I would check everybody's oldest problem first. I use this query to get the ID's of the oldest problem.
select min(id) from db group by user
this gives me a list of the oldest problem ID's. But I want people to be able to make a certain problem more urgent. I want the ID with the highest urgency for each user, or ID of the problem with the highest urgency
Getting the max(urgency) won't give the ID of the problem, it will give me the max urgency.
To be clear: I want to get this as a result
row | id
0 | 1
1 | 4
The last entry should be in the results since it's solved
Select ...
From SomeTable As T
Join (
Select T1.User, Min( T1.Id ) As Id
From SomeTable As T1
Join (
Select T2.User, Max( T2.Urgency ) As Urgency
From SomeTable As T2
Where T2.Solved = 'n'
Group By T2.User
) As MaxUrgency
On MaxUrgency.User = T1.User
And MaxUrgency.Urgency = T1.Urgency
Where T1.Solved = 'n'
Group By T1.User
) As Z
On Z.User = T.User
And Z.Id = T.Id
There are lots of esoteric ways to do this, but here's one of the clearer ones.
First build a query go get your min id and max urgency:
SELECT
user,
MIN(id) AS min_id,
MAX(urgency) AS max_urgency
FROM
db
GROUP BY
user
Then incorporate that as a logical table into
a larger query for your answers:
SELECT
user,
min_id,
max_urgency,
( SELECT MIN(id) FROM db
WHERE user = a.user
AND urgency = a.max_urgency
) AS max_urgency_min_id
FROM
(
SELECT
user,
MIN(id) AS min_id,
MAX(urgency) AS max_urgency
FROM
db
GROUP BY
user
) AS a
Given the obvious indexes, this should be pretty efficient.
The following will get you exactly one row back -- the most urgent, probably oldest problem in your table.
select id from my_table where id = (
select min(id) from my_table where urgency = (
select max(urgency) from my_table
)
)
I was about to suggest adding a create_date column to your table so that you could get the oldest problem first for those problems of the same urgency level. But I'm now assuming you're using the lowest ID for that purpose.
But now I see you wanted a list of them. For that, you'd sort the results by ID:
select id from my_table where urgency = (
select max(urgency) from my_table
) order by id;
[Edit: Left out the order by!]
I forget, honestly, how to get the row number. Someone on the interwebs suggests something like this, but no idea if it works:
select #rownum:=#rownum+1 ‘row', id from my_table where ...