MySQL sub-query listing information on one line - mysql

I am trying to get a query to show all the data for event prizes on one line. For example this is an example of my table.
Event_id Place Money
101 1 120
101 2 60
101 3 30
102 1 100
102 2 50
102 3 25
What I want is for the results to look like the following:
Event_id First Second Third
101 120 60 30
102 100 50 25
Any help to accomplish this would be greatly appreciated.

Hope this helps.
SELECT Event_ID,
GROUP_CONCAT(IF(place = 1, `money`, NULL)) `First`,
GROUP_CONCAT(IF(place = 2, `money`, NULL)) `Second`,
GROUP_CONCAT(IF(place = 3, `money`, NULL)) `Third`
FROM tableName
GROUP BY event_id
If you want learn more about sql tricks, visit this:
Common but Useful MySQL Queries
SEE on SQLFiddle

This also works for me
SELECT DISTINCT Event_id, (SELECT Money FROM PRIZE WHERE Place=1 AND Event_id=t.Event_id) AS First,
(SELECT Money FROM PRIZE WHERE Place=2 AND Event_id=t.Event_id) AS Second,
(SELECT Money FROM PRIZE WHERE Place=3 AND Event_id=t.Event_id) AS Third
FROM PRIZE t;

Related

Find average time between the first and second listening by users

Let's say I have the following table of user listenings for a music platform:
id
user_id
started_at
finished_at
1
101
'2017-10-05 08:38:22'
'2017-10-05 13:59:03'
2
101
'2017-10-05 15:15:30'
'2017-10-05 15:15:41'
3
101
'2017-10-05 15:15:46'
'2017-10-05 15:46:46'
4
102
'2017-10-12 13:45:27'
'2017-10-12 15:14:49'
5
103
'2017-10-10 12:21:19'
'2017-10-10 12:42:27'
6
103
'2017-10-10 12:45:56'
'2017-10-10 12:52:22'
7
103
'2017-10-10 14:25:54'
'2017-10-10 16:32:57'
8
103
'2017-10-10 16:35:01'
'2017-10-10 18:09:50'
where id (int) is record of user that listened to music (or book or any other material), user_id (int) is a listener id, started_at (timestamp) is time when user started listening and finished_at is when user finished listening
What I need to do is find average time between the first and second listening by user. For example, for a first user with user_id = 101, it will be:
'2017-10-05 15:15:30' (started_at column, second row) - '2017-10-05 13:59:03' (finished_at, first row)
Which gives 1 hour and 15 minutes time difference.
For that case I wrote the following code:
SELECT user_id, (TIMESTAMPDIFF(SECOND, pDataDate, started_at)/3600)
FROM (
SELECT *,
LAG(finished_at) OVER (ORDER BY finished_at) pDataDate
FROM listenings
) q
WHERE pDataDate IS NOT NULL
My problem is in handling the cases in which there is only one user_id in the table (user_id = 102 in this example). It has only one row which means started_at and finished_at at the same column. I don't how to properly write a statement that combines both of the cases. Can someone suggest the query (may be with if/case statements) that covers both of the cases?
Also, I want to find a way to only take the average of the first two top rows of the group. Say, for user_id = 101, I will only take the average between rows 1 and 2.
Thanks in advance and sorry if I couldn't write it clearer. I will probably edit question for convenient reading
Instead of LAG(), use ROW_NUMBER(). You can then use aggreagtion:
SELECT user_id,
TIMESTAMPDIFF(SECOND, MAX(finished_at), NULLIF(MIN(finished_at), MAX(finished_at))) / 3600
FROM (SELECT l.*,
ROW_NUMBER() OVER (PARTITION BY user_id ORJDER BY finished_at) as seqnum
FROM listenings l
) l
WHERE seqnum <= 2
GROUP BY user_id;
This returns NULL if a user has only one row.

Solving for outlier range, how to calculate on two different rows from same output?

I have query below as:
SELECT
age_quartile,
MAX(age) AS quartile_break
from
(SELECT
full_name,
age,
NTILE(4) OVER (ORDER BY age) AS age_quartile
FROM friends) AS quartiles
WHERE age_quartile IN (1, 3)
GROUP BY age_quartile)
This gives me output that looks like:
age_quartile | quantile_break
1 31
3 35
Desired Output:
outlier range
25
41
where 25 = 31-6 and 41 = 35 + 6
How can I add to my query above where I can my final desired output? My query currently gives me what the numbers are where I need to do one additional step to solve for the outlier range. thanks!
table data looks like:
friends
full_name | age
Ameila Lara 1
Evangeline Griffin 21
Kiara Atkinson 31
Isobel Nieslen 31
Genevuve Miles 32
Jane Jenkins 99
Marie Acevedo null
Dont now ntile is the right function to use here. But one way is to define the age quartiles in a temp table and join with age table and find the results. Just a try. There may be better way. Interested to see other answers.
Sample Query:
with friends as
(
select 'user1' as full_name, 31 as age union all
select 'user2' as full_name, 55 as age union all
select 'user3' as full_name, 75 as age
),
quartiles_age as
(
select 1 as quartile, 0 as st_range, 25 as end_range union all
select 2 as quartile, 26 as st_range, 50 as end_range union all
select 3 as quartile, 51 as st_range, 75 as end_range union all
select 4 as quartile, 76 as st_range, 100 as end_range
)
SELECT
fr.full_name,
fr.age,
qrtl_age.quartile,
qrtl_age.end_range - fr.age as diff_age
FROM
friends fr
join quartiles_age qrtl_age on fr.age between qrtl_age.st_range and qrtl_age.end_range
Fiddle URL : (https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=c48d209264e90d276cea6ae03f2a7af6)
You can calculate the range using:
MAX(age) - MIN(age) AS age_range
That answers the question you asked. Your sample data has an arbitrary 6 for the calculation, which the question does not explain.

Creating a query that converts "chained" ids to "grouped" ids

I request anyone to help me title this question better, I am unsure on how to call it. Perhaps someone will have an idea after they read the question below:
So I have a set of players and their groups stored in MySQL. In the DB, due to legacy reasons they are stored in a strange manner (like a chain), as shown below:
Player_Id New_Group
100 1
101 0
102 0
103 0
104 1
105 0
106 0
107 0
108 0
So here basically PlayerId 100 has New_Group set as 1. So he is at the start of a new group. Now all the players after him since they have New_Group as 0, fall into his group. When we get to 104, a new group starts.
What I want to do is to write a query which will give result like so:
Player_Id Group_Id
100 1
101 1
102 1
103 1
104 2
105 2
106 2
107 2
108 2
You get the idea...I am trying to convert the New_Group logic into something that just generates pseudo-group-Ids for the players.
However it is beyond my SQL skills to do this. If any SQL Guru can help me that would be AWESOME. I use MySQL.
How's this?
SET #group = 0;
SELECT Player_Id, IF(New_Group, #group:=#group + 1, #group) AS Group_Id
FROM [table]
ORDER BY Player_Id;
SELECT player_id
, CASE WHEN new_group = 1 THEN #i:=#i+1 ELSE #i:=#i END i
FROM my_table
, (SELECT #i:=0) vars
ORDER
BY player_id;
Assuming you have some column in the table that is ascending you could do:
select Player_Id, count(select * from [table] t2 where new_group=1 and t2.ascending_row <= t1.ascending_row) from [table] t1
In your example above Player_Id could be used for ascending_row as the values are all incrementing. If not you may need to create a temporary table with a column containing the row id and then use that.

MySQL - Multiple Conditions and Their Results with GROUP BY

table name : users u
id username src
1 mark 101
2 stanley 102
3 john 103
4 stewe 104
table name : call_history c
id src dst duration
1 101 555-1217 20
2 555-1315 102 30
3 555-2245 102 40
4 102 555-6523 30
5 102 555-4213 20
6 555-1689 102 15
7 103 555-1775 35
There are two tables and these columns.
Conditions are;
SUM(duration) AS OutboundSUM (Condition: u.src=c.src )
COUNT(duration) AS OutboundCNT (Condition: u.src=c.src )
SUM(duration) AS InboundSUM (Condition: u.src=c.dst )
COUNT(duration) AS InboundCNT (Condition: u.src=c.dst )
What I need to see with Group By per username;
username OutboundSUM OutboundCNT InboundSUM InboundCNT
mark 20 1 0 0
stanley 50 2 85 3
john 35 1 0 0
stewe 0 0 0 0
I tried UNION ALL, sub query after select, INNER JOIN but It didn't work.
Union gives me 2 line for each username, join makes me crazy, sub queries takes longtime and wrong results.
All help is appreciated.
Problem, solved with Kicstart's solution. Thank you very much for each help.
Can you provide the primary keys of the table? It would help us give better answers.
Also, you MUST include both conditions u.src=c.src AND u.id=c.id for join.
If the IDs of the two tables do not relate to each other, I suggest you use different attribute names.
Couple of sub queries?
SELECT a.src,
oBSum AS OutboundSUM,
oBCnt AS OutboundCNT,
iBSum AS InboundSUM,
iBCnt AS InboundCNT
FROM users u
LEFT OUTER JOIN (SELECT src, SUM(duration) AS oBSum, COUNT(*) AS oBCnt FROM call_history GROUP BY src) ob
ON u.src = ob.src
LEFT OUTER JOIN (SELECT dst, SUM(duration) AS iBSum, COUNT(*) AS iBCnt FROM call_history GROUP BY dst) ib
ON u.src = ib.dst

how to select Count of Ranges from mysql table?

i have a table t_points in mySql like example in below.
Name Surname Point
Joe Arnold 120
Michale Black 250
Masha Petrova 300
Natalie Jackson 120
John Turo 200
Bona Meseda 250
Zeyda Nura 150
Zanura Bohara 60
Shaheen Boz 360
Abbas Murat 160
Keira Black 230
Tom Robinson 480
Fred Balka 490
Semia Hudovi 90
Sona Bahari 60
i want to write a query which will display the count of point ranges. Point ranges are like this: point between 0 and 100, 101 and 200, 201 and 300, 301 and 400.
Result must be like below
0_100 101_200 201_300 301_400
3 5 4 3
i think u understand what i want to say. So which query i have to use for this result?
Thanks.
select
count(CASE WHEN point BETWEEN 0 AND 100 THEN 1 END) as count0_100,
count(CASE WHEN point BETWEEN 101 AND 200 THEN 1 END) as count101_200,
count(CASE WHEN point BETWEEN 201 AND 300 THEN 1 END) as count201_300,
...
from
t_poits
Something like that:
select count(*) as count, abs(point/100) as range
from t_poits
group by abs(point/100)
set #range = 500;
select floor(field1/#range)*#range as `from`,(ceil(field1/#range)+if(mod(field1,#range)=0,1,0))*#range as `to`, count(field1)
from table1
group by 1,2
order by 1,2;
You can group points column and do some math operation to specify range. Based on that you can count number of records.
Like..
SELECT concat( 101 * round( Point /101 ) , '-', 101 * round( Point /101 ) +100 ) AS `range` , count( * ) AS `result`
FROM t_points
GROUP BY 1
ORDER BY Point
I hope it will work for you.
select
concat(floor(Point/100),'01_',ceil(Point/100),'00') as ind,
count(*) as cnt
from t group by ind order by ind asc;