I've a simple query that selects all quizzes from DB.
$mysqli->query("SELECT * FROM quizz WHERE quizz_level = $level");
To display this result, I do simple foreach.
I also have a results table where I save outcome of the quizz
result_id, user_id, quizz_id, result_value
Now I'd like to modify original foreach and if result exist for spceific user for specific quizz, mark it differently (as answered).
I intend to do following, SELECT results table and grab all quizz_id's for specific user. Put this into an array. And while doing original foreach to display quizz questions, match it against keys in results array.
Is there a better solution to this problem?
Can it be done via mysql sub-query?
p.s. probably my worst title ever for the question :)
How about
SELECT q.*,case when r.user_id = $user_id then 1 else 0 end case as answered
FROM quizz q
inner join results r on q.quizz_id = r.quizz_id
WHERE q.quizz_level = $level
and then filter on answered on your php code
EDIT
I've changed the left outer to inner as it doesn't make sense the left outer. Also changed the columns of the join to quizz_id
The select is bringing all the records from the table quizz (q.*) and with the inner join is linking to the results table on the quizz_id columns.
Then on the select part I use case to check if the user_id is the one you are looking for
case when r.user_id = $user_id then 1 else 0 end case as answered
Then the recorset will have all the quizz table columns plus a column called answered where if the value is 1 that quizz was answered by the user_id and if its 0 then it wasn't answered by the user_id.
Related
I'm facing a problem here:
I'm building a forum, this forum has several tables and I'm trying to fetch the comments and user info in a single query.
So far, it should be easy, the problem is that I can't change the structure and with the following query I get a perfect result IF there is a like to the answer. If no one likes the answer it fails.
Select
mfr.mfr_forum_answers.id,
mfr.mfr_forum_answers.date_created,
mfr.mfr_forum_answers.last_updated,
mfr.mfr_forum_answers.content,
mfr.mfr_forum_answers.accepted,
mfr.mfr_forum_answers.user_id,
mfr.mfr_users.level,
mfr.mfr_users.avatar,
mfr.mfr_forum_likes.subject_id,
mfr.wp_users.ID As ID1,
mfr.mfr_forum_topics.user_id As owner_id,
(SELECT count(mfr.mfr_forum_likes.id) FROM mfr.mfr_forum_likes WHERE mfr.mfr_forum_likes.subject_id = :id AND mfr.mfr_forum_likes.type = 'answer') as likes,
(SELECT count(mfr.mfr_forum_likes.id) FROM mfr.mfr_forum_likes WHERE mfr.mfr_forum_likes.subject_id = :id AND makefitreal.mfr_forum_likes.type = 'answer' AND mfr.mfr_forum_likes.user_id = :sessionId ) as i_like,
mfr.wp_users.user_nicename
From
mfr.mfr_forum_likes Inner Join
mfr.mfr_forum_answers
On mfr.mfr_forum_answers.topic_id =
mfr.mfr_forum_likes.subject_id Inner Join
mfr.mfr_users
On mfr.mfr_forum_answers.user_id = mfr.mfr_users.id
Inner Join
mfr.wp_users
On mfr.mfr_users.id = mfr.wp_users.ID Inner Join
mfr.mfr_forum_topics
On mfr.mfr_forum_answers.topic_id = mfr.mfr_forum_topics.id
Where
mfr.mfr_forum_answers.topic_id = :id
And
mfr.mfr_forum_likes.type = 'answer'
So far as said it returns only if an answer has a like, I'm thinking on adding a add to the user who posts the answer by default but I'm trying to improve my skills by solving new issues.
If someone has a suggestion in how I could overcome the fact that if a table is empty, the query continues I'd be really thankfull.
Thanks in advance-
Pihh
Yes. What you are looking for are called left and right joins. According to the documentation, with a LEFT JOIN you still join two tables as normal but
If there is no matching row for the right table in the ON or USING part in a LEFT JOIN, a row with all columns set to NULL is used for the right table.
This means that you can try to join two tables, but if a row does not have any results it will still return the results from the first table. The same is true for a RIGHT JOIN only it works the opposite way: it will return results if the tabled being joined to has results, but the original table does not.
It looks like you have 3 tables for 3 relationships: there are answers, a user gives an answer, and an answer might or might not have like. To grab this data, I would suggest starting from your answers table, performing an INNER JOIN on your users table (assuming there are always users), and a LEFT JOIN on your likes table. Here is a simple example:
SELECT *
FROM answers
INNER JOIN users ON users.id = answers.user_id
LEFT JOIN likes ON likes.answer_id = answer.id
WHERE answers.id = :id
AND likes.type = 'answers'
Of course, if for some unknown reason you need to start from your likes table, then you'd have to RIGHT JOIN the other tables. I hope that gives you a good idea of how you'd make your query.
I created two tables, one stores the questions of a quiz, and the other one stores all the answers, that users made.
The first table called "questions" contains the questions:
Field names: id|question
Eg. contents:
1|what's your fav color?
2|what's your fav animal?
The second table named "answers" stores all the answers, that users made:
Fields names: id|questionid|userid|answer
Eg. contents:
1|1|1|Red
1|1|3|Magenta
1|1|4|Green
I'd like to select those questions, that haven't been answered yet by a user.
I store the current user's id in a $_SESSION['id'] session. I tried so many ways, to get these questions, the closest query I've made, was this:
$query = SELECT questions.*, answers.* FROM questions LEFT JOIN answers ON questions.id=answers.questionid WHERE answers.id IS NULL OR answers.userid <> '.$_SESSION['id'];
This won't work, because if there's another userid in the answers table at the same question id, it still selects that row. What could be the problem? Where did I mess up my query?
Thanks in advance for all of your help!
Your user condition is in the wrong place. Since you'll want to try to find a match between the specific user and the question and detect a non match, the user part needs to go inside the ON clause with a null check in the WHERE clause;
SELECT q.*
FROM questions q
LEFT JOIN answers a
ON q.id = a.questionid
AND a.userid = YOUR_USER_ID
WHERE a.id IS NULL
An SQLfiddle to test with.
I have 3 tables. 1 table is like the master table and I want all rows from this table where GameID = X. Then I have a guides table which will have a matching ID and finally i have a user table that defines whether the user has selected this row to be hidden. this is causing issues. This table may not have a row associated with it. This table is shared amongst ALL users. The primary key of this table is UserID+InfoID. The query below returns what I want provided there are no other rows in the table for other userIDs.
SELECT PS_Info.*, PS_Guides.Guide, PS_Userhidden.* FROM PS_Info
LEFT JOIN PS_Guides ON PS_Info.ID = PS_Guides.InfoID
LEFT JOIN PS_Userhidden ON PS_Info.ID = PS_Userhidden.InfoID
WHERE PS_Info.GameID = :ID AND (PS_Userhidden.UserID = :UserID)
OR (PS_Userhidden.UserID IS NULL AND PS_Userhidden.InfoID IS NULL)
So I will run the php script and have infoID =1 and userID=1. In the table there is infoID=1 and userid = 2, but nothing will be returned for this row. If I remove PS_Userhidden.UserID = :UserID I get multiple of the same row. The user table will grow to millions of rows. I need a way to make this query stick to the primary key of the users table so it will still return a row if no match exists in the user table and also return a row if there is a match in the users table for the specific user
I think you just need to move the condition on the hidden user to the ON clause:
SELECT i.*, g.Guide, h.*
FROM PS_Info i LEFT JOIN
PS_Guides g
ON i.ID = g.InfoID LEFT JOIN
PS_Userhidden h
ON i.ID = h.InfoID AND h.UserID = :UserID
WHERE i.GameID = :ID ;
Your description of the problem sounds like something that can happen when you start fiddling with conditions in the WHERE clause of a LEFT JOIN. It is a little hard to follow though. If this doesn't work, edit your question with sample data and desired results -- or, better yet, set up a SQL Fiddle.
There are many questions on how to find duplicates in a database, but not with the specific problem that I have.
I have a table with approx. 120000 entries. I need to find duplicates. To find them, I use a php script that is structured like the following:
//get all entries from database
//loop through them
//get entries with greater id
//compare all of them with the original one
//update database (delete duplicate, update information in linked tables, etc.)
It is not possible to sort out all duplicates already in the initial query, because I have to loop through all entries since my duplicate search is sensitive not only to entries that are 100% alike, but also entries that are 90% alike. I use similar_text() for that.
I think the first loop is okay, but looping through all other entries within the loop is just too much. With 120000 entries this would be close to (120000^2)/2 iterations.
So instead of using a loop within the loop, there must be a better way to do it. Do you have any ideas? I thought about using in_array(), but it is not sensitive to something like 90% string similarity, and also doesn't give me the array's fields it found the duplicates in - I would need those to get the entries' ids to update the database correctly.
Any ideas?
Thank you very much!
Charles
UPDATE 1
The query I am using right now is the following:
SELECT a.host_id
FROM host_webs a
JOIN host_webs b ON a.host_id != b.host_id AND a.web = b.web
GROUP BY a.host_id
It shows originals and duplicates perfectly, but I need to get rid of the originals, i.e. the first ones found with the associated data. How can I accomplish that?
You can JOIN the table onto itself and do it all in SQL (I know you say you don't think you can, but I would be surprised if this is the case). All you need to do is put all the columns you use to test for duplicates into the ON clause of the JOIN.
SELECT id
FROM tablename a
JOIN tablename b ON a.id != b.id AND a.col1 = b.col1 AND a.col2 = b.col2
GROUP BY id
This will return just the ids of the rows where col1 and col2 are duplicated. You can incorporate whatever string comparisons you need into this, the ON clause can be as complicated as you need it to be. For example:
SELECT id
FROM tablename a
JOIN tablename b ON a.id != b.id AND
(a.col1 = b.col1 AND (a.col2 = b.col2 OR a.col3 = b.col3))
OR ((a.col1 = b.col1 OR a.col2 = b.col2) AND a.col3 = b.col3)
OR (SOUNDEX(a.col1) = SOUNDEX(b.col1) AND SOUNDEX(a.col2) = SOUNDEX(b.col2) AND SOUNDEX(a.col3) = SOUNDEX(b.col3))
GROUP BY id
EDIT
Since all you are actually doing with your query is looking for rows where the web column is identical, this would do the job of finding only the duplicates and not the original "good" records - assuming host_id is numeric and that the "good" record would be the one with the lowest host_id:
SELECT b.host_id
FROM host_webs a
INNER JOIN host_webs b ON b.web = a.web AND b.host_id > a.host_id
GROUP BY b.host_id
I imagine the end game here would be to remove the duplicates, so if you are feeling brave you could actually delete them in one go:
DELETE b.*
FROM host_webs a
INNER JOIN host_webs b ON b.web = a.web AND b.host_id > a.host_id
The GROUP BY is not necessary in the DELETE statement because it doesn't matter if you try and delete the same row more than once in a single statement.
If you're doing a 1-time removal of duplicate items, I wouldn't bother writing a php script - it's cleaner to do it in sql.
The general algorithm for removing duplicates that I find works the best is:
1. duplicate the table
2. truncate the original table
3. set a unique index on whichever columns need to be unique
4. reinsert the rows using either INSERT IGNORE INTO original_table SELECT * FROM duplicate_table OR REPLACE INTO original_table SELECT * FROM duplicate table
5. fixed linked tables - remove orphaned rows (DELETE x FROM x LEFT JOIN original TABLE ON (...) WHERE original_table.id IS NULL)
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
SQL Join Differences
$PERSON = $DATABASE_LINK->query("SELECT * FROM `users`,`profiles` WHERE users.first_name = 'shane' && users.last_name = 'larson' && users.setup = '1' && profiles.zipcode = '53511' ORDER BY `full_name` ASC");
$PERSON = $PERSON->fetch_object();
var_dump($PERSON);
I want a query that scans for a user record based off name, and checks the zip code from the profile table. Above is a example. It works, but idk how joins work exactly. Any explanation on how joins match 2 rows would be awesome :)
the result set that you are receiving is a cross product of all rows in the user table that match the users criteria and all rows in the profiles table that match the profiles criteria.
It seems likely that this is not what you want.
consider adding something like users.blammy = profiles.blammy to join the two tables.
SELECT Cities.Name, Countries.Name, Countries.id
FROM Cities INNER JOIN Countries
ON Cities.CountryId = Countries.id
WHERE Cities.Name LIKE = 'town' LIMIT 10
Looking at that example:
The first row selects three columns from two different tables (Cities and Countries).
The second row specifies the two tables to join.
The third row specifies what item to match the two tables on.
The result from that query is a list
of Cities.Name that match the LIKE
clause and their corresponding
Countries.Name (which it finds by
matching Cities.CountryId with
Countries.id).
Read more here: http://www.wellho.net/mouth/158_MySQL-LEFT-JOIN-and-RIGHT-JOIN-INNER-JOIN-and-OUTER-JOIN.html
So using SQL Statements with "WHERE table1.id_x = table2.id_y" do work, but as far as i know they are slower than Joins (primarly with huge datasets).
So there inner and outer Joins, the outer Joins are divided into left- and right-joins.
A little Explanation for a left-Join:
SELECT Class.Id, Class.Name, Professor.Id, Professor.Name
FROM FROM Professor INNER JOIN Class
ON Professor.Id = Class.ProfessorId;
This Statement Selects all Professors, and does not care if a Professor don't teaches a Class. But if there is a class without a Professor, this class is not selected. It's called left Join because 'NULL' References in the left Table are ignored. The explanation for the right-Join should be trivial ;)
SELECT Class.Id, Class.Name, Professor.Id, Professor.Name
FROM FROM Professor LEFT OUTER JOIN Class
ON Professor.Id = Class.ProfessorId;
The Inner Join don't shows Professors without Classes and it don't shows Classes without Professors.
Please remember: Not all DBMS got the Keywords 'INNER' and 'OUTER' ;)