calculate rank in highscore from 2 tables - sql

i have a trivia game and i want to reward users for 2 events:
1) answering correctly
2) sending a question to the questions pool
i want to query for score and rank of a specific player and i use this query:
SELECT (correct*10+sent*30) AS score, #rank:=#rank+1 AS rank
FROM ( trivia_players
JOIN ( SELECT COUNT(*) AS sent, senderid
FROM trivia_questions
WHERE senderid='$userid'
) a
ON trivia_players.userid=a.senderid
)
ORDER BY score DESC
and it works if the player is in both tables i.e answered correctly AND sent a question.
but it doesn't work if a player hasn't sent a question
any idea how to fix this query? ($userid is the given parameter)
thanks!
Thanks Tom! only problem is the ranks are not correct:
userid score rank
58217 380 1
12354 80 3
32324 0 2

I would probably do it like this:
SELECT
user_id,
score,
rank
FROM
(
SELECT
TP.user_id,
(TP.correct * 10) + (COUNT(TQ.sender_id) * 30) AS score,
#rank:=#rank + 1 AS rank
FROM
Trivia_Players TP
LEFT OUTER JOIN Trivia_Questions TQ ON
TQ.sender_id = TP.user_id
GROUP BY
TP.user_id,
TP.correct
ORDER BY
score DESC
) AS SQ
WHERE
SQ.user_id = $user_id
I don't use MySQL much, so the syntax may not be perfect. I think that you can use a subquery like this in MySQL. Assuming that MySQL handles COUNT() by only counting rows with a non-null value for , this should work.
The keys are that you do a COUNT over a non-null column from Trivia Questions so that it counts them up by the user and you need to use a subquery so that you can get ranks for everyone BEFORE constraining to a particular user id.

Have you tried using a RIGHT JOIN or LEFT JOIN? Just off the top of my head!

Related

SQL Where NOT IN returns blank

I am trying to get the rows where id are not in a list.
I have email with scores with several email for each score. I know how to get the max score for each email then I want to delete other rows. I try to delete all rows that are not in the list of max score but it does not work.
SELECT *
FROM mkgaction AS a
WHERE id NOT IN (SELECT id
FROM
(SELECT *
FROM mkgaction AS b
ORDER BY `score` DESC) x
GROUP BY `score`)
The select id returns me the right list but the all request returns me no line...
Do you know why?
Presumably -- if your query works at all -- you are using MySQL. The syntax in that database is:
delete ma
from mkgaction ma join
(select ma2.email, max(ma2.score) as max_score
from mkgaction ma2
group by ma2.email
) ma2
on ma.email = ma2.email and ma.score < ma2.max_score;

How to get MAX value out of the GROUPs COUNT

I've recently started to learn tsql beyond basic inserts and selects, I have test database that I train on, and there is one query that I can't really get to work.
There are 3 tables used in that query, in the picture there are simplified fields and relations
I have 2 following queries - first one is simply displaying students and number of marks from each subject. Second is doing almost what I want to achive - shows students and maxiumum amount of marks they got, so ex.
subject1 - (marks) 1, 5, 3, 4 count - 4
subject2 - (marks) 5, 4, 5 - count - 3
Query shows 4 and from what I checked it returns correct results, but I want one more thing - just to show the name of the subject from which there is maximum amount of marks so in the example case - subject1
--Query 1--
SELECT s.Surname, subj.SubjectName, COUNT(m.Mark) as Marks_count
FROM marks m, students s, subjects subj
WHERE m.StudentId = s.StudentNumber and subj.SubjectNumber = m.SubjectId
GROUP BY s.Surname, subj.SubjectName
ORDER BY s.Surname
--Query 2--
SELECT query.Surname, MAX(Marks_count) as Maximum_marks_count FROM (SELECT s.Surname, subj.SubjectNumber, COUNT(m.Mark) as Marks_count
FROM marks m, students s, subjects subj
WHERE marks.StudentId = s.StudentNumber and subj.SubjectNumber = m.SubjectId
GROUP BY s.Surname, subj.SubjectName) as query
GROUP BY query.Surname
ORDER BY query.Surname
--Query 3 - not working as supposed--
SELECT query.Surname, query.SubjectName, MAX(Marks_count) as Maximum_marks_count FROM (SELECT s.Surname, subj.SubjectNumber, COUNT(m.Mark) as Marks_count
FROM marks m, students s, subjects subj
WHERE marks.StudentId = s.StudentNumber and subj.SubjectNumber = m.SubjectId
GROUP BY s.Surname, subj.SubjectName) as query
GROUP BY query.Surname, query.SubjectName
ORDER BY query.Surname
Part of the query 1 result
Part of the query 2 and unfortunately query 3 result
The problem is that when I add to the select statement subject name I got results as from query one - there is no more maximum amount of marks just students, subjects and amount of marks from each subject.
If someone could say what I'm missing, I will much appreciate :)
Here's a query that gets the highest mark per student, put it at the top of your sql file/batch and it will make another "table" you can join to your other tables to get the student name and the subject name:
WITH studentBest as
SELECT * FROM(
SELECT *, ROW_NUMBER() OVER(PARTITION BY studentid ORDER BY mark DESC) rown
FROM marks) a
WHERE rown = 1)
You use it like this (for example)
--the WITH bit goes above this line
SELECT *
FROM
studentBest sb
INNER JOIN
subject s
ON sb.subjectid = s.subjectnumber
Etc
That's also how you should be doing your joins
How does it work? Well.. it establishes an incrementing counter that restarts every time studentid changes (the partition clause) and the numberin goes in des ending mark order (the order by clause). An outer query selects only those rows with 1 in the row number, ie the top mark per student
Why can't I use group by?
You can, but you have to write a query that summarises the marks table into the top mark (max) per student and then you have to join that data back to the mark table to retrieve the subject and all in it's a lot more faff, often less efficient
What if there are two subjects with the same mark?
Use RANK instead of ROW_NUMBER if you want to see both
Edit in response to your comment:
An extension of the above method:
SELECT * FROM
(
SELECT *, ROW_NUMBER() OVER(PARTITION BY su, st ORDER BY c DESC) rn FROM
(
SELECT studentid st, subjectid su, count(*) c
FROM marks
GROUP BY st, su
) a
) b
INNER JOIN student stu on b.st = stu.studentnumber
INNER JOIN subject sub on b.su = sub.subjectnumber
WHERE
b.rn = 1
We count the marks by student/subject, then rownumber them in descending order of count per student-subject pair, then choose only the first row and join in the other wanted data
Ok thanks to Caius Jard, some other Stack's question and a little bit of experiments I managed to write working query, so this is how I did it.
First I created view from query1 and added one more column to it - studentId.
Then I wrote query which almost satisfied me. That question helped me a lot with that task: Question
SELECT marks.Surname,
marks.SubjectName,
marks.Marks_count,
ROW_NUMBER() OVER(PARTITION BY marks.Surname ORDER BY marks.Surname) as RowNum
FROM MarksAmountPerStudentAndSubject marks
INNER JOIN (SELECT MarksAmountPerStudentAndSubject.Id,
MAX(MarksAmountPerStudentAndSubject.Marks_count) as MaxAmount
FROM MarksAmountPerStudentAndSubject
GROUP BY MarksAmountPerStudentAndSubject.Id) m
ON m.Id = marks.Id and marks.Marks_count = m.MaxAmount
It gives following results
That's what I wanted to achieve with one exception - if students have the same amount of marks from multiple subjects it displays all of them - thats fine but I decided to restrict this to the first result for each student - I couldn't just simply put TOP(1)
there so I used similar solution that Caius Jard showed - ROW_NUMBER and window function - it gave me a chance to choose records that has row number equals to 1.
I created another view from this query and I could simply write the final one
SELECT marks.Surname, marks.SubjectName, marks.Marks_count
FROM StudentsMaxMarksAmount marks
WHERE marks.RowNum = 1
ORDER BY marks.Surname
With result

How to use rounded value for join in SQL

i'm just learning SQL today and i never thought how fun it's until i'm fiddling with it.
I got a problem and i need a help.
i have 2 tables, Customer and Rate, with details stated below
Customer
idcustomer = int
namecustomer = varchar
rate = decimal(3,0)
with value as described:
idcustomer---namecustomer---rate
1---JOHN DOE---100
2---MARY JANE---90
3---CLIVE BAKER---12
4---DANIEL REYES---47
Rate
rate = decimal(3,0)
description = varchar(40)
with value as described:
rate---description
10---G Rank
20---F Rank
30---E Rank
40---D Rank
50---C Rank
60---B Rank
70---A Rank
80---S Rank
90---SS Rank
100---SSS Rank
Then i ran query below in order to round all values in customer.rate field then inner join it with rate table.
SELECT *, round(rate,-1) as roundedrate
FROM customer INNER JOIN rate ON customer.roundedrate = rate.rate
It didn't produce this result:
idcustomer---namecustomer---rate---roundedrate---description
1---JOHN DOE---100---100---SSS Rank
2---MARY JANE---90---90---SS Rank
3---CLIVE BAKER---12---10---G Rank
4---DANIEL REYES---47---50---C Rank
Is there anything wrong with my code ?
Your query should produce an 'ambigious column' error because you're not specifying a table name when referring to rate (in round(rate,-1)), which exists in both tables.
Also, the where part of a sql query is executed before the select part, so you can't refer to the alias customer.roundedrate in your where statement.
Try this instead
SELECT *, round(customer.rate,-1) as roundedrate
FROM customer INNER JOIN rate ON round(customer.rate,-1) = rate.rate
http://sqlfiddle.com/#!9/e94a60/2
I would suggest a correlated subquery for this:
select c.*,
(select r.description
from rate r
where r.rate <= c.rate
order by r.rate desc
fetch first 1 row only
) as description
from customer c;
Note: fetch first 1 row only is ANSI standard SQL, which some databases do not support. MySQL uses limit. Older versions of SQL Server use select top 1 instead.

Select records from joint tables

Got two tables:
tblJumper
JumperID
JumperName
and
tblWidthScored
ScoreID
fkJumperID
fkScoredWidth
Tables related by tblWidthScored.fkJumperID = tblJumper.JumperID
Table Contains following data:
tblJumper
1 Tom
2 Jerry
3 Bugs
tblWidthScored
1 1 5,72m
2 2 6,13m
3 1 5,80m
4 3 6,40m
5 2 6,30m
6 3 6,20m
What I'm trying to get is a list of each Jumpers personla best:
Tom 5,80m
Jerry 6,30m
Bugs 6,40m
Tried SELECT DISTINCT... in various forms but didn't succeed in any way.
Anyone couldl give a hint, please?
Thanks!
Although I think this is just a Join question and appropriate articles could be found on the web, I'll post one of the answers...
SELECT
J.JumperName,
S.Score
FROM tblJumper as J
INNER JOIN(
SELECT
fkJumperID,
Max(fkScoredWidth) as Score
FROM tblWidthScored
GROUP BY
fkJumperID
) as S on J.JumperID = S.fkJumperID
The hints you're asking for are in your actual question actually:
You're trying to find personal bests! That means some kind of aggregation: Maximum.
You're trying to find personal bests distinctly. Not "the best score amongst whole jumpers" but every person's personal best distinctly. That means some kind of distinction or grouping that you can partition the score datas people by people: Group By Clause
you can try this query. for this type of query, you have to use Group BY Clause with Sub-query.
SELECT JumperName, innertblWidthScored
FROM tblJumper INNER JOIN(SELECT fkJumperID,
Max(fkScoredWidth) as innertblWidthScored
FROM tblWidthScored
GROUP BY fkJumperID) WidthScored
on tblJumper.JumperID = WidthScored.fkJumperID
SQL FIDDLE DEMO
You could as well start with the JOIN, and do the GROUP BY afterwards. And if your sample output does actually suggest a sort order, you need an ORDER BY, too.
SELECT
JumperName
, MAX(fkScoredWidth) PersonalBest
FROM tblJumper
JOIN tblWidthScored
ON JumperID = fkJumperID
GROUP BY JumperName
ORDER BY MAX(fkScoredWidth) DESC;
SQL Fiddle
From result it seems that you want to retrieve jumper with its best score
try this -
select
a.jumperName,
b.bestScore
from tblJumper a, (
select
max(fkScoredWidth) as bestScore,
fkJumperID
from tblWidthScored
group by
fkJumperID
) b on b.fkJumperID = a.JumperID
order by
a.JumperID

how to create this query

how to create a query if i need to include two aggregate function in select row and per each function i need different group by and where conditions
in my example i need to returns the playerName, and how many the player win the this can be checked if the results in table game result= first, and how many times he played
but do not know how to deal with two aggregate functions .
simply i want to join the result of this two queries
1.
select playeName,count(*)
from player,game
where player.playerId=game.playerId and result="first"
group by game.playerId
2.
select count(*)
from game, player
where game.playerId=player.playerId
group by game.playerId
the set of attributes for table game are
playerId , result
the set of attributes for table player are
playerName,playerId
any idea???
Use:
SELECT p.playername,
SUM(CASE WHEN g.result = 'first' THEN 1 ELSE 0 END),
COUNT(*)
FROM PLAYER p
JOIN GAME g ON g.playerid = p.playerid
GROUP BY p.playername
Along with solutions proposed by OMG Ponies and Bnjmn, you can also get desired results by using WITH ROLLUP
select result, count(*)
from game, player
where game.playerId=player.playerId
group by game.playerId, result WITH ROLLUP
Then, on client side, find records with result equals 'first' and and result is null(which is #games played).