So the code has multiple join statements, and all I need to add is a where clause that checks one value. The table I need to reference is not used in the From statement which includes multiple joins. Is there anyway to add a regular FROM statement without disrupting the JOINS
SELECT D.data
H.otherData
FROM
Dada AS D
JOIN Haha AS H
ON D.ID = H.ID
WHERE
C.Value = x --This is what I want to do. Can I add another from statement that doesn't affect the join statement
Not possible without including C somewhere.
What you're saying is equivalent to "List all the Countries where the Person is called Tom"
You can add C table, but it has to be with a JOIN. Maybe you want a CROSS JOIN, which is the cartesian product of the joining tables against this new table C and doesn't require any condition (you can filter the results with a WHERE condition).
SELECT D.data
H.otherData
FROM
Dada AS D
JOIN Haha AS H
ON D.ID = H.ID
CROSS JOIN C
WHERE
C.Value = x
In this particular, case it would be the same as:
SELECT D.data
H.otherData
FROM
Dada AS D
JOIN Haha AS H
ON D.ID = H.ID
JOIN C
ON C.Value = x
If you want to filter the first JOIN depending on a value from C then you need to put the condition on a subquery on the WHERE, like an IN or EXISTS. There has to be some correlation (a column or expression that both share) between your first JOIN and this table C, otherwise there is no point in filtering.
For example:
SELECT D.data
H.otherData
FROM
Dada AS D
JOIN Haha AS H
ON D.ID = H.ID
WHERE EXISTS (SELECT 'value exists on table C' FROM C WHERE C.Value = D.data)
Related
This is the code:
SELECT c.ChapterNo, c.Title, l.LessonNo, l.Title
FROM Chapter c
LEFT JOIN Lesson l ON c.ChapterNo = l.ChapterNo
but i want to filter the Chapter Table by Chapter's Description A. I already used this:
SELECT c.ChapterNo, c.Title, l.LessonNo, l.Title
FROM Chapter c
LEFT JOIN Lesson l ON c.ChapterNo = l.ChapterNo
WHERE c.Desc = 'geometry'
and this
SELECT c.ChapterNo, c.Title, l.LessonNo, l.Title
FROM Chapter c
LEFT JOIN Lesson l ON c.ChapterNo = l.ChapterNo AND c.Desc = 'geometry'
but it does not work. I dont know why...
It does not work because you need to put the condition in the where clause:
SELECT c.ChapterNo, c.Title, l.LessonNo, l.Title
FROM Chapter c LEFT JOIN
Lesson l
ON c.ChapterNo = l.ChapterNo
WHERE c.Desc = 'geometry';
The rules for a left join are simple: Keep all the rows in the first table regardless of whether or not the WHERE clause evaluates to true. So, a filter on the first table does not reduce the number of rows.
I've come across some weird behavior that's preventing me from setting up a query in the way I'd like to. Would appreciate any ideas. If there's any information about the tables that would be helpful, please let me know. I looked through them and nothing jumped out at me that might cause this but nor did I know what I was looking for. Here is the behavior.
This works fine:
Select *
From
SCHEMA_A.TABLE_A a,
SCHEMA_B.TABLE_B b,
SCHEMA_C.TABLE_C c,
SCHEMA_A.TABLE_D d
Where
b.friend_id = c.friend_id
AND a.group_id = d.group_id
AND b.group_cd = d.group_cd
But this returns ORA-00904: b.friend_id = c.friend_id: invalid identifier
Select *
From
SCHEMA_A.TABLE_A a,
SCHEMA_B.TABLE_B b,
SCHEMA_A.TABLE_D d
Join
SCHEMA_C.TABLE_C c
On
b.friend_id = c.friend_id
Where
a.group_id = d.group_id
AND b.group_cd = d.group_cd
This returns ORA-00904: b.group_cd = d.group_cd: invalid identifier
Select *
From
SCHEMA_A.TABLE_A a,
SCHEMA_B.TABLE_B b
Join
SCHEMA_C.TABLE_C c
On
b.friend_id = c.friend_id
Join
SCHEMA_A.TABLE_D d
On
a.group_id = d.group_id
AND b.group_cd = d.group_cd
And this works again:
Select *
From
SCHEMA_A.TABLE_A a,
SCHEMA_B.TABLE_B b
Join
SCHEMA_C.TABLE_C c
On
b.friend_id = c.friend_id
Join
SCHEMA_A.TABLE_D d
On
b.group_cd = d.group_cd
Where
a.group_id = d.group_id
Try with the using keyword which is designed for join on same column name
Select *
From
SCHEMA_A.TABLE_A a,
SCHEMA_B.TABLE_B b
Join
SCHEMA_C.TABLE_C USING(friend_id)
Join
SCHEMA_A.TABLE_D d using(group_id)
where b.group_cd = d.group_cd;
Also make sure that you execute the other query with the right users, as an user that does not have correct permission will throw invalid identifier.
Edit : The actual problem is that you are joining TABLE_C with TABLE_D but the join condition refer to TABLE_B change it to
Select *
From
SCHEMA_A.TABLE_A a,
SCHEMA_A.TABLE_D d,
SCHEMA_B.TABLE_B b
Join
SCHEMA_C.TABLE_C c using(friend_id)
Where
a.group_id = d.group_id
AND b.group_cd = d.group_cd;
Apply the same logic for other queries, when doing a join, you do not join to a group of table, but to the last table mentionned in the from clause therefore it did not have access to TABLE_B.
For other people that might have an invalid identifier error, make sure your database is not case sensitive, else make sure to use the right case.
I believe that you have run into the intricacy in SQL dealing with join scoping/precedence. Since I am not myself a database programmer, I can't really tell you why it works the way it does, but I do see some logic in the way the query parser is behaving.
In your first query, all the joins are cross joins. As a result, they should have the same precedence. So all the joined columns are known when the join is evaluated.
In the second query, you have a combination of cross joins and inner joins. If we assume that inner join has precedence over cross joins, then table c and d are joined before any other tables are added to the mix. So when you reference b.friend_id in the inner join, which is outside of the inner join, the query parser isn't able to use that column in evaluating the join.
In the third query, the inner join between tables b, c and d take precedence over the cross join. So column a.group_id isn't available in evaluating the inner join condition. When you take table a out of the inner join condition in the final query, the query no longer has a conflicting precedence.
The problem here is the mixing of join types. When you use comma-separated table in the FROM clause, each term is evaluated individually. Take the following example:
From
SCHEMA_A.TABLE_A a,
SCHEMA_B.TABLE_B b,
SCHEMA_A.TABLE_D d
Join
SCHEMA_C.TABLE_C c
On
b.friend_id = c.friend_id
The database is trying to resolve SCHEMA_A.TABLE_D d Join SCHEMA_C.TABLE_C c On b.friend_id = c.friend_id. Within this scope, b has no meaning (it's a separate term).
For your third version, I get "A"."GROUP_ID": invalid identifier, which is also in-line with this explanation.
Ultimately the lesson you should take from this is not to mix join types.
I have a query that is running against a SQLite database that uses a couple of subqueries. In order to accommodate some new requirements, I need to translate it to use joins instead. Below is the structure version of the original query:
SELECT c.id AS category_id, b.budget_year,
(
SELECT sum(actual)
FROM lines l1
WHERE status = 'complete'
AND category_id = c.id
AND billing_year = b.budget_year
) AS actual
(
SELECT sum(planned)
FROM lines l2
WHERE status IN ('forecasted', 'in-progress')
AND category_id = c.id
AND billing_year = b.budget_year
) AS rough_proposed
FROM categories AS c
LEFT OUTER JOIN budgets AS b ON (c.id = b.category_id)
GROUP BY c.id, b.budget_year;
The next query is my first attempt to convert it to use LEFT OUTER JOINs:
SELECT c.id AS category_id, b.budget_year, sum(l1.actual) AS actual, sum(l2.planned) AS planned
FROM categories AS c
LEFT OUTER JOIN budgets AS b ON (c.id = b.category_id)
LEFT OUTER JOIN lines AS l1 ON (l1.category_id = c.id
AND l1.billing_year = b.budget_year
AND l1.status = 'complete')
LEFT OUTER JOIN lines AS l2 ON (l2.category_id = c.id
AND l2.billing_year = b.budget_year
AND l2.status IN ('forecasted', 'in-progress'))
GROUP BY c.id, b.budget_year;
However, the actual and rough_proposed columns are much larger than expected. I am no SQL expert, and I am having a hard time understanding what is going on here. Is there a straightforward way to convert the subqueries to joins?
There is a problem with both your queries. However, the first query hides the problem, while the second query makes it visible.
Here is what's going on: you join lines twice - once as l1 and once more as l2. The query before grouping would have the same line multiple times when there are both actual lines and forecast-ed / in-progress lines. When this happens, each line would be counted multiple times, resulting in inflated values.
The first query hides this, because it does not apply aggregation to actual and rough_proposed columns. SQLite picks the first entry for each group, which has the correct value.
You can fix your query by joining to lines only once, and counting the amounts conditionally, like this:
SELECT
c.id AS category_id
, b.budget_year
, SUM(CASE WHEN l.status = 'complete' THEN l.actual END) AS actual
, SUM(CASE WHEN l.status IN ('forecasted', 'in-progress') THEN l.planned END) AS planned
FROM categories AS c
LEFT OUTER JOIN budgets AS b ON (c.id = b.category_id)
LEFT OUTER JOIN lines AS l ON (l.category_id = c.id AND l1.billing_year = b.budget_year)
GROUP BY c.id, b.budget_year
In this new query each row from lines is brought in only once; the decision to count it in one of the actual/planned columns is made inside the conditional expression embedded in the SUM aggregating function.
In this question it's finally clicked how to write joins between multiple tables, where they link in a line e.g.
Table A - Table B - Table C
Where Table A references Table B, and Table B references Table C and so on.
What I still don't understand is how to reference the situation where Table A references Table B as above and also reference Table D.
In implicit Joins I can get the following to work, but want to move it to explicits...
SELECT a.name, b.office, c.firm, d.status
FROM job a, depts b, firms c, statuses d
WHERE a.office = b.ref
AND b.firm = c.ref
AND a.status = d.ref
Any tips?
SELECT
a.name,
b.office,
c.firm,
d.status
FROM
job a
JOIN depts b ON a.office = b.ref
JOIN firms c ON b.firm = c.ref
JOIN statuses d ON a.status = d.ref
That's as detailed as I could get on such an obscure question. You didn't describe what exactly does "link" mean in your case. So I don't know, maybe you need left join.
I have three tables: R, S and P.
Table R Joins with S through a foreign key; there should be at least one record in S, so I can JOIN:
SELECT
*
FROM
R
JOIN S ON (S.id = R.fks)
If there's no record in S then I get no rows, that's fine.
Then table S joins with P, where records is P may or may not be present and joined with S.
So I do
SELECT
*
FROM
R
JOIN S ON (S.id = R.fks)
LEFT JOIN P ON (P.id = S.fkp)
What if I wanted the second JOIN to be tied to S not to R, like if I could use parentheses:
SELECT
*
FROM
R
JOIN (S ON (S.id = R.fks) JOIN P ON (P.id = S.fkp))
Or is that already a natural behaviour of the cartesian product between R, S and P?
All kinds of outer and normal joins are in the same precedence class and operators take effect left-to-right at a given nesting level of the query. You can put the join expression on the right side in parentheses to cause it to take effect first. Remember that you will have to move the ON clauses around so that they stay with their joins—the join in parentheses takes its ON clause with it into the parentheses, so it now comes textually before the other ON clause which will be after the parentheses in the outer join statement.
(PostgreSQL example)
In
SELECT * FROM a LEFT JOIN b ON (a.id = b.id) JOIN c ON (b.ref = c.id);
the a-b join takes effect first, but we can force the b-c join to take effect first by putting it in parentheses, which looks like:
SELECT * FROM a LEFT JOIN (b JOIN c ON (b.ref = c.id)) ON (a.id = b.id);
Often you can express the same thing without extra parentheses by moving the joins around and changing the direction of the outer joins, e.g.
SELECT * FROM b JOIN c ON (b.ref = c.id) RIGHT JOIN a ON (a.id = b.id);
When you join the third table, your first query
SELECT
*
FROM
R
JOIN S ON (S.id = R.fks)
is like a derived table to which you're joining the third table. So if R JOIN S produces no rows, then joining P will never yield any rows (because you're trying to join to an empty table).
So, if you're looking for precedence rules then in this case it's just set by using LEFT JOIN as opposed to JOIN.
However, I may be misunderstanding your question, because if I were writing the query, I would swap S and R around. eg.
SELECT
*
FROM
S
JOIN R ON (S.id = R.fks)
The second join is tied to S as you explicity state JOIN P ON (P.id = S.fkp) - no column from R is referenced in the join.
with a as (select 1 as test union select 2)
select * from a left join
a as b on a.test=b.test and b.test=1 inner join
a as c on b.test=c.test
go
with a as (select 1 as test union select 2)
select * from a inner join
a as b on a.test=b.test right join
a as c on b.test=c.test and b.test=1
Ideally, we would hope that the above two queries are the same. However, they are not - so anybody that says a right join can be replaced with a left join in all cases is wrong. Only by using the right join can we get the required result.