I have a DB where I have stored a table with user information, a table with test (answers and points) and a table with user's answers for each question. Each question is worth in total 1 point and could have one or more correct answers. If all of the answers are correct, and the user check just one, he will receive just 0.25 points.
I want to make a query to check total points for each user, but I don't find a good method.
User table:
+--------+------------+-----------+-------------------+------------+--------+
| userID | first_name | last_name | email | password | points |
+--------+------------+-----------+-------------------+------------+--------+
| 1 | Jhon | Jhonny | jhon#yahoo.com | secretPass | 0 |
| 2 | Dan | Dan | dan#yahoo.com | 1234 | 0 |
| 3 | Dick | Pop | dd#yahoo.com | 123456 | 0 |
| 4 | Mihaela | Micky | mihaela#yahoo.com | pass12 | 0 |
+--------+------------+-----------+-------------------+------------+--------+
Question table:
(1 means that answer is good - we can have multiple correct answers)
+------------+--------------------------------------------------+---+---+---+---+
| questionID | question | a | b | c | d |
+------------+--------------------------------------------------+---+---+---+---+
| 1 | which of these are colors? | 1 | 0 | 0 | 1 |
| 2 | which of these are fruits? | 1 | 1 | 1 | 0 |
| 3 | which of these are programming language? | 0 | 1 | 1 | 0 |
| 4 | What is IPv6? | 0 | 0 | 0 | 1 |
+------------+--------------------------------------------------+---+---+---+---+
User's answer table:(1 means the user choose that answer but may be inccorect )
+------------+--------+---+---+---+---+
| questionID | userID | a | b | c | d |
+------------+--------+---+---+---+---+
| 1 | 1 | 1 | 1 | 0 | 0 |
| 2 | 1 | 1 | 1 | 1 | 0 |
| 1 | 3 | 1 | 0 | 1 | 1 |
| 1 | 4 | 1 | 1 | 1 | 0 |
| 3 | 1 | 1 | 0 | 1 | 1 |
| 4 | 1 | 1 | 0 | 1 | 1 |
| 1 | 2 | 1 | 1 | 0 | 0 |
| 2 | 2 | 0 | 1 | 0 | 1 |
| 3 | 2 | 0 | 1 | 1 | 1 |
| 4 | 2 | 1 | 1 | 0 | 1 |
| 2 | 3 | 1 | 0 | 0 | 1 |
| 3 | 3 | 1 | 0 | 1 | 1 |
| 4 | 3 | 1 | 0 | 1 | 1 |
| 2 | 4 | 0 | 1 | 1 | 1 |
| 3 | 4 | 1 | 0 | 0 | 1 |
| 4 | 4 | 0 | 0 | 1 | 0 |
+------------+--------+---+---+---+---+
Try this
SELECT
a.*,
u.name,
q.*,
# (a.a & q.a) + (a.b & q.b) + (a.c & q.c) + (a.d & q.d) userCorrects,
# (a.a + a.b + a.c + a.d) questionCorrects,
((a.a & q.a) + (a.b & q.b) + (a.c & q.c) + (a.d & q.d)) / (a.a + a.b + a.c + a.d) as userGrade
FROM
answer a
INNER JOIN
user u ON a.userID = u.id
INNER JOIN
question q ON a.questionID = q.id
You'd join questions and answers and add up correct answers. The formula per answer is simply: number of correct answers divided by 5. You'd GROUP BY user ID and SUM to get results per user.
select u.userid, u.first_name, u.last_name, counted.points
from
(
select
a.userid,
sum(((a.a = q.a) + (a.b = q.b) + (a.c = q.c) + (a.d = q.d) + (a.e = q.e)) / 5)
as points
from question q
join answer a on a.questionid = q.questionid
group by a.userid
) counted
join users u on u.userid = counted.userid;
In MySQL true = 1 and false = 0, so (a.a = q.a) is 1 when correct and 0 when wrong.
Nice question...!!!
I think this query will help you with all possible cases that may occur in this scenario.
SELECT
u.userID, u.first_name, u.last_name, u.email,
SUM(1 - ((!(answer.a = question.a)) + (!(answer.b = question.b)) + (!(answer.c = question.c)) + (!(answer.d = question.d))) * 0.25) AS Score
FROM answer
INNER JOIN
user u ON answer.userID = u.userID
INNER JOIN
question ON answer.questionID = question.questionID
GROUP BY answer.userID
Related
I need a help of a sql expert. I have a problem with my sql query. I have six MySQL tables:
pcs_persons (table of players)
+----+------------+------------+
| id | firstname | lastname |
+----+------------+------------+
| 1 | John | McClane |
| 2 | Jack | Marriott |
| 3 | Billy | Bravo |
| 4 | Archie | MacDonald |
+----+------------+------------+
pcs_matchs (table of match results)
+----+-------------------+-------------------+---------+------------+------------+
| id | id_candidate_dom | id_candidate_ext | id_day | id_season | id_compet |
+----+-------------------+-------------------+---------+------------+------------+
| 1 | 1 | 2 | 1 | 1 | 1 |
| 2 | 3 | 4 | 1 | 1 | 1 |
| 3 | 2 | 3 | 2 | 1 | 1 |
| 4 | 4 | 1 | 2 | 1 | 1 |
| 5 | 1 | 7 | 1 | 2 | 3 |
| 6 | 6 | 3 | 2 | 2 | 5 |
+----+-------------------+-------------------+---------+------------+------------+
pcs_lineup (table of those players who were selected to the match squad as starter - type 2, or as substitute - type 3)
+----+-----------+----------+------------+-------+
| id | id_match | id_club | id_person | type |
+----+-----------+----------+------------+-------+
| 1 | 1 | 1 | 1 | 2 |
| 2 | 1 | 1 | 2 | 3 |
| 3 | 1 | 2 | 3 | 2 |
| 4 | 1 | 2 | 4 | 3 |
+----+-----------+----------+------------+-------+
pcs_goals (table of scored goals by players)
| id | id_match | id_person | id_club | goal_min |
+----+-----------+------------+----------+-----------+
| 1 | 1 | 1 | 1 | 23 |
| 2 | 1 | 1 | 1 | 48 |
| 3 | 1 | 3 | 2 | 56 |
| 4 | 1 | 4 | 2 | 89 |
+----+-----------+------------+----------+-----------+
pcs_cards (table of received cards by players)
| id | id_match | id_person | id_club | card_min | card_yellow | card_red |
+----+-----------+------------+----------+-----------+--------------+-----------+
| 1 | 1 | 1 | 1 | 12 | 1 | |
| 2 | 1 | 1 | 1 | 43 | 1 | |
| 3 | 1 | 3 | 2 | 78 | | 1 |
| 4 | 1 | 4 | 2 | 91 | 1 | |
+----+-----------+------------+----------+-----------+--------------+-----------+
pcs_subs (table of substitutions)
| id | id_match | id_club | id_person_in | id_person_out | subs_min |
+----+-----------+----------+---------------+----------------+-----------+
| 1 | 1 | 1 | 7 | 1 | 82 |
| 2 | 1 | 1 | 8 | 2 | 85 |
| 3 | 1 | 2 | 5 | 3 | 89 |
| 4 | 1 | 2 | 6 | 4 | 91 |
+----+-----------+----------+---------------+----------------+-----------+
My current query is here:
SELECT pcs_lineup.id_person, pcs_lineup.id_club, pcs_lineup.type,
pcs_persons.lastname, pcs_persons.firstname, count( pcs_lineup.id_person) AS apps, count(pcs_subs.id_person_in) AS subs
FROM pcs_lineup
JOIN pcs_matchs ON pcs_matchs.id = pcs_lineup.id_match
JOIN pcs_persons ON pcs_persons.id = pcs_lineup.id_person
LEFT JOIN pcs_subs ON pcs_subs.id_person_in = pcs_lineup.id_person
WHERE pcs_lineup.id_club =2
AND pcs_matchs.id_compet =1
AND pcs_matchs.id_season =1
AND pcs_lineup.type = 2 OR pcs_subs.id_person_in IS NOT NULL AND pcs_subs.id_club =2
GROUP BY id_person
My current result structure (list of players who played as member of starting lineup or played as substitutes, players who just sat on the bench not counted)
+-----------+----------+-------+-----------+------------+-------+-------+
| id_person | id_club | type | lastname | firstname | apps | subs |
+-----------+----------+-------+-----------+------------+-------+-------+
I would like to add extra columns (goals, yellow cards, red cards) to the result, but I don't know how.
The structure of desired result:
+-----------+----------+-------+-----------+------------+-------+----------+-------+--------+---------------+------------+
| id_person | id_club | type | lastname | firstname | apps | starter | subs | goals | yellow cards | red_cards |
+-----------+----------+-------+-----------+------------+-------+----------+-------+--------+---------------+------------+
I hope that some expert could help for me, because I have no idea how could I join these tables for the desired result. Many thanks!
Modified code (results are not good)
SELECT pcs_lineup.id_person
,pcs_lineup.id_club
,pcs_lineup.type
,pcs_persons.lastname
,pcs_persons.firstname
,count( pcs_lineup.id_person) AS apps
,CASE WHEN pcs_lineup.type = 2 THEN 'YES' END starter
,count(pcs_subs.id_person_in) AS subs
,count(pcs_goals.goal_min) AS goals
,count(card_yellow) as "Yellow Cards"
,count(card_red) as "Red Card"
FROM pcs_lineup
JOIN pcs_matchs ON pcs_matchs.id = pcs_lineup.id_match
JOIN pcs_persons ON pcs_persons.id = pcs_lineup.id_person
LEFT JOIN pcs_subs ON pcs_subs.id_person_in = pcs_lineup.id_person
LEFT JOIN pcs_goals ON pcs_goals.id_match = pcs_matchs.id
AND pcs_persons.id = pcs_goals.id_person
LEFT JOIN pcs_cards ON pcs_cards.id_match = pcs_matchs.id
AND pcs_cards.id_person = pcs_persons.id
AND pcs_goals.id_club = pcs_cards.id_club
WHERE pcs_lineup.id_club =2
AND pcs_matchs.id_compet =1
AND pcs_matchs.id_season =1
AND pcs_subs.id_person_in IS NOT NULL AND pcs_subs.id_club =2
GROUP BY id_person
You need to join 2 more tables i.e. pcs_goals and pcs_cards -
SELECT pcs_lineup.id_person
,pcs_lineup.id_club
,pcs_lineup.type
,pcs_persons.lastname
,pcs_persons.firstname
,count( pcs_lineup.id_person) AS apps
,CASE WHEN pcs_lineup.type = 2 THEN 'YES' END starter
,count(pcs_subs.id_person_in) AS subs
,count(pcs_goals.goals_min) AS goals
,count(card_yellow) as "Yellow Cards"
,count(card_red) as "Red Card"
FROM pcs_lineup
JOIN pcs_matchs ON pcs_matchs.id = pcs_lineup.id_match
JOIN pcs_persons ON pcs_persons.id = pcs_lineup.id_person
LEFT JOIN pcs_subs ON pcs_subs.id_person_in = pcs_lineup.id_person
LEFT JOIN pcs_goals ON pcs_goals.id_match = pcs_matchs.id
AND pcs_persons.id = pcs_matchs.id_person
LEFT JOIN pcs_cards ON pcs_cards.id_match = pcs_matchs.id
AND pcs_cards.id_person = pcs_persons.id
AND pcs_goals.id_club = pcs_cards.id_club
WHERE pcs_lineup.id_club =2
AND pcs_matchs.id_compet =1
AND pcs_matchs.id_season =1
AND pcs_subs.id_person_in IS NOT NULL AND pcs_subs.id_club =2
GROUP BY id_person
I am not sure what do you mean by starter column.
pcs_subs.id_person_in change to id_person_out
Presume first, that the following table exists in a MySQL Database
|----|-----|-----|----|----|-----------|--------------|----|
| id | rid | ver | n1 | n2 | s1 | s2 | b1 |
|----|-----|-----|----|----|-----------|--------------|----|
| 1 | 1 | 1 | 0 | 1 | Hello | World | 0 |
| 2 | 1 | 2 | 1 | 1 | Hello | World | 0 |
| 3 | 1 | 3 | 0 | 0 | Goodbye | Cruel World | 0 |
| 4 | 2 | 1 | 0 | 0 | Hello | Doctor | 1 |
| 5 | 2 | 2 | 0 | 0 | Hello | Nurse | 1 |
| 6 | 3 | 1 | 0 | 0 | Dippity | Doo-Dah | 1 |
|----|-----|-----|----|----|-----------|--------------|----|
Question
How do I write a query to determine whether for any given rid, what changed between the most recent version and the version immediately preceding it (if any) such that it produces something like this:
|-----|-----------------|-----------------|-----------------|
| rid | numbers_changed | strings_changed | boolean_changed |
|-----|-----------------|-----------------|-----------------|
| 1 | TRUE | TRUE | FALSE |
| 2 | FALSE | TRUE | FALSE |
| 3 | n/a | n/a | n/a |
|-----|-----------------|-----------------|-----------------|
I think that I should be able to do this by doing a cross-join between the table and itself but I can't resolve how to perform this join to get the desired output.
I need to generate this "report" for a table with 10's of columns and 1-10 versions of 100's of records (resulting in 1000's of rows). Note the particular design of the database is not my own and altering the structure of the database (at this time) is not an acceptable approach.
The actual format of the output isn't important - and if it simplifies the query getting a "full breakdown" of what changed for each "change set" would also be acceptable, for example
|-----|-----|-----|----|----|----|----|----|
| rid | old | new | n1 | n2 | s1 | s2 | b1 |
|-----|-----|-----|----|----|----|----|----|
| 1 | 1 | 2 | Y | N | N | N | N |
| 1 | 2 | 3 | Y | Y | Y | Y | N |
| 2 | 4 | 5 | N | N | N | Y | N |
|-----|-----|-----|----|----|----|----|----|
Note that it is also ok, in this case to omit rid records which only have a single version, as for the purposes of this report I only care about records that have changed and getting a separate list of records that haven't changed is an easy query
You can join every row with the following one with
select *
from history h1
join history h2
on h2.rid = h1.rid
and h2.id = (
select min(h.id)
from history h
where h.rid = h1.rid
and h.id > h1.id
);
Then you just need to compare every column from the two rows like h1.n1 <> h2.n1 as n1.
The full query would be:
select h1.rid, h1.id as old, h2.id as new
, h1.n1 <> h2.n1 as n1
, h1.n2 <> h2.n2 as n2
, h1.s1 <> h2.s1 as s1
, h1.s2 <> h2.s2 as s2
, h1.b1 <> h2.b1 as b1
from history h1
join history h2
on h2.rid = h1.rid
and h2.id = (
select min(h.id)
from history h
where h.rid = h1.rid
and h.id > h1.id
);
Result:
| rid | old | new | n1 | n2 | s1 | s2 | b1 |
|-----|-----|-----|----|----|----|----|----|
| 1 | 1 | 2 | 1 | 0 | 0 | 0 | 0 |
| 1 | 2 | 3 | 1 | 1 | 1 | 1 | 0 |
| 2 | 4 | 5 | 0 | 0 | 0 | 1 | 0 |
Demo: http://sqlfiddle.com/#!9/2e5d12/5
If the columns can contain NULLs, You might need something like NOT h1.n1 <=> h2.n1 as n1. <=> is a NULL-save equality check.
If the version within a rid group is guaranteed to be consecutive, you can simplify the JOIN to
from history h1
join history h2
on h2.rid = h1.rid
and h2.ver = h1.ver + 1
Demo: http://sqlfiddle.com/#!9/2e5d12/7
I have a table 'questions' with columns
userid
qid(question id)
answer
The questions are multiple choice so not every question has the same number of answers a user can choose from.
question 100 might have 4 answers to choose from.
question 200 might have 6 answers to choose from.
question 300 might have 2 answers to choose from.
etc
So the table might look something like this:
+-------- --+---------+--------+
| userid | qid | answer |
+---- ------+---------+--------+
| 1 | 100 | 4 |
| 1 | 200 | 6 |
| 1 | 300 | 1 |
| 1 | 400 | 4 |
| 2 | 100 | 1 |
| 2 | 400 | 6 |
| 3 | 200 | 4 |
| 3 | 400 | 4 |
| 3 | 100 | 1 |
| 4 | 100 | 1 |
| 4 | 400 | 6 |
| 5 | 200 | 1 |
| 5 | 400 | 6 |
+-----------+---------+--------+
I want to know what's the count for the most given answer for question 100, what's the count for the second highest answer for question 100 , what's the count for the 3rd highest answer for question 100, etc
I've can't figure how to query this and not sure if this is possible.
I would the results to be something like:
+------+----+----+----+----+----+----+----+
| qid |ans1|ans2|ans3|ans4|ans5|ans6|ans7|
+------+----+----+----+----+----+----+----+
| 100 | 3 | 0 | 0 | 1 | 0 | 0 | 0 |
| 200 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
| 300 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
| 400 | 0 | 2 | 0 | 0 | 0 | 3 | 0 |
+------+----+----+----+----+----+----+----+
Group by the questions and then you can use aggregate functions like sum() that apply to each group. And you can use a condition in sum() to do conditional summing
select qid,
sum(answer = 1) as ans1,
sum(answer = 2) as ans2,
sum(answer = 3) as ans3,
sum(answer = 4) as ans4,
sum(answer = 5) as ans5,
sum(answer = 6) as ans6,
sum(answer = 7) as ans7,
count(*) as total
from your_table
group by qid
I have 2 tables bellow
0 --> Pending
1 --> Success
2 --> Fail
table : mntnc
+-------+-------+-------+
| id | own | sts |
+-------+-------+-------+
| 1 | BN | 1 |
| 2 | BB | 2 |
| 3 | BN | 1 |
| 4 | BD | 1 |
| 5 | BD | 0 |
table : istlsi
+-------+-------+-------+
| id | own | sts |
+-------+-------+-------+
| 1 | BN | 1 |
| 2 | BB | 1 |
| 3 | BB | 1 |
| 4 | BC | 0 |
| 5 | BD | 2 |
of the two tables above, I want to add both of them to be the table below
+-------+-----------+-----------+-----------+
| own | success | fail | pending |
+-------+-----------+-----------+-----------+
| BN | 3 | 0 | 0 |
| BB | 2 | 1 | 0 |
| BD | 1 | 1 | 1 |
| BC | 0 | 0 | 1 |
The two key points here:
Union tables (I aliased result to B)
Use sum(case...) for each column.
First we union both tables together as an inline view.
We then use a case statement for each desired column and evaluate the status setting the value to 1 or 0 depending on sts value. and then sum those...
SELECT own
, sum(case when sts=1 then 1 else 0 end) as Success
, sum(case when sts=2 then 1 else 0 end) as Fail
, sum(case when sts=0 then 1 else 0 end) as Pending
FROM ( SELECT ID, own, sts
FROM mntnc
UNION ALL
SELECT id, own, sts
FROM istlsi
) B
GROUP BY own
Each childrow has a parentid and position. For childrows with the same position there is one row where start='1'.
What i'm trying to do is return the pending rows with their start row.
The results that should be shown are; start(owen) pending(dave,paul). This is because they have the same position. Here is the SQL fiddle http://sqlfiddle.com/#!2/e6e54/1
id | name | parentid| position| start | pending |
1 | mike | 0 | 0 | 0 | 0 |
2 | dave | 1 | 1 | 0 | 1 |
3 | paul | 1 | 1 | 0 | 1 |
4 | john | 1 | 2 | 1 | 0 |
5 | bret | 1 | 2 | 0 | 0 |
6 | owen | 1 | 1 | 1 | 0 |
7 | rick | 1 | 3 | 1 | 0 |
8 | jaye | 1 | 3 | 0 | 0 |
$getquery = mysql_query("select child.*
from `mytable` child inner join `mytable` parent
on parent.id=child.parentid
inner join `mytable` child2 on child.parentid=child2.parentid
and child2.pending='1'
where child.start='1' ORDER BY child.id DESC");
while($row=mysql_fetch_assoc($getquery)) {
$name = $row['name'];
echo "<p>Name: $name </p>";
}
select * from `mytable` order by position, pending
You can add a where position=1 clause if that's the only position of interest.