table league
team_id name wins losses played recorded created
1 dodgers 10 4 14 1364790000 1353215830
2 angels 9 6 15 1364790000 1353661376
3 pirates 12 3 15 1364790000 1353543466
team_id name wins losses played recorded created
1 dodgers 22 9 31 1367274480 1353215830
2 angels 14 17 31 1367274480 1353661376
3 pirates 19 13 32 1367274480 1353543466
4 yankees 10 9 19 1367274480 1365577298
5 brewers 7 11 18 1367274480 1365394448
Would like Results as:
team_id name wins losses played
1 dodgers 12 5 17
2 angels 5 11 16
3 pirates 7 10 17
4 yankees 10 9 19
5 brewers 7 11 18
I've tried several queries with joins and have had no success. Every day the team, wins, lossed and played are captured and time stamped on the recorded column. The team was created on the created column. (All unix timestamps) There are several rows in between the 2 dates I'm trying for, but I don't need them for this query.
What I wanted to do was to get April's Won/Loss/Played for existing and new teams, I tried several queries, here are a couple that did not give me the desired results:
SELECT a.name as name, a.wins-b.wins as wins, a.losses-b.losses as losses, a.played-b.played as played from league a join league b on a.id=b.id where a.recorded= 1367274480 and b.recorded= 1364790000
and
SELECT new.*, new.wins-old.wins as newwins, new.losses-old.losses as newlosses FROM league new LEFT JOIN league old ON new.id=old.id WHERE (new.recorded=1367274480 and old.recorded=1364790000) or (new.created > 1364790000 and new.recorded=1367274480) GROUP BY new.id
You want every row for the later records and the same number of rows for the earlier, so you need to use LEFT JOIN to get NULL's for teams created between two dates, but WHERE recorded condition for the smaller table should be moved as ON condition for the join.
Also keep in mind that 2-NULL = NULL, so you need to change NULL's into 0's with coalesce().
SELECT a.name AS name,
a.wins - COALESCE( b.wins, 0 ) AS wins,
a.losses - COALESCE( b.losses, 0 ) AS losses,
a.played - COALESCE( b.played, 0 ) AS played
FROM league a LEFT JOIN league b
ON a.team_id = b.team_id AND b.recorded =1364790000
WHERE a.recorded =1367274480
The way the data is set up, it seems that you don't have a recorded value on each day. However, each column would be increasing, so you can take the difference between the max and min vals for the month.
Try this:
SELECT l.name as name,
max(l.wins)-min(l.wins) as wins,
max(l.losses)-min(l.losses) as losses,
max(l.played)-min(l.played) as played
from league l
where l.recorded <= 1367274480 and l.recorded >= 1364790000
group by l.name
Related
I'm trying to find admin activity within the last 30 days.
The accounts table stores the user data (username, password, etc.)
At the end of each day, if a user had logged in, it will create a new entry in the player_history table with their updated data. This is so we can track progress over time.
accounts table:
id
username
admin
1
Michael
4
2
Steve
3
3
Louise
3
4
Joe
0
5
Amy
1
player_history table:
id
user_id
created_at
playtime
0
1
2021-04-03
10
1
2
2021-04-04
10
2
3
2021-04-05
15
3
4
2021-04-10
20
4
5
2021-04-11
20
5
1
2021-05-12
40
6
2
2021-05-13
55
7
3
2021-05-17
65
8
4
2021-05-19
75
9
5
2021-05-23
30
10
1
2021-06-01
60
11
2
2021-06-02
65
12
3
2021-06-02
67
13
4
2021-06-03
90
The following query
SELECT a.`username`, SEC_TO_TIME((MAX(h.`playtime`) - MIN(h.`playtime`))*60) as 'time' FROM `player_history` h, `accounts` a WHERE h.`created_at` > '2021-05-06' AND h.`user_id` = a.`id` AND a.`admin` > 0 GROUP BY h.`user_id`
Outputs this table:
Note that this is just admin activity, so Joe is not included in this data.
from 2021-05-06 to present (yy-mm-dd):
username
time
Michael
00:20:00
Steve
00:10:00
Louise
00:02:00
Amy
00:00:00
As you can see this from data, Amy's time is shown as 0 although she has played for 10 minutes in the last month. This is because she only has 1 entry starting from 2021-05-06 so there is no data to compare to. It is 0 because 10-10 = 0.
Another flaw is that it doesn't include all activity in the last month, basically only subtracts the highest value from the lowest.
So I tried fixing this by comparing the highest value after 2021-05-06 to their most previous login before the date. So I modified the query a bit:
SELECT a.`Username`, SEC_TO_TIME((MAX(h.`playtime`) - (SELECT MAX(`playtime`) FROM `player_history` WHERE a.`id` = `user_id` AND `created_at` < '2021-05-06'))*60) as 'Time' FROM `player_history` h, `accounts` a WHERE h.`created_at` >= '2021-05-06' AND h.`user_id` = a.`id` AND a.`admin` > 0 GROUP BY h.`user_id`
So now it will output:
username
time
Michael
00:50:00
Steve
00:50:00
Louise
00:52:00
Amy
00:10:00
But I feel like this whole query is quite inefficient. Is there a better way to do this?
I think you want lag():
SELECT a.username,
SEC_TO_TIME(SUM(h.playtime - COALESCE(h.prev_playtime, 0))) as time
FROM accounts a JOIN
(SELECT h.*,
LAG(playtime) OVER (PARTITION BY u.user_id ORDER BY h.created_at) as prev_playtime
FROM player_history h
) h
ON h.user_id = a.id
WHERE h.created_at > '2021-05-06' AND
a.admin > 0
GROUP BY a.username;
In addition to the LAG() logic, note the other changes to the query:
The use of proper, explicit, standard, readable JOIN syntax.
The use of consistent columns for the SELECT and GROUP BY.
The removal of single quotes around the column alias.
The removal of backticks; they just clutter the query, making it harder to write and to read.
I have a table
Team Matches Won
A 10 2
B 20 4
C 40 10
I want to convert this table into following
Team Won/Lost Won Lost Number
A Won 2
A Lost 8
B Won 4
B Lost 16
C Won 10
C Lost 30
Thanks in advance !
In fact, there is no aggregation here - just a simple subtraction. I'd do this in two queries, one for wins and one for loses, and combine them with the union all operator:
SELECT team, 'Won' AS "Won/Lost", won AS "Won/Lost Number"
FROM my_table
UNION ALL
SELECT team, 'Lost', matches - won
FROM my_table
ORDER BY 1 ASC, 2 DESC
I am running the following query to understand to get users' first attempt to answer a question listed next to their second attempt.
SELECT
s.id AS attempt_id_first, m.id AS attempt_id_second, s.user_id
FROM
attempt s
INNER JOIN attempt m on s.user_id = m.user_id
WHERE
s.id<m.id
I end up with this:
attempt_first attempt_second user_id
7 17 1
9 10 2
9 15 2
10 15 2
4 6 9
24 25 15
29 34 19
29 36 19
34 36 19
I would like to have a new column that counts the number of attempts by users so that:
7 17 1 1
9 10 2 3
9 15 2 3
10 15 2 3
4 6 9 1
24 25 15 1
29 34 19 3
29 36 19 3
34 36 19 3
I am sure this is trivial, but I cannot get it to work. Help anyone?
I think this is it: Just display the results, and throw in an extra count subquery:
select
userid,
id,
(select
count('x')
from
attempt x
where
x.userid = a.userid) as attempcount
from
attempt a
If you like to keep the first and second attempt in separate columns, you can of course embed the subselect in your original query.
It seems wrong, though. Firstly, you need to have at least two attemps, otherwise none will show. You can solve that by changing inner join to left join and move the condition in the where clause to that join. Secondly, the 'second attempt' is not the second attempt per say. Actually, for each of the attempts you get all next attempts. Look at the example of user 2. You accidentally get three rows (where there are three attemps), but you get attempt 9 and 10, as well as attempt 9 and 15 as well as 10 and 15. 9, 15 is incorrect, since 15 isn't the attempt that followed 9. The more attempts a user has, the more of these false results you will get.
If you want one attempt listed next to the next one, with the count, I would suggest:
SELECT s.user_id, s.id AS attempt_id_first,
(select s2.id
from attempt s2
where s2.user_id = s.user_id and
s2.id > s.id
order by s2.id
limit 1
) as attempt_id_second,
(select count(*)
from attempt s3
where s3.user_id = s.user_id
) as totalAttempts
FROM attempt s ;
This only lists each attempt once with the next one. The count is included as the last column.
Fixture Table
uid home_uid away_uid winner date season_division_uid
1 26 6 6 2013-07-30 18
2 8 21 8 2013-06-30 18
3 6 8 8 2013-06-29 18
4 21 26 21 2013-05-20 18
5 6 26 6 2013-04-19 18
This table contains hundreds of rows.
Currently I have a query to select all the teams in a division, i.e.
SELECT team_uid
FROM Season_Division_Team
WHERE season_division_uid='18'
which lists the rows of team uid's i.e. [6,26,8,21,26].
Now for each of the unique team ids, I would like to return the last 3 winner values, ordered by the date column, that they were involved in (they could be an away_uid or home_uid).
So the returned value example would be:
team_id winner date
6 6 2013-07-30
6 8 2013-06-29
6 26 2013-04-19
26 6 2013-07-30
26 21 2013-05-20
26 6 2013-04-19
Any ideas? Thank you
Im not sure how to get it direct, a query like
select * from Season_division_Team where
`date >= (select min(`date`) from
(select `date` from season_division_team order by date desc limit 3))
and (home_uid = 6 or away_uid = 6)
Thats not going to be a good query. But only way i can think of currently
Its hard to get the 3rd largest value from SQL Example
the sub query is trying to get the date where the last win occured, and then getting all dates after that where the team played.
EDIT:
SELECT * FROM Season_Division_Team WHERE winner = 6 ORDER BY `date` DESC LIMIT 3
that sounds more like your latter comment
I'm working on a baseball related website. I have a table with a batting lineup for two baseball teams:
+----+----------+--------------+--------+
| id | playerId | battingOrder | active |
+----+----------+--------------+--------+
Batting order is an integer between 1 and 20. This corresponds to the following logic:
Batting Order 1-9 — Away Team Lineup
Batting Order 10 — Away Team Pitcher
Batting Order 11-19 — Home Team Lineup
Batting Order 20 — Home Team Pitcher
The active field is a tinyint 0 or 1, representing the pitcher on the mound and the batter on the plate.
Known Fact: There will always be one active pitcher from one team and one active batter from the opposite team.
I need to write a query that returns a row for a home team player that corresponds to the next batter in the battingOrder. (the one that that occurs after the active batter's battingOrder)
Example:
If the player in battingOrder 13 is active, the query should return the player in batting order 14.
If the player in battingOrder 19 is active, the query should return the player in batting order 11 (the lineup loops back to the first player for the team).
I've never used a CASE query before, but I came up with the following:
SELECT *
FROM lineups
WHERE battingOrder =
CASE (
SELECT battingOrder
FROM lineups
WHERE battingOrder > 10 AND active = 1
LIMIT 1
)
WHEN 11 THEN 12
WHEN 12 THEN 13
WHEN 13 THEN 14
WHEN 14 THEN 15
WHEN 15 THEN 16
WHEN 16 THEN 17
WHEN 17 THEN 18
WHEN 18 THEN 19
WHEN 19 THEN 11
END
LIMIT 1;
It seems to work, but what edge cases and/or pitfalls have I walked into? Is this efficient? I'm particulary interested in a solution to my problem that does not use a nested query.
Select LNext.player As NextPlayer
From lineups As L
Left Join lineups As LNext
On LNext.BattingOrder Between 11 And 20
And LNext.BattingOrder = Case
When L.BattingOrder = 19 Then 11
Else L.BattingOrder + 1
End
Where L.battingOrder Between 11 And 20
And L.active = 1
In fact, you could make it handle both home and away like so:
Select LNext.player As NextPlayer
From lineups As L
Left Join lineups As LNext
On LNext.BattingOrder = Case
When L.BattingOrder = 19 Then 11
When L.BattingOrder = 9 Then 1
Else L.BattingOrder + 1
End
Where L.active = 1