Hi there I have a question regarding SQL structure. I have some of it figured out but I cant get the rest.
USERS TABLE
user OS_signup
1 Mobile
2 Windows
3 Mac
4 Mobile
5 Windows
DUNGEON TABLE
user dungeon progress status OS_completed deaths
1 Dungeon 1 1 finished Windows 0
1 Dungeon 1 2 incomplete Windows 0
2 Dungeon 2 1 finished Windows 1
3 Dungeon 2 1 finished Mac 1
3 Dungeon 1 2 finished Mac 3
3 Dungeon 3 3 incomplete Mac 0
4 Dungeon 1 1 finished Mobile 1
5 Dungeon 1 1 incomplete Windows 5
Which operating system is most popular when comparing the percent of users that completed the first level of progress of the first dungeon vs. users who have an account on the game?
I have this query
SELECT OS_signup, count(*) AS count_total
FROM users
GROUP BY OS_signup
and this one
SELECT OS_completed, count(*) AS count_completed
FROM dungeons
WHERE dungeon = "Dungeon 1" AND progress >=1 AND status = "finished"
GROUP BY OS_completed
I obviously get two tables, but I want to write a query that outputs a combined table so that I get one table that looks like this:
OS percent-done
Mobile 0.5
Windows 0.5
Mac 1
How can I write a query to achieve a table like the above?
Thank you!
for MySQL it could be something like this:
SELECT OS_signup, count(*) AS count_total, COUNT(tmp.user_id) as count_completed, COUNT(tmp.user_id) / COUNT(*) as percent
FROM users
LEFT JOIN (
SELECT user_id
FROM dungeons
WHERE dungeon = 'Dungeon 1' AND progress >= 1 AND status = 'finished'
GROUP BY user_id
) tmp ON tmp.user_id = users.id
GROUP BY OS_signup
Just join the results of these two queries together:
WITH total(OS, count_total) AS (
SELECT OS_signup, count(*)
FROM users
GROUP BY OS_signup
),
completed(OS, count_completed) AS (
SELECT OS_completed, count(*)
FROM dungeons
WHERE dungeon = 'Dungeon 1'
AND progress >= 1
AND status = 'finished'
GROUP BY OS_completed
)
SELECT OS,
count_total,
count_completed,
CAST(count_completed AS FLOAT) / count_total AS percent
FROM total
JOIN completed USING (OS)
ORDER BY ...;
(MySQL does not have common table expressions; you'd have to use views or subqueries instead.)
Related
People travel in pairs. How to find the maximum and minimum number of days between trips every user?
People:
id
user
1
Harry
2
George
3
Thomas
4
Jacob
5
Jack
6
Oliver
Travels:
id
date
user1
user2
1
2005-10-03
2
3
2
2005-10-04
1
4
3
2005-10-05
5
6
4
2005-10-06
1
3
5
2005-10-07
2
4
6
2005-10-08
3
5
7
2005-10-10
1
4
8
2005-10-11
5
2
9
2005-10-15
6
4
I tried to solve this problem in the following way, but I still do not understand how to solve this problem:
select People.id,People.user, count(*)
from People
INNER join
(SELECT MIN(TIMESTAMPDIFF(day, t1.date, t2.date)) as mintime,max(TIMESTAMPDIFF(day, t1.date, t2.date))
from Travels as t1
join Travels as t2 on t1.PERSON_1 = t2.PERSON_1
WHERE t1.date< t2.date
GROUP BY t1.PERSON_1) as t3
group by People.id
There is an idea to use the position function to iterate over each user, and then, as a result, look at the dates and find the minimum and maximum, but I still don't understand how to do this
Best is to do it in steps with subqueries, as below (comments are in the query):
select user, max(dateDiff)
from (
select
user,
-- get the diff between previous and current row date to get diff between trips
datediff(date, lag(date) over (partition by user order by date)) dateDiff
from (
-- full flat list of all users and trip dates
select date, user1 `user`
from testtbl
union all
select date, user2
from testtbl
) a
) a group by user
SQL fiddle
Note that I used windowed function which are not avaiable in MySql 5 and below.
I have a "library" history table (Excel like) to see where a book was or is.
id
date
book
action
client
1
2020-12-01
1
toClient
1
2
2020-12-02
1
returned
1
3
2020-12-03
1
toClient
2
4
2020-12-04
2
toClient
2
5
2020-12-05
3
toClient
1
6
2020-12-06
3
returned
1
7
2020-12-07
2
returned
2
8
2020-12-08
2
toClient
1
I am trying to discover how many books and what books are by client 2 (for example).
I have something like that to find everything about the books that are or was by client 2:
SELECT * FROM history WHERE book IN
(SELECT book FROM history WHERE action='toClient' AND client=2 GROUP BY book)
But so, I get also books that aren't anymore on client 2.
Is there a simple way to query items where the last action was "toClient" and not "returned" and the current client is client 2?
Edit: forgot to write that action can be also revision, timeExtension or something other.
To list books that are currently out, you can do:
select h.*
from history h
where
action = 'toClient'
and h.date = (select max(d1.date) from history h1 where h1.book = h.book)
The idea is to filter on the latest event date per book. Then we only retain the row if the corresponding event is "toClient".
This returns the results for all clients. If you want just one client, just add an additional condition to the where clause of the query:
and client = 2
You can also do this with window functions:
select *
from (
select h.*,
rank() over(partition by book order by date desc) rn
from history h
) h
where rn = 1 and action = 'toClient' -- and client = 2
I have a table with winner and loser statistics from a game:
id winner_id loser_id
1 1 2
2 1 2
3 3 4
4 4 3
5 1 2
6 2 1
7 3 4
8 3 2
9 3 5
10 3 6
11 2 3
12 3 6
13 2 3
I want a result table where i can find the highest winning streak of every player in the game. A streak of a player is broken, when he lost a game (player_id = loser_id). It should look like:
player_id win_streak
1 3
2 2
3 4
4 1
5 0
6 0
I tried many queries with user defined variables etc. but i can't find a solution. Thanks!
SQL Fiddle : http://sqlfiddle.com/#!9/3da5f/1
Is this the same as Alex's approach; I'm not quite sure, except that it seems to have one distinct advantage.... ;-)
SELECT player_id, MAX(CASE WHEN result = 'winner' THEN running ELSE 0 END) streak
FROM
( SELECT *
, IF(player_id = #prev_player,IF(result=#prev_result,#i:=#i+1,#i:=1),#i:=1) running
, #prev_result := result
, #prev_player:=player_id
FROM
( SELECT id, 'winner' result, winner_id player_id FROM my_table
UNION
SELECT id, 'loser', loser_id FROM my_table
) x
,
( SELECT #i:=1,#prev_result = '',#prev_player:='' ) vars
ORDER
BY x.player_id
, x.id
) a
GROUP
BY player_id;
I guess you should better to do that on php (or any other language you use) side.
But just to give you some idea and as experiment and example for some unique cases (hope it could be useful somewhere)
Here is my approach:
http://sqlfiddle.com/#!9/57cc65/1
SELECT r.winner_id,
(SELECT MAX(IF(winner_id=r.winner_id,IF(#i IS NULL, #i:=1,#i:=#i+1), IF(loser_id = r.winner_id, #i:=0,0)))
FROM Results r1
WHERE r1.winner_id = r.winner_id
OR r1.loser_id = r.winner_id
GROUP BY IF(winner_id=r.winner_id, winner_id,loser_id)) win_streak
FROM ( SELECT winner_id
FROM Results
GROUP BY winner_id
) r
It returns not all ids now but only who had ever win. So to make it better, probably you have user table. If so it would simplify a query. If you have no user table you need to union all somehow users who had never win.
You are welcome if any questions.
I am aggregating data and I cannot sum certain columns so I would like to take the most frequent observation from that column, or the mode value. Each ID can have only one site and number, so if there are ties then pick the smaller of the two numbers.
Example follows:
ID site number
1 3 45
1 3 45
1 2 56
1 3 56
2 4 5
2 5 5
2 5 3
2 5 5
I want it to look like:
ID site number
1 3 45
2 5 5
Here's one way of doing it:
with aggregation as
(
select id
, site
, number
, numberCount = count(1)
from SiteNumbers
group by id
, site
, number
), aggregateRanks as
(
select *
, idRank = row_number() over (partition by id order by numberCount desc, number, site)
from aggregation
)
select id
, site
, number
from aggregateRanks
where idRank = 1
SQL Fiddle with demo.
It matches your results, but depending on all your different cases might need some tweaking; hopefully it gives you some ideas.
I need a little help with a MySQL query.
I have two tables one table is a list of backlinks with a is_homepage (bool) flag. The second table is a list of the domains for all of the backlinks, a was link_found (bool) flag, and a url_count column which is the number of rows in the backlinks table that are associated with each domain.
Note that the domain_id column is the foreign key to the domain table id column. Heres some sample data.
backlinks
id domain_id is_homepage page_href
1 1 1 http://ablog.wordpress.com/
2 1 0 http://ablog.wordpress.com/contact/
3 1 0 http://ablog.wordpress.com/archives/
4 2 1 http://www.somewhere.org/
5 2 0 http://www.somewhere.org/page=3
6 3 1 http://www.great-fun-site.com/
7 3 0 http://www.great-fun-site.com/index.html
8 4 0 http://red.blgspot.com/page=7
9 4 0 http://blue.blgspot.com/page=9
domains
id url_count link_found domain_name
1 3 1 wordpress.com
2 2 0 somewhere.org
3 2 1 great-fun-site.com
4 2 1 blgspot.com
The results Im looking to get from the above data would be: count = 2, total = 5.
Im trying to get the count of rows from the domains table (count) and then the sum of the url_count (total) from the domains table WHERE link_found is 1 and where one of the links in the backlink table is_homepage is 1.
Here's the query I'm trying to work with.
SELECT SUM(1) AS count, SUM(`url_count`) total
FROM `domains` AS domain
LEFT JOIN `backlinks` AS link ON link.domain_id = domain.id
WHERE domain.id IN (
SELECT DISTINCT(bl.domain_id)
FROM `backlinks` AS bl
WHERE bl.tablekey_id = 11
AND bl.is_homepage = 1
)
AND domain.link_found = 1
AND link.is_homepage = 1
GROUP BY `domain`.`id`
The problem with this query is that it returns a row for each entry in the domains table. I think I might need one more sub query to add up the returned results but I'm not sure if that's correct. Does anyone see what I'm doing wrong? Thank you!
EDIT:
The problem I'm having is that if there are more than one homepage in the back-links table then its counted multiple times. I need to only count each domain once.
Well, you shouldn't have to do a group by as you are not selecting anything other than aggregated fields. I'm no mysql expert, but this should work:
SELECT count(d.id) as count, sum(d.url_count) as total from domains as d
inner join backlinks as b
on b.domain_id = d.id
Where d.Link_found = 1 and b. is_homepage = 1
The reason you're getting a row for each entry in the domains table is that you're grouping by domain.id. If you want grand totals only, just leave off the GROUP BY piece.
I think a fairly simple query will do the trick:
SELECT COUNT(*), SUM(domains.URL_Count)
FROM domains
WHERE domains.link_found = 1 AND domains.id IN (
SELECT domain_id FROM backlinks WHERE is_homepage = 1)
There's a working SQLFiddle here.
Thanks for the help. Sorry it was so hard to explain I need a MySQL fiddle :)
If anyones interested heres what I ened up with:
SELECT SUM(1) AS count, SUM(total) AS total
FROM
(
SELECT SUM(`url_count`) total
FROM `domains` AS domain
LEFT JOIN `backlinks` AS link ON link.domain_id = domain.id
WHERE domain.id IN (
SELECT DISTINCT(bl.domain_id)
FROM `backlinks` AS bl
WHERE bl.tablekey_id = 11
AND bl.is_homepage = 1
)
AND domain.link_found = 1
AND link.is_homepage = 1
GROUP BY `domain`.`id`
) AS result