What exactly is wrong with those SQL-Queries - mysql

i am having a little trouble with this part in my lecture over SQL-Basics.
I think it is not very well explained and i am having a hard time finding any good information on the internet. In my book it says: "It is almost allways an error if two tuple attributes are not linked by an explicit join predicate: "
And then this example:
SELECT s.Name, c.Name AS Contact, c.Phone
FROM Suppliers AS s, ContactPersons AS c
WHERE s.Name = 'Shop Rite'
AND c.Phone LIKE '+49 351%'
No where is explained what an explicit join predicate is. For me this example just looks fine.
Beforehand there was a similair example:
SELECT s.Name, c.Name AS Contact, c.Phone
FROM Suppliers AS s, ContactPersons AS c
WHERE s.SuppID = c.SuppID
Which is an ok Join as the books says. I dont really understand what the difference is and what exactly is a JOIN-Predicate?
Also i am sorry for any grammer-mistakes (I am not a native speaker)
Thx in advance!

Implicit join syntax:
SELECT *
FROM Table1 , Table2
WHERE Table1.id = Table2.id
Explicit join syntax:
SELECT *
FROM Table1
JOIN Table2
ON Table1.id = Table2.id
Implicit syntax is fine and it will work, but it's not suggested. It is confusing syntax and may lead to many mistakes , mostly when dealing with more then two tables and when you need to LEFT JOIN (the stupid plus sign) . You should make it as a habit to use only the proper syntax of a join.
Here is an example for a query with 6 tables combining LEFT JOINs :
SELECT <columns>
FROM YourTable,AnotherTable,ThirdTable,FourthTable,AnotherTable2,AnotherTable3
WHERE YourTable.id = AnotherTable.id(+) AND
YourTable.sec_id = AnotherTable.Sec_Id(+) AND
AnotherTable.id (+) = ThirdTable.id(+) AND
YourTable.id = FourthTable.id AND
FourthTable.Date = ...
.......
As you can see, I didn't put even half of the conditions, assuming there could be a lot more conditions and it looks like crap.

Related

MySQL Query limiting results by sub table

I'm really struggling with this query and I hope somebody can help.
I am querying across multiple tables to get the dataset that I require. The following query is an anonymised version:
SELECT main_table.id,
sub_table_1.field_1,
main_table.field_1,
main_table.field_2,
main_table.field_3,
main_table.field_4,
main_table.field_5,
main_table.field_6,
main_table.field_7,
sub_table_2.field_1,
sub_table_2.field_2,
sub_table_2.field_3,
sub_table_3.field_1,
sub_table_4.field_1,
sub_table_4.field_2
FROM main_table
INNER JOIN sub_table_4 ON sub_table_4.id = main_table.id
INNER JOIN sub_table_2 ON sub_table_2.id = main_table.id
INNER JOIN sub_table_3 ON sub_table_3.id = main_table.id
INNER JOIN sub_table_1 ON sub_table_1.id = main_table.id
WHERE sub_table_4.field_1 = '' AND sub_table_4.field_2 = '0' AND sub_table_2.field_1 != ''
The query works, the problem I have is sub_table_1 has a revision number (int 11). Currently I get duplicate records with different revision numbers and different versions of sub_table_1.field_1 which is to be expected, but I want to limit the result set to only include results limited by the latest revision number, giving me only the latest sub_table_1_field_1 and I really can not figure it out!
Can anybody lend me a hand?
Many Thanks.
It's always important to remember that a JOIN can be on a subquery as well as a table. You could build a subquery that returns the results you want to see then, once you've got the data you want, join it in the parent query.
It's hard to 'tailor' an answer that's specific to you problem, as it's too obfuscated (as you admit) to know what the data and tables really look like, but as an example:
Say table1 has four fields: id, revision_no, name and stuff. You want to return a distinct list of name values, with their latest version of stuff (which, we'll pretend varies by revision). You could do this in isolation as:
select t.* from table1 t
inner join
(SELECT name, max(revision_no) maxr
FROM table1
GROUP BY name) mx
on mx.name = t.name
and mx.maxr = t.revision_no;
(Note: see fiddle at the end)
That would return each individual name with the latest revision of stuff.
Once you've got that nailed down, you could then swap out
INNER JOIN sub_table_1 ON sub_table_1.id = main_table.id
....with....
INNER JOIN (select t.* from table1 t
inner join
(SELECT name, max(revision_no) maxr
FROM table1
GROUP BY name) mx
on mx.name = t.name
and mx.maxr = t.revision_no) sub_table_1
ON sub_table_1.id = main_table.id
...which would allow a join with a recordset that is more tailored to that which you want to join (again, don't get hung up on the actual query I've used, it's just there to demonstrate the method).
There may well be more elegant ways to achieve this, but it's sometimes good to start with a simple solution that's easier to replicate, then simplify it once you've got the general understanding of the what and why nailed down.
Hope that helps - as I say, it's as specific as I could offer without having an idea of the real data you're using.
(for the sake of reference, here is a fiddle with a working version of the above example query)
In your case where you only need one column from the table, make this a subquery in your select clause instead of than a join. You get the latest revision by ordering by revision number descending and limiting the result to one row.
SELECT
main_table.id,
(
select sub_table_1.field_1
from sub_table_1
where sub_table_1.id = main_table.id
order by revision_number desc
limit 1
) as sub_table_1_field_1,
main_table.field_1,
...
FROM main_table
INNER JOIN sub_table_4 ON sub_table_4.id = main_table.id
INNER JOIN sub_table_2 ON sub_table_2.id = main_table.id
INNER JOIN sub_table_3 ON sub_table_3.id = main_table.id
WHERE sub_table_4.field_1 = ''
AND sub_table_4.field_2 = '0'
AND sub_table_2.field_1 != '';

Empty id in MySQL, cannot show null answers

I got this, and I want to get their "company" names for each one.
SELECT `client`.`name`,`client`.`lastname`
FROM `check`,`reserv`,`client`
WHERE `check`.`idReserv`=`reserv`.`id`
AND `reserv`.`idPerson`=`client`.`id`
ORDER BY `check`.`id`
, and I want to get their "company" names for each one, from table "company".
So I tried this:
SELECT `client`.`name`,`client`.`lastname`, `company`.`name`
FROM `check`,`reserv`,`client`,`company`
WHERE `reserv`.`idCompany`=`company`.`id`
AND `check`.`idReserv`=`reserv`.`id`
AND `reserv`.`idPerson`=`client`.`id`
ORDER BY `check`.`id`
but there is some people in the table "reserv" with an "idCompany" inexistent. so with that condition, this query only show me people who has an existent "id" in the table "company". I want to show the people with no company up and the space of company.name in blank if there is no company
I tryed many ways even with joins, but I cannot fix it. I'm tired to write "company" also.
You can use LEFT JOIN for this purpose like-
reserv r LEFT JOIN company c ON r.idCompany = c.id
You should use LEFT join instead.
SQL LEFT JOIN
SELECT c.name, c.lastname, co.name
FROM check AS ck
LEFT JOIN reserv AS r ON(ck.idReserv = r.id)
LEFT JOIN client AS c ON(r.idPerson = c.id)
LEFT JOIN company AS co ON(r.idCompany = co.id)
ORDER BY c.id
The ANSI 89 standard uses , notation for table joins with the criteria of the join being in the where clause. However I don't believe mySQL supports this outer style of join needed to address your problem. To express an outer join in this syntax you would need to use a *= for left join or =* for a right join; but again not sure mySQL supports it.
So in your case:
SELECT `client`.`name`,`client`.`lastname`, `company`.`name`
FROM `check`,`reserv`,`client`,`company`
WHERE `reserv`.`idCompany`*=`company`.`id`
AND `check`.`idReserv`=`reserv`.`id`
AND `reserv`.`idPerson`=`client`.`id`
ORDER BY `check`.`id`
However, I find that notation difficult to read and no need for all the escaping of table/column names (except reserved words)... so the below follows the ANSI 92 standards which allow for the use of INNER and LEFT Join syntax to explicitly define the type of join. Both notations should optimize to the same execution plan so either works (provided mySQL supports the *= notation) as well; it's just a matter of which standard you choose to use.
SELECT client.name
, client.lastname
, company.name
FROM `check`
INNER JOIN reserv
on `check`.idReserv=reserv.id
INNER JOIN client
on reserv.idPerson=client.id
LEFT JOIN company
on reserv.idCompany=company.id
ORDER BY `check`.id

Joining three tables together based on common ids

What I'm looking to generate is the wp_tylerposts table as it is, with the same specifications as the query (selecting only those with post_type='sponsor', but I'd like to reference the other two tables (object_id from wp_tylerterm_relationships and the corresponding name from wp_tylerterms) so that added on to wp_tylerpostswould be the corresponding name value from wp_tylerterms (added to the table would be "Gold Sponsors", for example).
Hopefully that makes sense. I'm sure there's a pretty simple solution, and I've tried my hand at some join queries without any luck... haven't done any of the stuff in a long time. Any help would be much appreciated!
Edit: I've come closer, I think, but I still can't retrieve the "name" column value from wp_tylerterms, here's what I have:
SELECT
c.ID, c.post_title, a.name
FROM
wp_tylerposts c
LEFT JOIN
wp_tylerterm_relationships b
ON
c.ID = b.object_id
LEFT JOIN
wp_tylerterms a
ON
b.object_id = a.term_id
WHERE
c.post_type = 'sponsor'
In second join, you use b.object_id instead of b.term_taxonomy_id. Your query should look like this:
SELECT
c.ID, c.post_title, a.name
FROM
wp_tylerposts c
LEFT JOIN
wp_tylerterm_relationships b
ON
c.ID = b.object_id
LEFT JOIN
wp_tylerterms a
ON
b.term_taxonomy_id = a.term_id
WHERE
c.post_type = 'sponsor'

Write SQL other optimized way

I have below query which I have written like below. Actually, I want to get two diff. colors from colors table. Please look into it and can you tell me It is optimized way? Can I write below query other optimized way?
SELECT d.*,
(SELECT c.clr_title FROM colors AS c WHERE c.id = d.base_color_id) AS base_color,
(SELECT c.clr_title FROM colors AS c WHERE c.id = d.overlay_color_id) AS overlay_color
FROM indira.dress AS d
WHERE id=669;
Thanks for your help.
Here's another way to get an equivalent result:
SELECT d.*
, b.clr_title AS base_color
, o.clr_title AS overlay_color
FROM indira.dress d
LEFT
JOIN colors b ON b.id = d.base_color_id
LEFT
JOIN colors o ON b.id = d.overlay_color_id
WHERE d.id=669
The correlated subqueries in the SELECT list can be expensive for large sets. But for returning a single row, that's not going to be a performance issue, since those subqueries will get executed only once.
In a more general case, for returning lots of rows, using a JOIN is usually more efficient.
You likely already have suitable indexes. For optimum performance, you'd want an index ON indira.dress(id) (likely already the primary key) and ON colors (id) (again, likely already the primary key). There's likely no performance benefit of adding a covering index.
Here is another option. I don't know what columns you do have on dress table so you will likely have to call the ones you out in select and group but this should work.
Not sure if it would be any faster/slower but wanted to give you more options ;-)
Here it is in sql fiddle where I also show what would happen if null was given for overlay. -> http://sqlfiddle.com/#!2/ebc82/3
SELECT
d.name
,MAX(CASE WHEN d.base_color_id = c.id THEN c.clr_title ELSE NULL END) base_color
,MAX(CASE WHEN d.overlay_color_id = c.id THEN c.clr_title ELSE NULL END) overlay_color
FROM
dress d
INNER JOIN colors c ON
c.Id IN (d.base_color_id, d.overlay_color_id)
WHERE
d.id = 669
GROUP BY
d.name
Since you're restricting to a single record, it's probably just fine. But you could always join against the color table twice, like:
select
d.*
,base_color.clr_title base_color
,overlay_color.clr_title overlay_color
from
indira.dress d
left join
colors base_color on d.base_color_id = base_color.id
left join
colors overlay_color on d.overlay_color_id = overlay_color.id
where
d.id = 669

Replacing Subqueries with Joins in MySQL

I have the following query:
SELECT PKID, QuestionText, Type
FROM Questions
WHERE PKID IN (
SELECT FirstQuestion
FROM Batch
WHERE BatchNumber IN (
SELECT BatchNumber
FROM User
WHERE RandomString = '$key'
)
)
I've heard that sub-queries are inefficient and that joins are preferred. I can't find anything explaining how to convert a 3+ tier sub-query to join notation, however, and can't get my head around it.
Can anyone explain how to do it?
SELECT DISTINCT a.*
FROM Questions a
INNER JOIN Batch b
ON a.PKID = b.FirstQuestion
INNER JOIN User c
ON b.BatchNumber = c.BatchNumber
WHERE c.RandomString = '$key'
The reason why DISTINCT was specified is because there might be rows that matches to multiple rows on the other tables causing duplicate record on the result. But since you are only interested on records on table Questions, a DISTINCT keyword will suffice.
To further gain more knowledge about joins, kindly visit the link below:
Visual Representation of SQL Joins
Try :
SELECT q.PKID, q.QuestionText, q.Type
FROM Questions q
INNER JOIN Batch b ON q.PKID = b.FirstQuestion
INNER JOIN User u ON u.BatchNumber = q.BatchNumber
WHERE u.RandomString = '$key'
select
q.pkid,
q.questiontext,
q.type
from user u
join batch b
on u.batchnumber = b.batchnumber
join questions q
on b.firstquestion = q.pkid
where u.randomstring = '$key'
Since your WHERE clause filters on the USER table, start with that in the FROM clause. Next, apply your joins backwards.
In order to do this correctly, you need distinct in the subquery. Otherwise, you might multiply rows in the join version:
SELECT q.PKID, q.QuestionText, q.Type
FROM Questions q join
(select distinct FirstQuestion
from Batch b join user u
on b.batchnumber = u.batchnumber and
u.RandomString = '$key'
) fq
on q.pkid = fq.FirstQuestion
As to whether the in or join version is better . . . that depends. In some cases, particularly if the fields are indexed, the in version might be fine.