SQL many to many relation - mysql

I’m using Wordpress quiz plugin. It has two tables (in fact more, but I’m trying to focus on the issue) – ‘questions’ and ‘questionpots’:
‘questions’: questionID ---- Question ---- potID
‘questionpots’: potID ---- potName
And the default query is:
$SQL = 'Select * FROM questions WHERE potID='.$potID;
So by default every question is assigned to a specific pot (‘potID’ in ‘questions’ table) and the relation is ‘one to many’. What I want to do is to assign questions to MORE than just one pot, so that for example the question “When did Rome fell?” would be asked whether someone choose “Rome history” pot OR “Ancient history” pot.
So I added the third table, ‘relations’, which matches questionIDs and potIDs.
The problem is I can’t figure out the query to select * from ‘questions’ WHERE potID='.$potID assuming that there’s one or MORE than one question for $potID.
How should I join this tables?
Thanks.

To resolve a many-to-many relationship between two entities, we add a third table, a relationship table, with foreign keys referencing the two entity tables.
A row added to the relationship table represents a (wait for it...) relationship between the two entities.
As an example, assuming tables question and pot both have an id column as the primary key:
CREATE TABLE question_pot
( question_id INT UNSIGNED NOT NULL COMMENT 'fk ref question'
, pot_id INT UNSIGNED NOT NULL COMMENT 'fk ref pot'
, PRIMARY KEY (question_id, pot_id)
, CONSTRAINT FK_question_pot_question
FOREIGN KEY (question_id) REFERENCES question(id)
, CONSTRAINT FK_question_pot_pot
FOREIGN KEY (pot_id) REFERENCES pot(id)
) ENGINE=InnoDB
To add a relationship from question id=444 to two pots pot id=7 and pot id=13:
INSERT INTO question_pot VALUES (444,7), (444,13);
And scrap the pot_id column in the question table. You can save those first...
INSERT INTO question_pot (question_id, pot_id)
SELECT q.id
, q.pot_id
FROM question q
WHERE q.pot_id IS NOT NULL
;
And then drop the pot_id column from question.
If you've gotten that far, now you just need to perform JOIN operations...
To get all questions in pot id=13, for example:
SELECT q.*
FROM question q
JOIN question_pot r
ON r.question_id = q.id
WHERE r.pot_id = 13
FOLLOWUP
The query above gives an example of assigning an alias to a table, and using the alias to qualify column references.
Qualifying column references is best practice; it makes the statement easier for someone to read and interpret, without requiring them to lookup the definitions of the tables to figure out which column is coming from which table.
Compare the query above to this:
SELECT *
FROM question
JOIN question_pot
ON question_id = id
WHERE pot_id = 13
Now, try answering these questions: Is id a reference to a column from question or question_pot. (If this query is in a MySQL stored program (procedure, function, trigger), is the reference to id a reference to a column, or to a procedure variable.)
In some cases, we have to qualify column references to make the reference unambiguous, when the reference can refer to a column in multiple tables, and causes MySQL to throw an ambiguous column reference error.
It's possible to have a query working just fine, and then add a column to a table, e.g. adding id column to the question_pot table, and then the query will start failing with the "ambiguous column" error.
In my way of thinking, it's not acceptable that the addition of a column to a table should cause a "working" query to break. We can prevent that kind of error popping up in the future, simply by explicitly qualifying the column reference, even if it's not required right now.
As far as why we choose to use short aliases like q and r in place of just the table name.
In EXACTLY the same way you've identified the table names in your comment:
wp_ai_quiz_tblquestions (=question)
wp_ai_quiz_tblquestionpots (=pot)
wp_ai_quiz_question_pot (=question_pot)
I'm essentially saying the same thing with the table aliases:
wp_ai_quiz_tblquestions (=q)
wp_ai_quiz_tblquestionpots (=p)
wp_ai_quiz_question_pot (=r)
Compare reading this:
SELECT q.*
FROM wp_ai_quiz_tblquestions q
JOIN wp_ai_quiz_question_pot r
ON r.question_id = q.id
WHERE r.pot_id = 13
To reading this:
SELECT wp_ai_quiz_tblquestions.*
FROM wp_ai_quiz_tblquestions
JOIN wp_ai_quiz_question_pot
ON wp_ai_quiz_question_pot.question_id = wp_ai_quiz_tblquestions.id
WHERE wp_ai_quiz_question_pot.pot_id = 13
Throw in a few more tables, and also don't line things up like I always line things up, and it requires more effort from the reader to decipher what the statement is doing. In really complicated queries, referencing half a dozen or more tables, I'll add comments above the query, explaining the usage of the aliases.
-- find all questions in a particular pot
-- q = question row to be returned from wp_ai_tblquestion
-- r = relationship between question and pot to be searched
SELECT q.id
, q.name
, ...

Related

Why should I use JOIN if I only need info from the table with foreign key?

I have a table Customer with Primary key customerNumber. I also have a table table customerOrder that has a foreign key FK_customerNumber to customerNumber in the Customer table.
I know the customerNumber and need to select only order information related to that user.
I see a lot of tutorials using a JOIN like this
SELECT
projectNumber, orderNumber
FROM
`customerOrder` t1
INNER JOIN `customer` t2
ON t1.`FK_customerNumber` = t2.`customerNumber`
WHERE
t2.`customerNumber` = 50;
It's working but why can't I just select the FK? I get the same result. The value of the FK in customerOrder and PK in customer are the same.
This gives me the same result without JOIN
SELECT
projectNumber, orderNumber
FROM
`customerOrder`
WHERE
`FK_customerNumber` = 50;
I understand if I need info from both tables like this but now I only need info from the customerOrder
SELECT customerOrder.`projectNumber`, customer.`username`
FROM `customerOrder`
INNER JOIN customer ON customerOrder.`FK_customerNumber` = customer.`customerNumber`;
Why can't I just select the FK
You can.
I can't speak for the tutorial writers, but I'd hazard a guess that they're trying to demonstrate how the JOIN works - the principle that you've something on the left and something related on the right, and a JOIN is how you associate those two things.
If you perform an EXPLAIN SELECT ... - you'll likely see that MySQL has optimised out looking at the left table altogether, as it knows you're only really dealing with data on the right.
The only scenario I could foresee necessitating the JOIN is if there's no foreign key constraint on the right. In that scenario, the more verbose SELECT ... FROM ... JOIN ... would ensure that you don't get anything from the right if it's missing from the left (which the foreign key constraint would prevent from being the case).
Other reasons you might opt to be more verbose:
The query is "more explicit" - someone coming to it afresh can see the relationship of the two tables at a glance (that might be desirable, depending on context)
You intend to pull some additional data from the left at some point soon (feature isn't completed in code yet)
Equally you might explicitly choose not to include the join for the sake of ensuring optimal performance. (But as I say, you'd be surprised at how good MySQL is at optimising on its own :-))

Renaming a Key on a multi-table MySQL SELECT

I'm trying to get the content of 3 different tables.
table A = Is containing our users list, table B = Is returning contracts related to users,table C = Is returning formula details related to the contracts.
In order to make it the right way, I'm using the following multi table request:
SELECT * FROM rscm_students A, rscm_files B, rscm_formulas C
WHERE B.dossier_status = 0
AND A.student_agency = :agency
AND B.file_student_id = A.id
AND B.file_formula_id = C.id
AND C.formula_place = 0
GROUP BY A.student_uniqid
ORDER BY B.file_date_create";
This is where the whole damn thing become a little complicated. It is returning the correct datas, but as the primary key of every table here is called "id". I can't do some foreach in php. If I got 3 contracts on 1 user, it impossible for me to regroup every contract in the same user array.
I'm still not an expert in SQL, that's why I'm using Phinx to control my database. This is also why my primary keys are named "id".
If you have a good idea, please let me know!
Alright, I will make an answer out of it.
First off, don't use
select *
The above select is fine for quick and dirty development prior to production. But it makes a mess out of things such as your joins with common column names coming out of multiple tables (like id and others).
Use modern explicit join syntax. Don't use the older join style. So use join and on.
Lastly with table aliases, create unique output column names for the id columns or other clashes such as
A.id as aid, B.id as bid

Seeking optimised proper mysql query for multiple table joins

Hi developer/dba friends I have small issues for fetching details with mysql tables as following :
I have cid lets say cid=xxx
I want output(cid,ruser_id,dtoken,akey,prase) in one record using cid as key input
what mysql query I should perform that can also optimise this fetch with smooth less time on execution?
table structure is as following:
tbl_mk(cid,pro_id) -> pro_id primary key of tbl_pro
tbl_luser(cid,ruser_id) -> ruser_id primary key of tbl_ruser
tbl_ruser(id,dtoken)->id is primary key of this tbl_ruser where its referenced in tbl_luser as ruser_id
tbl_pro(id,akey)-> id is primary key of this tbl_pro which its referenced in tbl_mk as pro_id
tbl_app(akey,prase)
primary id/reference naming convention is i.e like if name of table is
tbl_name then id referenced in other table for tbl_name is name_id. where id is primary key of tbl_name.
I know there are lot of mysql experts here so how to make it working with less efforts, fyi I am basically mobile app developer but there is time m working on some mysql stuffs for web apis needs :)
Thanks and I really appreciate and admire if some one can solve this problem for me.I did one query and getting details but seems its not proper way I need more efficient way thats why m posting here.
Waiting for some best reply with expected answer.
Just join the tables. Assuming :cid is your input:
SELECT l.cid, l.ruser_id, r.dtoken, p.akey, prase
FROM tbl_luser l
JOIN tbl_ruser r ON l.ruser_id = r.id
JOIN tbl_mk m ON l.cid = m.cid
JOIN tbl_pro p ON p.id = m.prod_id
JOIN tbl_app a ON a.akey = p.akey
WHERE l.cid = :cid

SELECT Query where only one of three fields containing Foreign Keys is filled

As shown below the Table Recording contains three foreign keys, only one of which will be filled for any one entry. My question is how can I format a select statement in Access that will only pull the names from the relevant table given the foreign key for that table?
I have tried using IIf, which works when checking which Foreign Key is Not Null after the SELECT:
SELECT Recording.[idRecording],
IIf(Recording.[Artist_idArtist] Is Not Null,
Artist.[artName] ,
IIf(Recording.[Band_idBand] is Not Null,
Band.[bName],
Composer.[cName]))
FROM ...
But putting any conditions after the FROM statement, to get the correct JOIN, results in an error:
FROM
IIf(Recording.[Artist_idArtist] Is Not Null,
Artist INNER JOIN Recording ON Recording.[Artist_idArtist] = Artist.[idArtist],
IIf(Recording.Band_idBand Is Not Null,
Band INNER JOIN Recording ON Recording.[Band_idBand] = Band.[idBand],
Composer INNER JOIN Recording ON Recording.[Composer_idComposer] = Composer.[idComposer]));
I'm new to this so I'm probably missing something obvious, or going about it the wrong way. I believe what I've ended up with here is an Exclusive Arc? I'm not sure if this is even a good design, I had thought of scrapping the Recording table and simply adding the foreign keys to Track.
For reference here is the Relationship Design:
You can solve the problem with LEFT JOINs
SELECT
R.idRecording,
Nz(A.artName, Nz(B.bName, C.cName))
FROM
((Recording R
LEFT JOIN Artist A
ON R.Artist_idArtist = A.idArtist)
LEFT JOIN Band B
ON R.Band_idBand = B.idBand)
LEFT JOIN Composer C
ON R.Composer_idComposer = C.idComposer
They return all the records form the table on the left side and only the records from the table on the right side for which the join condition is met.
I also simplified the IIf cascade with the Nz function, which returns the second argument when the first one is null.
I also use short aliases for table names, thus simplifying the query further.

sql on mysql about join

the code below provide a result too much Infact i want to list the customer that never buy somethink How can i fix the code below
SELECT
webboard.listweb.id,
webboard.listweb.iditempro,
webboard.listweb.url,
webboard.listweb.useradddate,
webboard.listweb.expiredate,
webboard.prorecord.urlpostonweb
webboard.prorecord.urlpostonweb
FROM
webboard.listweb ,
webboard.prorecord
Where listweb.id Not In
(select webboard.prorecord.idlist From webboard.prorecord )
Using the syntax
FROM
webboard.listweb ,
webboard.prorecord
will perform a cartesian, or cross, join on the tables involved. So for every row in the table listweb all the rows in prorecord are displayed.
You need to use an INNER JOIN to only select the rows in listweb that have related rows in the prorecord table. What are the fields which identify the rows (your Primary Keys) and what is the name of the foreign key field in the prorecord table?
EDIT: Just re-read the question and comments and I see you want the rows in listweb which do not have an entry in prorecord
Your SELECT will then look like:
SELECT
webboard.listweb.id,
webboard.listweb.iditempro,
webboard.listweb.url,
webboard.listweb.useradddate,
webboard.listweb.expiredate,
webboard.prorecord.urlpostonweb
-- webboard.prorecord.urlpostonweb -- You have this field twice
FROM webboard.listweb LEFT JOIN webboard.prorecord
ON webboard.listweb.id = webboard.prorecord.idlist -- I'm guessing at the foreign key here
WHERE webboard.prorecord.idlist IS NULL