CROSS JOIN in SQL (MySQL) - mysql

I want to make a query like that
SELECT playerId, SUM(distance) as distance, sprint.sprint, SUM(calories) as calories,
SUM(stepsRate) as steps, SUM(jump) as jump, deviceId as I
FROM readings as re
CROSS JOIN (select count(agg.maxSpeed) as sprint from(
select max(r.distance) as maxSpeed,
CASE
WHEN r.distance > 20 THEN #rownr
ELSE #rownr := #rownr + 1
END as flag
from readings as r
CROSS JOIN (SELECT #rownr := 0) AS dummy
**where r.playerId = re.playerId**
group by flag) as agg
where agg.maxSpeed > 20) AS sprint
GROUP BY playerId;
but the problem is I can't make Whare Statement in
where r.playerId = re.playerId
I can't reach to playerId
My Reading Table Schema
Id int(11) AI PK
deviceId varchar(50)
playerId int(11)
heartRate int(11)
distance double
calories double
StepsRate double
jump double
sessionId int(11)
Some Sample Data
Id | DeviceId | PlayerId | HeartRate | distance | calories
21711 AAAAA2 2016 148 38 10
21721 AAAAA2 2016 126 33.5 13
21731 AAAAA2 2016 111 33.5 6
21741 AAAAA2 2016 192 33.5 22
Expected query result
PlayerId | Distance | Sprint | Calories | Steps | DeviceId
1002 4993 49 6750 10 AAAAA1
2016 2592 49 5775 10 AAAAA2
2017 9994 49 7790 10 AAAAA3
2018 5939 49 5350 10 AAAAA4
2019 4794 49 6259 10 AAAAA5
The CROSS JOIN get the COUNT of Sprint but I can't pass the playerId to the WHERE statement in CROSS JOIN

Related

Mysql Current Winning streak

I have been trying to get the current winning streak based on a results table. I am having lots of trouble with this though. I have managed to get what the teams highest win streak for each team over the full period is. But what I cannot manage to do is get the current win streak if they have one based on the last result per team.
For example the below table where the result is 'H' indicated as win.
TeamID Result Date
25 A 02/12/17
25 H 16/12/17
25 D 22/12/17
25 D 03/01/18
25 H 20/01/18
28 D 09/12/17
28 D 23/12/17
28 H 01/01/18
28 H 20/01/18
58 H 02/12/17
58 A 16/12/17
58 H 23/12/17
58 H 01/01/18
58 D 20/01/18
61 D 03/12/17
61 A 17/12/17
61 D 26/12/17
61 H 30/12/17
61 H 14/01/18
So TeamID 25 has a current win streak of 1. TeamID 28 a win streak of 2 and Team 58 a win streak of 0 as there last result was 'D' (Draw)
I have used the below code which I can get working but not with the additional grouping the results come back all as 1 which is clearly wrong.
http://www.sqlteam.com/article/detecting-runs-or-streaks-in-your-data
I have looked at other bits of code on here but none of them appear to be based on the last result or they are SQL Server based but I need the code to work in MySQL. Thanks for any help on this.
How to ask a question;
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(TeamID INT NOT NULL
,Result CHAR(1) NOT NULL
,Date DATE NOT NULL
,PRIMARY KEY(teamid,date)
);
INSERT INTO my_table VALUES
(25,'A','2017-12-02'),
(25,'H','2017-12-16'),
(25,'D','2017-12-22'),
(25,'D','2018-01-03'),
(25,'H','2018-01-20'),
(28,'D','2017-12-09'),
(28,'D','2017-12-23'),
(28,'H','2018-01-01'),
(28,'H','2018-01-20'),
(58,'H','2017-12-02'),
(58,'A','2017-12-16'),
(58,'H','2017-12-23'),
(58,'H','2018-01-01'),
(58,'D','2018-01-20'),
(61,'D','2017-12-03'),
(61,'A','2017-12-17'),
(61,'D','2017-12-26'),
(61,'H','2017-12-30'),
(61,'H','2018-01-14');
How to answer one:
SELECT a.*
FROM
( SELECT x.*
, CASE WHEN #prev=teamid THEN CASE WHEN result = 'H' THEN #i:=#i+1 ELSE #i:=0 END ELSE #i:=0 END i
, #prev:=teamid prev
FROM my_table x
, (SELECT #prev:=null,#i:=0) vars
ORDER
BY teamid
, date
) a
JOIN
( SELECT teamid
, MAX(date) date
FROM my_table
GROUP
BY teamid
) b
ON b.teamid = a.teamid
AND b.date = a.date;
+--------+--------+------------+------+------+
| TeamID | Result | Date | i | prev |
+--------+--------+------------+------+------+
| 25 | H | 2018-01-20 | 1 | 25 |
| 28 | H | 2018-01-20 | 2 | 28 |
| 58 | D | 2018-01-20 | 0 | 58 |
| 61 | H | 2018-01-14 | 2 | 61 |
+--------+--------+------------+------+------+

Select multiple fields from subquery

I have the next query:
SELECT
a.Date,
(SELECT SUM(Used), SUM(Max) FROM Switch_Statistic b WHERE Date = (SELECT MAX(Date) FROM Switch_Statistic WHERE Switch_ID = b.Switch_ID AND Date <= a.Date))
FROM Switch_Statistic a
GROUP BY Date;
As you see I need to select SUM(Used), SUM(Max) from subquery. With CONCAT is not good solution!
Table schema:
ID --- Switch_ID --- Date --- Max --- Used
Some data:
1 641 2014-10-04 2 16
20 630 2014-10-04 1 7
24 634 2014-10-04 0 8
26 641 2014-10-06 2 16
32 641 2014-10-07 2 16
35 641 2014-10-08 3 16
39 641 2014-10-09 2 16
64 293 2014-10-10 1 22
...
557 38 2014-10-12 3 22
559 293 2014-10-12 1 22
563 294 2014-10-12 6 22
565 641 2014-10-12 2 16
What I need:
Example with CONCAT_WS
mysql> SELECT
a.Date,
(SELECT CONCAT_WS('/', SUM(Used), SUM(Max)) FROM Switch_Statistic b WHERE Date = (SELECT MAX(Date) FROM Switch_Statistic WHERE Switch_ID = b.Switch_ID AND Date <= a.Date)) AS Result
FROM Switch_Statistic a
GROUP BY Date;
+------------+----------+
| Date | Result |
+------------+----------+
| 2014-10-04 | 3/31 |
| 2014-10-06 | 3/31 |
| 2014-10-07 | 3/31 |
| 2014-10-08 | 4/31 |
| 2014-10-09 | 3/31 |
| 2014-10-10 | 249/1587 |
| 2014-10-11 | 354/2147 |
| 2014-10-12 | 360/2185 |
+------------+----------+
8 rows in set (0.26 sec)
Query logic:
1) Select all date's from table
2) SUM - Used and Max for current date, if Switch_ID don't have record for this date, then select the last which exists in table
Link to sqlfiddle - http://sqlfiddle.com/#!2/c3d479
You should be able to do this with just aggregation and no subqueries or joins:
SELECT date, sum(used) as used, sum(max) as max
FROM switch_statistic ss
where ss.date = (select max(date) from Switch_Statistics ss2 where ss2.Switch_id = ss.SwitchId
GROUP BY ss.date;
EDIT:
You seem to want a cumulative sum. In MySQL, this is often best done using variables:
SELECT date, used, max, (#u := #u + used) as cumeused, #m := #m + max) as cumemax
fROM (SELECT date, sum(used) as used, sum(max) as max
FROM switch_statistic ss
GROUP BY ss.date
) ss CROSS JOIN
(SELECT #u := 0, #m := 0) vars
ORDER BY date;

select data based on multiple criteria (cloest value)

I am using MySQL. I'm trying to build something and just can't find a solution to a problem.
I am selecting a value from the lookup table based on my table as shown in the below example.
Select Criteria:
my.id<>l.id AND my.route1=l.route1 AND my.route2=l.route2 AND my.utc=l.utc
where my.stime is closest or same as l.stime
ex) my.id=2's col should get the l.id=1, l.etime=7777 since my.id<>l.id and the rest are the same.
ex) my,id=5's col has options l.id=3, l.etime=9999 and l.id=4, l.etime=7979 since my.id<>l.id, my.route=l.route, my.utc=l.utc. Yet, since my.stime=2220 is closer to l.stime=2222 than l.stime=3333 , l.id=3, l.etime=9999 will be chosen.
ex) my,id=6's col example is to select either value if "closest" is the same.
ex) my,id=7's col example is to return NULL when the criteria is not met.
Table: lookup (l.)
id route1 route2 utc stime etime
---|--------|--------|-----|-------|------
1 11 22 111 1111 7777
2 11 22 111 1111 8888
3 22 33 222 2222 9999
4 22 33 222 3333 7979
5 22 33 222 3335 8989
Table: my (my.) | result
id route1 route2 utc stime | l.id l.etime
---|--------|--------|-----|------- |-------|----------|
2 11 22 111 1111 | 1 7777
5 22 33 222 2220 | 3 9999
6 22 33 222 3334 | 4or5 7979or8989
7 22 33 999 9999 | null null
A new table should be created where the result is appended to the last col of my.
Any help is appreciated. Thanks in advance.
This solution is a bit convoluted, but it's a starting point.
First, let's create an auxiliary table:
CREATE TEMP TABLE temp AS
SELECT m.id mid, l.id lid, ABS(l.stime-m.stime) timediff
FROM my m JOIN lookup l
WHERE m.route1 = l.route1 AND m.route2 = l.route2 AND
m.utc = l.utc AND m.id <> l.id;
From this table we can get the minimum timediff for each my.id:
SELECT mid, min(timediff) mtimediff FROM temp GROUP BY mid
Result:
mid mtimediff
---------- ----------
2 0
5 2
6 1
Now we can find which rows in lookup have this stime difference, and choose the smallest id:
SELECT t.mid mid, min(lid) lid
FROM temp t JOIN (
SELECT mid, min(timediff) mtimediff FROM temp GROUP BY mid
) mt ON t.mid = mt.mid AND t.timediff = mt.mtimediff
GROUP BY t.mid
This is the result:
mid lid
---------- ----------
2 1
5 3
6 4
And finally we use those ids to extract the data from the tables:
SELECT m.id, m.route1, m.route2, m.utc, m.stime, l.id, l.etime
FROM my m JOIN lookup l JOIN (
SELECT t.mid mid, min(lid) lid
FROM temp t JOIN (
SELECT mid, min(timediff) mtimediff FROM temp GROUP BY mid
) mt ON t.mid = mt.mid AND t.timediff = mt.mtimediff
GROUP BY t.mid
) ON m.id = mid AND l.id = lid;
Giving:
id route1 route2 utc stime id etime
---------- ---------- ---------- ---------- ---------- ---------- ----------
2 11 22 111 1111 1 7777
5 22 33 222 2220 3 9999
6 22 33 222 3334 4 7979

Select highest 3 scores in each day for every user

I have a MYSQL table like this:
id | userid | score | datestamp |
-----------------------------------------------------
1 | 1 | 5 | 2012-12-06 03:55:16
2 | 2 | 0,5 | 2012-12-06 04:25:21
3 | 1 | 7 | 2012-12-06 04:35:33
4 | 3 | 12 | 2012-12-06 04:55:45
5 | 2 | 22 | 2012-12-06 05:25:11
6 | 1 | 16,5 | 2012-12-06 05:55:21
7 | 1 | 19 | 2012-12-06 13:55:16
8 | 2 | 8,5 | 2012-12-07 06:27:16
9 | 2 | 7,5 | 2012-12-07 08:33:16
10 | 1 | 10 | 2012-12-07 09:25:19
11 | 1 | 6,5 | 2012-12-07 13:33:16
12 | 3 | 6 | 2012-12-07 15:45:44
13 | 2 | 4 | 2012-12-07 16:05:16
14 | 2 | 34 | 2012-12-07 18:33:55
15 | 2 | 22 | 2012-12-07 18:42:11
I would like to display user scores like this:
if a user on a certain day has more than 3 scores it would get only highest 3, repeat that for every day for this user and then add all days together. I want to display this sum for every user.
EDIT:
So in the example above for user 1 on 06.12. I would add top 3 scores together and ignore 4th score, then add to that number top 3 from the next day and so on. I need that number for every user.
EDIT 2:
Expected output is:
userid | score
--------------------
1 | 59 //19 + 16.5 + 7 (06.12.) + 10 + 6.5 (07.12.)
2 | 87 //22 + 0.5 (06.12.) + 34 + 22 + 8.5 (07.12.)
3 | 18 //12 (06.12.) + 6 (07.12.)
I hope this is more clear :)
I would really appreciate the help because I am stuck.
Please take a look at the following code, if your answer to my comment is yes :) Since your data all in 2012, and month of november, I took day.
SQLFIDDLE sample
Query:
select y.id, y.userid, y.score, y.datestamp
from (select id, userid, score, datestamp
from scores
group by day(datestamp)) as y
where (select count(*)
from (select id, userid, score, datestamp
from scores group by day(datestamp)) as x
where y.score >= x.score
and y.userid = x.userid
) =1 -- Top 3rd, 2nd, 1st
order by y.score desc
;
Results:
ID USERID SCORE DATESTAMP
8 2 8.5 December, 07 2012 00:00:00+0000
20 3 6 December, 08 2012 00:00:00+0000
1 1 5 December, 06 2012 00:00:00+0000
Based on your latter updates to question.
If you need some per user by year/month/day and then find highest, you may simply add aggregation function like sum to the above query. I am reapeating myself, since your sample data is for just one year, there's no point group by year or month. That's why I took day.
select y.id, y.userid, y.score, y.datestamp
from (select id, userid, sum(score) as score,
datestamp
from scores
group by userid, day(datestamp)) as y
where (select count(*)
from (select id, userid, sum(score) as score
, datestamp
from scores
group by userid, day(datestamp)) as x
where y.score >= x.score
and y.userid = x.userid
) =1 -- Top 3rd, 2nd, 1st
order by y.score desc
;
Results based on sum:
ID USERID SCORE DATESTAMP
1 1 47.5 December, 06 2012 00:00:00+0000
8 2 16 December, 07 2012 00:00:00+0000
20 3 6 December, 08 2012 00:00:00+0000
UPDATED WITH NEW SOURCE DATA SAMPLE
Simon, please take a look at my own sample. As your data was changing, I used mine.
Here is the reference. I have used pure ansi style without any over partition or dense_rank.
Also note the data I used are getting top 2 not top 3 scores. You can change is accordingly.
Guess what, the answer is 10 times simpler than the first impression your first data gave....
SQLFIDDLE
Query to 1:
-- for top 2 sum by user by each day
SELECT userid, sum(Score), datestamp
FROM scores t1
where 2 >=
(SELECT count(*)
from scores t2
where t1.score <= t2.score
and t1.userid = t2.userid
and day(t1.datestamp) = day(t2.datestamp)
order by t2.score desc)
group by userid, datestamp
;
Results for query 1:
USERID SUM(SCORE) DATESTAMP
1 70 December, 06 2012 00:00:00+0000
1 30 December, 07 2012 00:00:00+0000
2 22 December, 06 2012 00:00:00+0000
2 25 December, 07 2012 00:00:00+0000
3 30 December, 06 2012 00:00:00+0000
3 30 December, 07 2012 00:00:00+0000
Final Query:
-- for all two days top 2 sum by user
SELECT userid, sum(Score)
FROM scores t1
where 2 >=
(SELECT count(*)
from scores t2
where t1.score <= t2.score
and t1.userid = t2.userid
and day(t1.datestamp) = day(t2.datestamp)
order by t2.score desc)
group by userid
;
Final Results:
USERID SUM(SCORE)
1 100
2 47
3 60
Here goes a snapshot of direct calculations of data I used.
SELECT
*
FROM
table1
LEFT JOIN
(SELECT * FROM table1 ORDER BY score LIMIT 3) as lr on DATE(lr.datestamp) = DATE(table1.datastamp)
GROUP BY
datestamp

MySQL : Get max values of groups

I have a table:
user_id | fav_song_genre | votes_as_fav_member
--------+----------------+--------------------
1 | hip hop | 3
2 | hip hop | 5
3 | rock | 8
4 | rock | 6
How do I get only results of user_id's with the highest votes_as_fav_member per group fav_song_genre:
Something like
SELECT *, MAX(votes_as_fav_member) as most_votes
FROM table
GROUP BY
fav_song_genre
I'm using that but it's not giving me the ID's of the members with most votes per genre.
This is not a problem of MySQL, rather a bit of logic problem with your approach.
Let's say we have the following:
user_id | fav_song_genre | votes_as_fav_member
--------+----------------+--------------------
1 | hip hop | 3
2 | hip hop | 5
3 | rock | 8
4 | rock | 6
5 | hip hop | 5
6 | rock | 8
Which ID should the query return? Should it return only one? or all that have the same amount of votes?
So, if you require only a single ID, what is the differentiation of a draw?
Lieven beat me to the SQL resolution by a few seconds, though.
Reasoning goes like
SELECT max vote for each genre
JOIN back with the original table to retrieve the additional columns for the records found.
SQL Statement
SELECT us.*
FROM UserSongs us
INNER JOIN (
SELECT fav_song_genre
, MAX(votes_as_fav_member) AS votes_as_fav_member
FROM UserSongs
GROUP BY
fav_song_genre
) usm ON usm.fav_song_genre = us.fav_song_genre
AND usm.votes_as_fav_member = us.votes_as_fav_member
Edit
How can I make sure the person with the lower ID is returned
SELECT MIN(us.user_id) as user_id
, us.fav_song_genre
, us.votes_as_fav_member
FROM UserSongs us
INNER JOIN (
SELECT fav_song_genre
, MAX(votes_as_fav_member) AS votes_as_fav_member
FROM UserSongs
GROUP BY
fav_song_genre
) usm ON usm.fav_song_genre = us.fav_song_genre
AND usm.votes_as_fav_member = us.votes_as_fav_member
GROUP BY
us.fav_song_genre
, votes_as_fav_member
Not sure if that's what you are asking:
SELECT g.fav_song_genre
, t.user_id
, g.most_votes
FROM yourTable t
JOIN
( SELECT fav_song_genre
, MAX(votes_as_fav_member) as most_votes
FROM yourTable
GROUP BY fav_song_genre
) AS g
ON t.fav_song_genre = g.fav_song_genre
AND t.votes_as_fav_member= g.most_votes
;
I created the table and tested, and I think this will also work
Table Data:
user_id fav_song_genre votes_as_fav_member
1 hip_hop 3
2 hip_hop 5
3 rock 8
4 rock 6
5 blues 20
6 indie 18
7 rock 35
8 country 33
9 hip_hop 35
10 indie 5
11 blues 7
12 hip_hop 59
13 indie 187
14 classic 45
15 country 61
16 hip_hop 243
Query:
select t.user_id, t.fav_song_genre, t.votes_as_fav_member
from (
select user_id, max(votes_as_fav_member) as max_votes, fav_song_genre
from table1 group by fav_song_genre
)
as x inner join table1 as t on t.votes_as_fav_member = x.max_votes and t.fav_song_genre = x.fav_song_genre;
Results:
user_id fav_song_genre votes_as_fav_member
5 blues 20
7 rock 35
13 indie 187
14 classic 45
15 country 61
16 hip_hop 243