JOIN statement not working with where clause - sql

I'm trying to do
select *
from buildings
where levels = 3
join managers;
but it says error at join. I want to match the the id of the building with the id in the managers table so I think I want natural join.

If you put the JOIN in the right place (between FROM and WHERE) and it were legal to write JOIN without ON then the result would be a cross join - a cartesian product that you then filter in WHERE.
That's a perfectly valid thing to do, though not probably what you intended in this case. It can be achieved by adding comma-separate tables to the FROM clause, eg:
FROM buildings, managers
It's generally better style to write an explicit inner join when you intend to join two tables on a condition:
SELECT *
FROM buildings b INNER JOIN managers m ON (b.manager_id = m.manager_id)
WHERE b.levels = 3;
... because it makes it clear to someone else reading the statement that the ON clause is a join condition, and the bl.levels=3 is a filter. The SQL implementation generally doesn't care, and quite likely transforms the above into:
SELECT *
FROM buildings b, managers m
WHERE b.levels = 3 AND b.manager_id = m.manager_id;
internally anyway, but it's easier (IMO) to understand complex queries with many joins when they're written using explicit join syntax.
There's another way to write what you want, but it's dangerous and IMO shouldn't be used:
SELECT *
FROM buildings bl
NATURAL JOIN managers m
WHERE bl.levels = 3;
That JOINs on any columns that're named the same. It's a nightmare to debug, you have to look up the table structures to understand what it does, it breaks if someone renames a column, and it's just painful. Do not use. See Table expressions in the PostgreSQL manual.
More acceptable is the USING syntax also discussed above:
SELECT *
FROM buildings bl
INNER JOIN managers m USING (manager_id)
WHERE bl.levels = 3;
which matches the columns named manager_id in both columns and JOINs on them, combining them into a single column, but unlike NATURAL JOIN does so explicitly and without scary magic.
I still prefer to write INNER JOIN ... ON (...) but it's reasonable to use USING and, IMO, never reasonable to use NATURAL.
Test table structures were:
create table managers ( manager_id integer primary key );
create table buildings (
manager_id integer references manager(manager_id),
levels integer
);

JOIN operator applied to the tables, you should provide it in the FROM clause. If you do so you will get a new error claiming that there is no JOIN condition, because you have to provide the JOIN condition after the ON clause (it is not optional):
SELECT *
FROM buildings b JOIN managers m ON b.managerid = m.id
WHERE b.levels = 3

if you want to do that, you have to write something like:
select *
from bulidings b
join managers m on b.id=m.id
where levels = 3
The request seems odd however, I believe that one of them should have a foreign key into the other. So a more appropriate query would be:
select *
from buidings b
join managers m on b.id = m.building_id //// or b.manager_id = m.id
where levels = 3

I think which query you are written is wrong.
So please try this
select * from buildings as bl
join managers as mn on bl.F_ManagerID = mn.ManagerID
where bl.levels = 3
I think this will help you...

Related

ORA-00904 Invalid Identifier for a query involving inner join

I have a very simple query that I am trying to execute:
select *
from submissions
inner join (
select *
from hackers
inner join challenges
on hackers.hacker_id = challenges.hacker_id
) dtable
on submissions.challenge_id = dtable.challenge_id
and submissions.hacker_id = dtable.hacker_id;
Oracle rejects it with:
ORA-00904: "DTABLE"."HACKER_ID": invalid identifier.
I have kept the alias dtable visible by keeping it outside the brackets. Why is Oracle rejecting my query?
SELECT * in your sub-query is a problem.
You have the same column name in both tables that are being joined. This means that you're trying to create an inline-view called dtable where at least two columns have the same name (in this case both tables have a hacker_id column and your use of * is essentially saying "use them both".). You can't do that.
You're going to need SELECT hackers.a, hackers.b, challenges.x, challenges.y, etc, etc in your sub-query. By being explicit in this way you can ensure that no two columns have the same name.
An alternative could be SELECT hackers.*, challenges.a AS c_a, challenges.b AS c_b, etc.
Either way you are being explicit about which fields to pick up, what their positions and names are, etc. The end result being that you can then avoid columns having the same name as other columns.
You do not need subqueries for this. Your query is not actually "simple". The simple form looks more like this:
select . . .
from submissions s join
hackers h
on s.hacker_id = h.hacker_id join
challenges c
on s.challenge_id = c.challenge_id;
Note that I removed the condition between challenge and hackers on hacker_id. That extra join condition doesn't really make sense to me (although it might make sense if you provided sample data).
As others have said: The sub-select selects two different columns hacker_id from two different tables. This confuses Oracle.
But there is no need for a sub-select
select * from
submissions
inner join challenges
on submissions.challenge_id = challenges.challenge_id
inner join hackers
on submissions.hacker_id = hackers.hacker_id;

SQL optimization (inner join or selects)

I have a dilema, i had a teacher that thought me basically that inner joins are hell (he reproved me because I missed the delivery of the final proyect by 3 mins...), now i have another that tells me that using just selects is inefficient, so I don't know what is white nor black... could someone enlighten me with their knowledge?
Joins
SELECT
NombreP AS Nombre,
Nota
FROM Lleva
INNER JOIN Estudiante ON CedEstudiante = Estudiante.Cedula
WHERE
Lleva.SiglaCurso='CI1312';
No Joins
SELECT
NombreP AS Nombre,
Nota
FROM (
SELECT
Nota,
CedEstudiante
FROM Lleva
WHERE
SiglaCurso='CI1312'
) AS Lleva, (
SELECT
NombreP,
Cedula
FROM Estudiante
) AS Estudiante
WHERE
CedEstudiante = Estudiante.Cedula;
So wich one is more efficient?
Lets re-write the code so it's easier to understand:
SELECT E.NombreP AS Nombre
,L.Nota
FROM Lleva L INNER JOIN Estudiante E ON L.CedEstudiante = E.Cedula
WHERE
L.SiglaCurso='CI1312';
A table using a subquery may look like this:
SELECT L.Nota
,(SELECT E.Nombre
FROM Estudiante E
WHERE E.Cedula = L.CedEstudiante
)
FROM Lleva L
WHERE
L.SiglaCurso='CI1312'
What you actually did in your original query was an implicit join. This is similar to inner join, without declaring the exact joining conditions. Implicit joins will attempt to join on similarly named columns between tables. Most programmers do not advise or use implicit join.
As for join versus subquery, they are applied in different situations.
They are not equivalent. Notice what I have put in bold below:
A subquery will guarantee 1 returned value or return NULL; if there are multiple values returned in the subquery you will get an error for returning more than 1 value and have to solve the problem with an aggregation perhaps (max value, top 1 value). Subqueries that return no match will return NULL without affecting the rest of the row.
The JOIN (INNER JOIN) operates differently. It can match rows and get the single value you're looking for just like a subquery. But a join can multiply returned rows if the joining conditions are not distinct (singular/non-repeating). This is why joins are usually done on Primary Keys (PK's). PK's are distinct by definition. The INNER JOIN will also remove rows if a joining condition doesn't occur between tables. This may be what your first professor was trying to explain- an INNER JOIN can work in many cases - similar to a subquery- but can also can return additional rows or remove rows from the output.

Access SQL subquery access field from parent

I have a SQL query that works in Access 2016:
SELECT
Count(*) AS total_tests,
Sum(IIf(score>=securing_threshold And score<mastering_threshold,1,0)) AS total_securing,
Sum(IIf(score>=mastering_threshold,1,0)) AS total_mastering,
total_securing/Count(*) AS percent_securing,
total_mastering/Count(*) AS percent_mastering,
(Count(*)-total_securing-total_mastering)/Count(*) AS percent_below,
subjects.subject,
students.year_entered,
IIf(Month(Date())<9,Year(Date())-students.year_entered+6,Year(Date())-students.year_entered+7) AS current_form,
groups.group
FROM
((subjects
INNER JOIN tests ON subjects.ID = tests.subject)
INNER JOIN (students
INNER JOIN test_results ON students.ID = test_results.student) ON tests.ID = test_results.test)
LEFT JOIN
(SELECT * FROM group_membership LEFT JOIN groups ON group_membership.group = groups.ID) As g
ON students.ID = g.student
GROUP BY subjects.subject, students.year_entered, groups.group;
However, I wish to filter out irrelevant groups before joining them to my table. The table groups has a column subject which is a foreign key.
When I try changing ON students.ID = g.student to ON students.ID = g.student And subjects.ID = g.subject I get the error 'JOIN expression not supported'.
Alternatively, when I try adding WHERE subjects.ID = groups.subject to the subquery, it asks me for the parameter value of subjects.ID, although it is a column in the parent query.
Googling reveals many similar errors but they were all resolved by changing the brackets. That didn't help.
Just in case the table relationships help:
Thank you.
EDIT: Sample database at https://www.dropbox.com/s/yh80oooem6gsni7/student%20tracker.ACCDB?dl=0
MS Access queries with many joins are difficult to update by SQL alone as parenetheses pairings are required unlike other RDBMS's and these pairings must follow an order. Moreover, some pairings can even be nested. Hence, for beginners it is advised to build queries with complex and many joins using the query design GUI in the MS Office application and let it build out the SQL.
For a simple filter on the g derived table, you could filter subject on the derived table, g, but likely you want want all subjects:
...
(SELECT * FROM group_membership
LEFT JOIN groups ON group_membership.group = groups.ID
WHERE groups.subject='Earth Science') As g
...
So for all subjects, consider re-building query from scratch in GUI that nearly mirrors your table relationships which actually auto-links joins in the GUI. Then, drop unneeded tables.
Usually you want to begin with the join table or set like groups and group_membership or tests and test_results. In fact, consider saving the g derived table as its own query.
Then add the distinct record primary source tables like students and subjects.
You may even need to play around with order in FROM and JOIN clauses to attain desired results, and maybe even add the same table in query. And be careful with adding join tables like group_membership (two one-to-many links), to GROUP BY queries as it leads to the duplicate record aggregation. So you may need to join aggregates queries by subject.
Unless you can post content of all tables, from our perspective it is difficult to help from here.
Your subquery g uses a LEFT JOIN, but there is a enforced 1:n relation between the two tables, so there will always be a matching group. Use a INNER JOIN instead.
With g.subject you are trying to join on a column that is on the right side of a left join, that cannot really work.
Also you shouldn't use SELECT * on a join of tables with identical column names. Include only the qualified column names that you need.
LEFT JOIN
(SELECT group_membership.student, groups.group, groups.subject
FROM group_membership INNER JOIN groups
ON group_membership.group = groups.ID) As g
ON (students.ID = g.student AND subjects.ID = g.subject)
I would call the columns in group_membership group_ID and student_ID to avoid confusion.
I don't have the database to test, but I would use subject table as subquery:
(SELECT * FROM subject WHERE filter out what you don't need) Subj
Then INNER JOIN this new Subj Table in your query which would exclude irrelevant groups.
Also I would never create join in WHERE clause (WHERE subjects.ID = groups.subject), what this does it creates cartesian product (table with all the possible combinations of subjects.ID and groups.subject) then it filters out records to satisfy your join. When dealing with huge data it might take forever or crash.
Error related to "Join expression may not be supported"; do datatypes match in those fields?
I solved it by (a lot of trial and error and) taking the advice here to make queries in the GUI and joining them. The end result is 4 queries deep! If the database was bigger, performance would be awful... but now its working I can tweak it eventually.
Thank you everybody!

using parenthesis in SQL

What's the differences between these SQLs:
SELECT *
FROM COURS NATURAL JOIN COMPOSITIONCOURS NATURAL JOIN PARCOURS;
SELECT *
FROM COURS NATURAL JOIN (COMPOSITIONCOURS C JOIN PARCOURS P ON C.IDPARCOURS = P.IDPARCOURS) ;
SELECT *
FROM (COURS NATURAL JOIN COMPOSITIONCOURS C) JOIN PARCOURS P ON C.IDPARCOURS = P.IDPARCOURS ;
They have different results.
It's difficult to tell precisely without a sample result set, but I would imagine its your utilization of natural JOINs which is typically bad practice. Reasons being:
I would avoid using natural joins, because natural joins are:
Not standard SQL.
Not informative. You aren't specifying what columns are being joined without referring to the schema.
Your conditions are vulnerable to schema changes. If there are multiple natural join columns and one such column is removed from a table, the query will still execute, but probably not correctly and this change in behavior will be silent.
Thanks for all of yours answers! I finally find out that the problem is because of the use of natural JoIn.
When using natural join, it will join every column with the same name! When I am using NATURAL JOIN for several times , it's possible those columns, which have same names but you don't wants to combine , could be joined automatically!
Here is an example,
Table a (IDa, name,year,IDb)
Table b (IDb, bonjour,salute,IDc)
Table c (IDc, year, merci)
If I wrote like From a Natural Join b Natural c,because IDb and IDc are common names for Table a,b,c. It seems Ok! But attention please!
a.year and b.year can also be joined ! That's what we don't want!

Optimizing for an OR in a Join in MySQL

I've got a pretty complex query in MySQL that slows down drastically when one of the joins is done using an OR. How can I speed this up? the relevant join is:
LEFT OUTER JOIN publications p ON p.id = virtual_performances.publication_id
OR p.shoot_id = shoots.id
Removing either condition in the OR decreases the query time from 1.5s to 0.1s. There are already indexes on all the relevant columns I can think of. Any ideas? The columns in use all have indexes on them. Using EXPLAIN I've discovered that once the OR comes into play MySQL ends up not using any of the indexes. Is there a special kind of index I can make that it will use?
This is a common difficulty with MySQL. Using OR baffles the optimizer because it doesn't know how to use an index to find a row where either condition is true.
I'll try to explain: Suppose I ask you to search a telephone book and find every person whose last name is 'Thomas' OR whose first name is 'Thomas'. Even though the telephone book is essentially an index, you don't benefit from it -- you have to search through page by page because it's not sorted by first name.
Keep in mind that in MySQL, any instance of a table in a given query can make use of only one index, even if you have defined multiple indexes in that table. A different query on that same table may use another index if the optimizer reasons that it's more helpful.
One technique people have used to help in situations like your is to do a UNION of two simpler queries that each make use of separate indexes:
SELECT ...
FROM virtual_performances v
JOIN shoots s ON (...)
LEFT OUTER JOIN publications p ON (p.id = v.publication_id)
UNION ALL
SELECT ...
FROM virtual_performances v
JOIN shoots s ON (...)
LEFT OUTER JOIN publications p ON p.shoot_id = s.id;
Make two joins on the same table (adding aliases to separate them) for the two conditions, and see if that is faster.
select ..., coalesce(p1.field, p2.field) as field
from ...
left join publications p1 on p1.id = virtual_performances.publication_id
left join publications p2 on p2.shoot_id = shoots.id
You can also try something like this on for size:
SELECT * FROM tablename WHERE id IN
(SELECT p.id FROM tablename LEFT OUTER JOIN publications p ON p.id IN virtual_performances.publication_id)
OR
p.id IN
(SELECT p.id FROM tablename LEFT OUTER JOIN publications p ON p.shoot_id = shoots.id);
It's a bit messier, and won't be faster in every case, but MySQL is good at selecting from straight data sets, so repeating yourself isn't so bad.