How is resolved tables visibility in sql join - sql

I have problems understanding the correct syntax of sql using explicit joins.
For example in the first snippet, in the second join "ec" is visible and the restriction works. But in the second snippet "op" cannot be resolved.
First snippet:
select im.*
from gestion_corte gc
join evento_corte ec
on ec.id_corte = gc.id_corte
join op_corte op
on op.id_corte = gc.id_corte
and op.fecha_evento = ec.fecha_evento
join img_corte im
on op.id_corte = im.id_corte
where ec.fecha_evento > '01092012'
Second snippet, "op" cannot be resolved in first join:
select im.*
from gestion_corte gc
join evento_corte ec
on ec.id_corte = gc.id_corte
and op.fecha_evento = ec.fecha_evento -- This condition has moved
join op_corte op
on op.id_corte = gc.id_corte
join img_corte im
on op.id_corte = im.id_corte
where ec.fecha_evento > '01092012'
In consequence, the visibility is resolved processing from top to bottom? are any other important thing to have in consideration?

In the SQL join syntax, an alias is not known until it is defined. So, you can do:
from A a join
B b
on a.val = b.val join
C c
on a.val = c.val
However, you cannot do:
from A a join
B b
on a.val = b.val and
a.id = c.id join
C c
on a.val = c.val
The SQL engine simply does not know what "c" is, because "c" has not been seen yet in the from statement.
You second query is easy to fix by moving the condition, as you discovered.

A join is between two virtual tables.
The join predicate can only refer to columns from these two virtual tables (though each virtual table may consist of columns from multiple base tables).
It is not necessarily the case that the order of valid usage is top down as the logical join order is determined by the order of the ON clauses. So for example in the following query (SQL Fiddle) the virtual table (a join b) is joined onto the virtual table (c join d join e)
SELECT *
FROM a
JOIN b
ON a.id = b.id /*Only a and b in scope here*/
JOIN c
JOIN d
ON c.id = d.id /*Only c and d in scope here*/
JOIN e
ON e.id = d.id /*Only c, d, e in scope here*/
ON d.id = a.id /*a,b,c,d,e all in scope*/
This query (SQL Fiddle) would give an error as a is not in scope there.
SELECT *
FROM a
JOIN b
ON a.id = b.id
JOIN c
JOIN d
ON c.id = d.id
JOIN e
ON e.id = a.id -- Can't reference a here
ON d.id = a.id

I don't get your second question, so I have only answer to first one.
Your joins can be imagined this way:
"result" = select "some columns" from "any source"
"any source" can be a table or a subquery (i.e. another "result") or "any source" join "any source":
"any source" = table | "result" | "any source" join "any source"
So if you want a "result" you should determine "any source" first then "some columns" (let's don't focus on it). Let's go to the "any source" definition to determine it. Table is determined, "result" recursively leads to prevprevious sentence and join construction has a rule - to determine it firstly get left "any source" then right.
join is a left associative operation and isn't a commutative operation generally (inner join and cross join are, but not the left join or right join).
So, look at your 2nd snippet. This part gestion_corte join evento_corte hasn't been calculated because of op_corte hasn't been determined yet

Related

How to join Multiple

This is the code example
for three tables and I made a link on them
Now I want to add the age column from table D
SELECT A.COD,a.namee, B.NAMEE,C.NAMEE
FROM ((A INNER JOIN B ON A.COD = B.COD)
LEFT JOIN C ON A.COD = C.COD)
I mean, this code is expected
SELECT A.COD, a.name , B.NAME,C.NAME ,D.Age
FROM ((A INNER JOIN B ON A.COD = B.COD)
LEFT JOIN C ON A.COD = C.COD) , D
But in access, an error message appears, the text of the message says that the JOIN method is not supported
Is there a way to solve this?
Access does not allow to do a direct cross-join (operator ",") involving an SQL expression that includes a different type of join. The solution is as simple as enclosing the first operation between parentheses and add another SELECT, as follows:
SELECT T_A.COD, T_A.Name_ , T_B.Name_, T_C.Name_, T_D.Age
FROM
(
SELECT T_A.COD, T_A.Name_ , T_B.Name_, T_C.Name_
FROM
( T_A
INNER JOIN
T_B
ON T_A.COD = T_B.COD)
LEFT JOIN
T_C
ON T_A.COD = T_C.COD
)
, T_D
My advice is that you always enclose in a SELECT every individual join operation (with the exception of cross-joins) as good programming practice. There is no problem in doing a series of cross-joins because the cross-join operator is associative.

How to include column values as null even when condition is not met?

Write a query to show ALL building names, their metering company name and meter type for all buildings that do not have postpaid meters.
The image 1 is the result that I should get and image 2 is the results that i am getting:
USE Ultimate_DataBase
GO
SELECT [Bld_Name], [Elec_company_name], [Mtype_Name]
FROM [dbo].[Metering_Company] A
FULL OUTER JOIN [dbo].[Metering_Type] D
ON A.[MType_ID]= D.MType_ID
FULL OUTER JOIN [dbo].[Building_metering] B
ON A.[Elec_ID]= B.[Elec_ID]
FULL OUTER JOIN [dbo].[Building] C
ON C.[Bld_ID]= B.[Bld_ID]
WHERE [Mtype_Name] != 'POSTPAID'
Try moving the WHERE logic to the corresponding ON clause:
SELECT [Bld_Name], [Elec_company_name], [Mtype_Name]
FROM [dbo].[Metering_Company] A
FULL OUTER JOIN [dbo].[Metering_Type] D
ON A.[MType_ID]= D.MType_ID AND
[Mtype_Name] != 'POSTPAID' -- change is here
FULL OUTER JOIN [dbo].[Building_metering] B
ON A.[Elec_ID]= B.[Elec_ID]
FULL OUTER JOIN [dbo].[Building] C
ON C.[Bld_ID]= B.[Bld_ID];
Note: Please add aliases to your select clause. They are not mandatory, assuming no two tables ever have columns by the same name, but just having aliases would have made your question easier to answer.
FULL JOIN isn't seem necessary -- in fact FULL JOIN is almost never needed, and especially not for routine JOINs in a well-structured database.
The structure of the question suggests NOT EXISTS:
SELECT b.*
FROM dbo.Building b
WHERE NOT EXISTS (SELECT 1
FROM dbo.Building_metering bm JOIN
dbo.Metering_Company mc
ON bm.Elec_ID = mc.Elec_ID JOIN
dbo.Metering_Type mt
ON mt.MType_ID = mc.MType_ID
WHERE bm.Bld_ID = b.Bld_ID AND mt.Mtype_Name = 'POSTPAID'
);
You can also express this as a LEFT JOIN and filtering:
SELECT b.*
FROM dbo.Building b LEFT JOIN
dbo.Building_metering bm
ON bm.Bld_ID = b.Bld_ID LEFT JOIN
dbo.Metering_Company mc
ON bm.Elec_ID = mc.Elec_ID LEFT JOIN
dbo.Metering_Type mt
ON mt.MType_ID = mc.MType_ID AND
mt.Mtype_Name = 'POSTPAID'
WHERE mt.MType_ID IS NULL;
This allows you to select columns from any of the tables.
Notes:
FULL JOIN is almost never needed.
Use meaningful table aliases! Arbitrary letters mean nothing. Use table abbreviations.
Escaping column and table names with square braces just makes code harder to write and to read.
USE Ultimate_DataBase
GO
SELECT [Bld_Name], [Elec_company_name], [Mtype_Name]
FROM [dbo].[Metering_Company] A
LEFT JOIN [dbo].[Metering_Type] D
ON A.[MType_ID]= D.MType_ID
LEFT JOIN [dbo].[Building_metering] B
ON A.[Elec_ID]= B.[Elec_ID]
LEFT JOIN [dbo].[Building] C
ON C.[Bld_ID]= B.[Bld_ID]
Use this

join graph disconnect ORACLE

The query below won't run as I have a join graph disconnect. As far as I understand when you alias a table you have to put it first in the join condition, but when i do so I still get the same error, any suggestions?
select
date1.LABEL_YYYY_MM_DD as Some_Label1,
A.Some_Field as Some_Label2,
round(sum(D.Some_Field3)/count (*),0)as Some_Label3
from Table_A A
inner JOIN Table_B B ON (A.some_key = B.some_key)
inner JOIN date_time date1 ON (A.START_DATE_TIME_KEY = date1.DATE_TIME_KEY)
left outer join Table_C C on(C.some_GUID = A.some_ID)
left outer join Table_D D on(D.a_ID = C.a_ID)
where
(1=1)
and date1.LABEL_YYYY_MM_DD ='2015-03-30'
and D.blah ='1'
group by
date1.LABEL_YYYY_MM_DD,
A.Some_Field
order by
date1.LABEL_YYYY_MM_DD
;
Based on my comment above I should change the first inner join to B.some_key = A.some_key and so on however I still get the disconnect...
according to the Oracle documentation (http://docs.oracle.com/javadb/10.8.3.0/ref/rrefsqlj18922.html) there is space required between ON and the boolean expression

Oracle left outer join stops returning results when 2-table condition added

I have an oracle sql query with a whole string of inner and left joins. However, when I add the last left join condition, it stops returning results. This may end up being a simple answer like "you can't outer join on columns from 2 tables" but I can't find any such rule for oracle, and plenty of examples showing the opposite. The sql query is:
FROM a, b,
c, d,
e, f,
g, h,
(SELECT id5 FROM
some_table WHERE
conditions) i,
(SELECT id7, type FROM
some_other_table WHERE
conditions) j
WHERE b.time in (range) AND
b.count <> 0 AND
b.id1 = e.id1 AND
e.type = g.type AND
g.type2 = f.type2 AND
b.id2 = a.id2(+) AND
b.time = a.time(+) AND
b.id3 = c.id3(+) AND
b.time = c.time(+) AND
c.id4 = d.id4(+) AND
c.time = d.time(+) AND
c.id5 = i.id5(+) AND
c.time = h.time(+) AND
c.id6 = h.id6(+) AND
h.id7 = j.id7(+); --AND
--e.type = j.type(+);
When I uncomment the final condition, no results are returned. Since this is supposed to be an outer join, that shouldn't happen. So, something in here must be making it not act like an outer join?
Is there a typo or error in here somewhere? Is there an oracle rule I am breaking? Anything that could be solved by switching to ANSI join format?
Thanks
Either you've missed something in translating your query to the simple form or I've missed something in my manipulation, but it looks like the query may not be doing what you think it should. Rewriting in standard ANSI form is more revealing:
FROM a
right outer join b
on b.id2 = a.id2
AND b.time = a.time
right outer join c
on c.id3 = b.id3
AND c.time = b.time
left outer join d
on d.id4 = c.id4
AND d.time = c.time
join e
on e.id1 = b.id1
cross join f
join g
on g.type = e.type
AND g.type2 = f.type2
left outer join h
on h.time = c.time
AND h.id6 = c.id6
left outer join(
SELECT id5 FROM
some_table WHERE
conditions) i
on i.id5 = c.id5
left outer join(
SELECT id7, type FROM
some_other_table WHERE
conditions) j
on j.id7 = h.id7
and j.type = e.type --> the criteria in question
where b.time in (range)
AND b.count <> 0;
Does this look right to you? You don't mention the RIGHT OUTER joins but I'm hoping you just forgot. You do mention the INNER joins, but table f has no join criteria at all so I've used a CROSS join, hoping here also that this is your intention.
Is the join criteria for table e as it should be? According to the pattern you have set, I would expect to see "id5" here instead of "id1". Of course, you have changed all the names to submit a simplified example, so this may be meaningless. So the first thing I would suggest is that you rewrite your original code to the ANSI form like I did, using the real table and column names. You may see something.
You are correct in that adding the marked criteria should have no effect on the number of rows in the result set. That being the case, there is something else going on.
To find out what, comment out the entire last join. If you see something screwy, keep commenting out tables to get to where the problem occurs. If everything looks good, execute just the nested query that forms "table" j. I can't think of anything it might contain that could cause the situation as you explain it, but examine it anyway.
Finally, if all else fails, form queries with just tables e and j and then with just tables h and j (with their corresponding join criteria). See what happens.
Then get back here and explain to us how the problem was somewhere else the whole time. :)
The problem (as suggested in the comments on the question) was that h.id7 = j.id7(+) AND e.type = j.type(+) is functionally equivalent to a.id1 = b.id1(+) and c.id2 = b.id2(+). The oracle join syntax is not precise enough to see that, due to other join conditions, this is not a full outer join, so this is not allowed.
This should have caused an error, but something else in the query suppressed the error, not clear to me how that happened. In any case, switching to ANSI format allowed me to more precisely specify the join conditions, and the query worked as expected.

Alternatives to full outer join for logical OR in tree structure query

I hope the title is clear enough. I've been implementing logical AND/OR for tree structures which are kept in the database, using a simple nodes and a parent-child association table.
A sample tree has a structure like this:
A sample tree structure query is as follows:
The double lines in the query pattern mean that A has a child of type B (somewhere down its child nodes) OR C. I have implemented A -> HASCHILD -> C -> HASCHILD -> E with an inner join, and A -> HASCHILD -> B -> HASCHILD -> E is implemented like this.
The trick is joining these two branches on A. Since this is an OR operation, either B branch or C branch may not exist. The only method I could think of if to use full outer joins of two branches with A's node_id as the key. To avoid details, let me give this simplified snippet from my SQL query:
WITH A as (....),
B as (....),
C as (....),
......
SELECT *
from
A
INNER JOIN A_CONTAINS_B ON A.NODE_ID = A_CONTAINS_B.parent
INNER JOIN B ON A_CONTAINS_B.children #> ARRAY[B.NODE_ID]
INNER JOIN .....
full OUTER JOIN -- THIS IS WHERE TWO As ARE JOINED
(select
A2.NODE_ID AS A2_NODE_ID
from
A2
INNER JOIN A_CONTAINS_C ON A2.NODE_ID = C_CONTAINS_C.parent
INNER JOIN C ON A_CONTAINS_C.children #> ARRAY[C.NODE_ID]
INNER JOIN ....)
as other_branch
ON other_branch.A2_NODE_ID = A.NODE_ID
This query links two As which actually represent the same A using node_id, and if B or C does not exist, nothing breaks.
The result set has duplicates of course, but I can live with that. I can't however think of another way to implement OR in this context. ANDs are easy, they are inner joins, but left outer join is the only approach that lets me connect As. UNION ALL with dummy columns for both branches is not an option because I can't connect As in that case.
Do you have any alternatives to what I'm doing here?
UPDATE
TokenMacGuy's suggestion gives me a cleaner route than what I have at the moment. I should have remembered UNION.
Using the first approach he has suggested, I can apply a query pattern decomposition, which would be a consistent way of breaking down queries with logical operators. The following is a visual representation of what I'm going to do, just in case it helps someone else visualize the process:
This helps me do a lot of nice things, including creating a nice result set where query pattern components are linked to results.
I've deliberately avoided details of tables or other context, because my question is about how to join results of queries. How I handle the hierarchy in DB is a different topic which I'd like to avoid. I'll add more details into comments. This is basically an EAV table accomponied by a hierarchy table. Just in case someone would like to see it, here is the query I'm running without any simplifications, after following TokenMacGuy's suggestion:
WITH
COMPOSITION1 as (select comp1.* from temp_eav_table_global as comp1
WHERE
comp1.actualrmtypename = 'COMPOSITION'),
composition_contains_observation as (select * from parent_child_arr_based),
OBSERVATION as (select obs.* from temp_eav_table_global as obs
WHERE
obs.actualrmtypename = 'OBSERVATION'),
observation_cnt_element as (select * from parent_child_arr_based),
OBS_ELM as (select obs_elm.* from temp_eav_table_global as obs_elm
WHERE
obs_elm.actualrmtypename= 'ELEMENT'),
COMPOSITION2 as (select comp_node_tbl2.* from temp_eav_table_global as comp_node_tbl2
where
comp_node_tbl2.actualrmtypename = 'COMPOSITION'),
composition_contains_evaluation as (select * from parent_child_arr_based),
EVALUATION as (select eva_node_tbl.* from temp_eav_table_global as eva_node_tbl
where
eva_node_tbl.actualrmtypename = 'EVALUATION'),
eval_contains_element as (select * from parent_child_arr_based),
ELEMENT as (select el_node_tbl.* from temp_eav_table_global as el_node_tbl
where
el_node_tbl.actualrmtypename = 'ELEMENT')
select
'branch1' as branchid,
COMPOSITION1.featuremappingid as comprootid,
OBSERVATION.featuremappingid as obs_ftid,
OBSERVATION.actualrmtypename as obs_tn,
null as ev_ftid,
null as ev_tn,
OBS_ELM.featuremappingid as obs_elm_fid,
OBS_ELm.actualrmtypename as obs_elm_tn,
null as ev_el_ftid,
null as ev_el_tn
from
COMPOSITION1
INNER JOIN composition_contains_observation ON COMPOSITION1.featuremappingid = composition_contains_observation.parent
INNER JOIN OBSERVATION ON composition_contains_observation.children #> ARRAY[OBSERVATION.featuremappingid]
INNER JOIN observation_cnt_element on observation_cnt_element.parent = OBSERVATION.featuremappingid
INNER JOIN OBS_ELM ON observation_cnt_element.children #> ARRAY[obs_elm.featuremappingid]
UNION
SELECT
'branch2' as branchid,
COMPOSITION2.featuremappingid as comprootid,
null as obs_ftid,
null as obs_tn,
EVALUATION.featuremappingid as ev_ftid,
EVALUATION.actualrmtypename as ev_tn,
null as obs_elm_fid,
null as obs_elm_tn,
ELEMENT.featuremappingid as ev_el_ftid,
ELEMENT.actualrmtypename as ev_el_tn
from
COMPOSITION2
INNER JOIN composition_contains_evaluation ON COMPOSITION2.featuremappingid = composition_contains_evaluation.parent
INNER JOIN EVALUATION ON composition_contains_evaluation.children #> ARRAY[EVALUATION.featuremappingid]
INNER JOIN eval_contains_element ON EVALUATION.featuremappingid = eval_contains_element.parent
INNER JOIN ELEMENT on eval_contains_element.children #> ARRAY[ELEMENT.featuremappingid]
the relational equivalent to ∨ is &Union;. You could either use union to combine a JOIN b JOIN e with a JOIN c JOIN e or just use the union of b and c and join on the resulting, combined relation, something like a JOIN (b UNION c) JOIN e
More completely:
SELECT *
FROM a
JOIN (
SELECT
'B' source_relation,
parent,
b.child,
b_thing row_from_b,
NULL row_from_c
FROM a_contains_b JOIN b ON a_contains_b.child = b.node_id
UNION
SELECT
'C',
parent
c.child,
NULL,
c_thing
FROM a_contains_c JOIN c ON a_contains_c.child = c.node_id
) a_c ON A.NODE_ID = a_e.parent
JOIN e ON a_c.child = e.node_id;