How to select every level with different user and user progress - mysql

I wanted to make a learning apps with a completed questions progress track on each level. I have 2 tables which contain all the level with its question count, and a table that save an user progress (completed questions) on each level. Here is the record,
Level table:
+----------+----------------------+-------------+--------+
| id_level | name | jumlah_soal | id_sub |
+----------+----------------------+-------------+--------+
| 1 | Basic Level 1 | 5 | 1 |
| 2 | Basic Level 2 | 6 | 1 |
| 3 | Basic Level 3 | 7 | 1 |
| 8 | Intermediate Level 1 | 5 | 2 |
| 9 | Intermediate Level 2 | 5 | 2 |
| 10 | Intermediate Level 3 | 5 | 2 |
..........................................................
Progress Table:
+-----------------+---------+-------------+----------+
| id_progreslevel | id_user | completed | id_level |
+-----------------+---------+-------------+----------+
| 1 | 1 | 2 | 1 |
| 2 | 1 | 3 | 2 |
| 3 | 2 | 2 | 1 |
+-----------------+---------+-------------+----------+
and when I joined the tables with the following query
SELECT IFNULL(progreslevel.id_user, 1) as id_user,
-> level.*, IFNULL(progreslevel.completed, 0) as completedquestions
-> FROM level LEFT JOIN progreslevel
-> ON level.id_level = progreslevel.id_level
-> WHERE level.id_sub = 1
-> HAVING id_user = 1;
It queries what I wanted:
+---------+----------+---------------+-------------+--------+--------------------+
| id_user | id_level | name | jumlah_soal | id_sub | completed questions |
+---------+----------+---------------+-------------+--------+--------------------+
| 1 | 1 | Basic Level 1 | 5 | 1 | 2 |
| 1 | 2 | Basic Level 2 | 6 | 1 | 3 |
| 1 | 3 | Basic Level 3 | 7 | 1 | 0 |
+---------+----------+---------------+-------------+--------+--------------------+
BUT, when I tried to change to query a progress for user ID = 2, it became like this:
+---------+----------+---------------+-------------+--------+--------------------+
| id_user | id_level | name | jumlah_soal | id_sub | completed questions |
+---------+----------+---------------+-------------+--------+--------------------+
| 2 | 1 | Basic Level 1 | 5 | 1 | 2 |
| 2 | 3 | Basic Level 3 | 7 | 1 | 0 |
+---------+----------+---------------+-------------+--------+--------------------+
Yes, the Basic Level 2 is gone because user 2 hadn't done it yet but user 1 had.
This is where I'm stuck, I want to whichever user I choose, its always query all the level, even when the other user had done it. It should be like this:
+---------+----------+---------------+-------------+--------+--------------------+
| id_user | id_level | name | jumlah_soal | id_sub | completed questions |
+---------+----------+---------------+-------------+--------+--------------------+
| 2 | 1 | Basic Level 1 | 5 | 1 | 2 |
| 2 | 2 | Basic Level 2 | 6 | 1 | 0 |
| 2 | 3 | Basic Level 3 | 7 | 1 | 0 |
+---------+----------+---------------+-------------+--------+--------------------+
How do I achieve this?
Thanks in advance and Sorry if you got dizzy either at my explanation or database, I tried my best to translate it, so it's understandable

I'm not really sure that I understand the requirement, but I think you're after something like this...
SELECT l.*
, 2 id_user
, COALESCE(MAX(p.completed),0) completed_questions
FROM level l
LEFT
JOIN progress p
ON p.id_level = l.id_level
AND p.id_user = 2
WHERE l.id_sub = 1
GROUP
BY l.id_level;

Related

Grab unique data from repeating record

I've got a procedural question that is likely going to expose how burnt out I am. I have 3 tables, users, projects and project_users. projects is joined with project_user to allow users to share projects.
I'd like to let, say, Bob see all of his projects and who he is sharing them with. In the example below Bob's sharing his 1 project with everyone. In his dashboard I'd like to show him "Bob's 1 Project" with everyone it's shared with. This is being managed in a while loop because Bob really has dozens of projects he's sharing with dozen's of people. The problem is, how can I show 1 project with everyone it's shared_with instead of showing the same project over and over each time there's a new shared_with value?
id | users | | id | projects | | id | owner_id | shared_with | project_id | role
---+-------------------- ---+------------------- ---+----------+-------------+------------+------
1 | bob#here.com | | 1 | Bob's 1 Project | | 1 | 1 | NULL | 1 | 1
2 | tom#there.com | | 2 | Tom's | | 2 | 1 | 4 | 1 | 2
3 | jan#red.com | | 3 | Jan's | | 3 | 1 | 2 | 1 | 2
4 | joe#somewhere.com | | 4 | Joe's Project | | 4 | 1 | 3 | 1 | 2
5 | fred#where.com | | 5 | Fred's Project | | 5 | 1 | 5 | 1 | 2

Complex SQL Counting Query

I have a Facility Booking System and I would like to create a CSV format report.
1. I want to count total booking records
2. I want to count how many user booked our facilities, based on that timeslot
3. I want to combine two queries into one as I need to put the result into the CSV
And here is the table structure:
fbid | memberid | fbdate | fbtimeslot | fname | fposition | fattendance
------------------------------------------------------------------------------
1 | C00001 | 2014-12-12 | 1 | computer | 1 | 1
2 | C00002 | 2014-12-12 | 1 | computer | 4 | 3
3 | C00003 | 2014-12-12 | 1 | computer | 6 | 1
4 | C00002 | 2014-12-12 | 1 | computer | 8 | 3
5 | C00004 | 2014-12-12 | 1 | computer | 4 | 0
6 | C00002 | 2014-12-12 | 1 | computer | 24 | 1
7 | C00001 | 2014-12-12 | 2 | computer | 1 | 0
8 | C00002 | 2014-12-12 | 3 | computer | 1 | 0
For task 1, I have found the solution:
(fattendance = 3 means the position has changed and I have to keep record but the CSV report doesn't this)
SELECT fbtimeslot, COUNT(*) FROM facilitybooking WHERE fname='computer' AND fattendance<>3 GROUP BY fbtimeslot
But for 2 and 3, I tried more than 20 different statements but I still couldn't get the result.
Well, hard tasks though.
I will be very appreciated if you can help me to solve this.
Extras: SQLFiddle Link: http://sqlfiddle.com/#!2/4800b8/3

What happens if I select two tables with no WHERE clause?

I had a technical interview last week, and my interviewer asked me what happens if I run the following query:
SELECT * FROM tbl1, tbl2
I think I answered it correctly, but it wasn't an in-depth answer.
I said that I would select all the columns in both tables. For example if tbl1 has 3 columns, and tbl2 has 4 columns. The result set would have 7 columns.
Then he asked me why 7? and I said because I was selecting everything from each table.
That was a bad answer, but I couldn't think of anything else.
To cut to the chase, after the interviewed I executed the latter statement using two tables.
Table A, had 3 animal: dog, cat and elephant.
Table B had 2 names: Mat and Beth
This is the result set that I got after the statement being executed:
*********************************************
| id_tbl1 | name_tbl1 | id_tbl2 | name_tbl2 |
*********************************************
| 1 | dog | 1 | Mat |
| 2 | cat | 1 | Mat |
| 3 | elephant | 1 | Mat |
| 1 | dog | 2 | Beth |
| 2 | cat | 2 | Beth |
| 3 | elephant | 2 | Beth |
*********************************************
So my question is, why does the statement behaves like that?
In other words:
Why does the Table B's records repeat themselves until I reach the end of table A, and then it starts all over again?
How would you have answered the question in a way that it would've "WOW'd" the interviewer?
If this question does not belong to SO, feel free to delete it or close it!
If you do a select like this, all rows in one resultset are joined to all rows in the other resultset (Cartesian Product).
So you get a list of all rows of the first table with the first row of the second table, Then all entries for the second row and so on. The order may be an implementation detail. Not sure if it is defined that the first order is by the first table, it might be different across implementations.
If you join three tables (or more), then the same happens with all rows of all tables. This, of course, is not only for tables, but for any result set from joins.
The result will be a cartisian product
take a look at this example
SQL Example
You can see there are two tables one has 5 records and the other has 4 and the result is 20 records. Means 5 * 4 = 20 instead of 5 + 4 = 9 as you are assuming.
Table1
| IDX | VAL |
---------------
| 1 | 1val1 |
| 1 | 1val2 |
| 2 | 2val1 |
| 2 | 2val2 |
| 2 | 2val3 |
Table2
| ID | POINTS |
---------------
| 1 | 2 |
| 2 | 10 |
| 3 | 21 |
| 4 | 29 |
Result of below query
SELECT * FROM Table1 , Table2
| IDX | VAL | ID | POINTS |
-----------------------------
| 1 | 1val1 | 1 | 2 |
| 1 | 1val1 | 2 | 10 |
| 1 | 1val1 | 3 | 21 |
| 1 | 1val1 | 4 | 29 |
| 1 | 1val2 | 1 | 2 |
| 1 | 1val2 | 2 | 10 |
| 1 | 1val2 | 3 | 21 |
| 1 | 1val2 | 4 | 29 |
| 2 | 2val1 | 1 | 2 |
| 2 | 2val1 | 2 | 10 |
| 2 | 2val1 | 3 | 21 |
| 2 | 2val1 | 4 | 29 |
| 2 | 2val2 | 1 | 2 |
| 2 | 2val2 | 2 | 10 |
| 2 | 2val2 | 3 | 21 |
| 2 | 2val2 | 4 | 29 |
| 2 | 2val3 | 1 | 2 |
| 2 | 2val3 | 2 | 10 |
| 2 | 2val3 | 3 | 21 |
| 2 | 2val3 | 4 | 29 |
I think you are confusing yourself by running an example with two tables that have identical fields. You are referring to a Union, which will combine the values of 1 table with another, and using your example this would give you 3 + 4 = 7 results.
The comma separated FROM statement is doing JOIN, which will go through all values in Table X and pair them with all the values of Table Y. This would result in Size of X * Size of Y results, and using your example this would be 3 * 4 = 12.

How should I write this MySQL query containing multiple Left Joins

I have a query consisting of multiple joins and I am wondering whether it can be re-written to improve performance.
I have 2 tables as follows (I have removed non-important columns for this example):
slots
------------------------------------------
| id | name | slot_1 | slot_2 | slot_3 |
------------------------------------------
| 1 | Bob | 1 | 2 | 3 |
| 2 | Jim | 4 | 3 | 3 |
| 3 | Alf | 1 | 2 | 5 |
------------------------------------------
(There are 25 slots in total, each in it's own column)
slot_details
-----------------------------------
| id | stat_1 | stat_2 | stat_3 |
-----------------------------------
| 1 | 1 | 5 | 6 |
| 2 | 4 | 31 | 23 |
| 3 | 6 | 5 | 7 |
| 4 | 7 | 4 | 9 |
| 5 | 2 | 3 | 5 |
-----------------------------------
(There are 10 stats in total)
The query is as follows:
SELECT
slots.name,
slot_1_details.stat_1 AS slot_1_stat_1,
slot_1_details.stat_2 AS slot_1_stat_2,
slot_1_details.stat_3 AS slot_1_stat_3,
slot_2_details.stat_1 AS slot_2_stat_1,
slot_2_details.stat_2 AS slot_2_stat_2,
slot_2_details.stat_3 AS slot_2_stat_3,
slot_3_details.stat_1 AS slot_3_stat_1,
slot_3_details.stat_2 AS slot_3_stat_2,
slot_3_details.stat_3 AS slot_3_stat_3
FROM
slots
LEFT JOIN
slot_details AS slot_1_details
ON (
slot_1_details.id = slots.slot_1
)
LEFT JOIN
slot_details AS slot_2_details
ON (
slot_2_details.id = slots.slot_2
)
LEFT JOIN
slot_details AS slot_3_details
ON (
slot_3_details.id = slots.slot_3
)
WHERE (
slots.id = 1
)
The expected outcome of this query would be as follows:
| name | slot_1_stat_1 | slot_1_stat_2 | slot_1_stat_3 | slot_2_stat_1 | slot_2_stat_2 | slot_2_stat_3 | slot_3_stat_1 | slot_3_stat_2 | slot_3_stat_3 |
|bob | 1 | 5 | 6 | 4 | 31 | 23 | 6 | 5 | 7 |
Unfortunately I am not in a situation where I can change the tables.
Thank you for the help!
maybe
SELECT * FROM slots s LEFT JOIN slot_details sd ON s.id=sd.id
but i'm not sure because the query you posted is very confusing.
what are the keys of those tables?

select random value from each type

I have two tables, rating:
+-----------+-----------+-------------+----------+
| rating_id | entity_id | rating_code | position |
+-----------+-----------+-------------+----------+
| 1 | 1 | Quality | 0 |
| 2 | 1 | Value | 0 |
| 3 | 1 | Price | 0 |
+-----------+-----------+-------------+----------+
And rating_option
+-----------+-----------+------+-------+----------+
| option_id | rating_id | code | value | position |
+-----------+-----------+------+-------+----------+
| 1 | 1 | 1 | 1 | 1 |
| 2 | 1 | 2 | 2 | 2 |
| 3 | 1 | 3 | 3 | 3 |
| 4 | 1 | 4 | 4 | 4 |
| 5 | 1 | 5 | 5 | 5 |
| 6 | 2 | 1 | 1 | 1 |
| 7 | 2 | 2 | 2 | 2 |
| 8 | 2 | 3 | 3 | 3 |
| 9 | 2 | 4 | 4 | 4 |
| 10 | 2 | 5 | 5 | 5 |
| 11 | 3 | 1 | 1 | 1 |
| 12 | 3 | 2 | 2 | 2 |
| 13 | 3 | 3 | 3 | 3 |
| 14 | 3 | 4 | 4 | 4 |
| 15 | 3 | 5 | 5 | 5 |
+-----------+-----------+------+-------+----------+
I need a SQL query (not application level, must stay in the database) which will select a set of ratings randomly. A sample result would look like this, but would pick a random value for each rating_id on subsequent calls:
+-----------+-----------+------+-------+----------+
| option_id | rating_id | code | value | position |
+-----------+-----------+------+-------+----------+
| 1 | 1 | 1 | 1 | 1 |
| 8 | 2 | 3 | 3 | 3 |
| 15 | 3 | 5 | 5 | 5 |
+-----------+-----------+------+-------+----------+
I'm totally stuck on the random part, and grouping by rating_id has been a crap shoot so far. Any MySQL ninjas want to take a stab?
Thanks,
Joe
EDIT: I've tried rand() in a bunch of combinations, and I'm sure that it will be necessary to create the randomness of the result, but I cannot figure out how to return one random row for each of the rows in rating. I cannot use order by rand() limit 1 because I need three rows, and order by rand() limit 3 won't give me one of each rating_id, which is the ultimate goal. I need a combination of rand() and either subqueries or joins so that I can ensure one of each rating_id.
Alright, a little messy, but seems to do the job. Someone may know what they're doing better than I do that can clean this up:
SELECT random.rating_id, random.rand_option_id, r3.code, r3.value, r3.position
FROM
(SELECT r.rating_id,
(SELECT r2.option_id
FROM rating_option r2
WHERE r2.rating_id = r.rating_id
ORDER BY RAND() LIMIT 1) AS 'rand_option_id'
FROM rating_option r
GROUP BY r.rating_id
) random
LEFT JOIN rating_option AS r3 ON r3.option_id = rand_option_id
Results (varies every time, of course):
+-----------+----------------+------+-------+----------+
| rating_id | rand_option_id | code | value | position |
+-----------+----------------+------+-------+----------+
| 1 | 4 | 4 | 4 | 4 |
| 2 | 6 | 1 | 1 | 1 |
| 3 | 13 | 3 | 3 | 3 |
+-----------+----------------+------+-------+----------+
You could use the rand() function to do sorting in a select on the rating table.
For example:
select rating_id from rating order by rand() limit 1
As clarified in your comments, and the other posts above
select * from rating_option order by rand()
will return all records in a random order... However, if you want only X number, then inclue that as the limit as noted by others
select * from rating_option order by rand() limit 5 (or whatever number)
Have you looked into the rand() function?
SELECT column FROM table
ORDER BY RAND()
LIMIT 1
http://www.petefreitag.com/item/466.cfm
http://dev.mysql.com/doc/refman/5.0/en/mathematical-functions.html#function_rand
Sample code
select *
from rating_option
group by rating_id
order by rand()