When joining two tables, what are the difference between the two blocks below and which is the better approach?
Pattern A:
SELECT ...
FROM A
INNER JOIN B
ON A.PK = B.FK
WHERE 1=1
AND A.Name = "Foo"
AND B.Title = "Bar"
Pattern B:
SELECT ...
FROM A
INNER JOIN B
ON A.PK = B.FK
AND B.Title = "Bar"
WHERE 1=1
AND A.Name = "Foo"
This is going to differ from person to person, but I think that Pattern A is better.
What it does is it separates the table level joins from the filters. This could be helpful for queries with multiple joins and multiple filters because it clearly separates the two types of joining that is going on.
I prefer pattern A, but there is probably not any difference. You really have to look at the execution plan though to make sure it is running efficiently.
If you replace INNER JOIN with OUTER JOIN, there will be difference.
Otherwise, these queries:
SELECT ...
FROM A
INNER JOIN
B
ON A.PK = B.FK
WHERE A.Name = "Foo"
AND B.Title = "Bar"
SELECT ...
FROM A
INNER JOIN
B
ON A.PK = B.FK
AND B.Title = "Bar"
WHERE A.Name = "Foo"
SELECT *
FROM A, B
WHERE B.Title = "Bar"
AND A.Name = "Foo"
AND A.PK = B.FK
are identical.
Oracle, MySQL, PostgeSQL and SQL Server will treat them exactly the same, and use exactly same plan for all of them.
I'd use this one:
SELECT ...
FROM A
INNER JOIN
B
ON B.FK = A.PK
WHERE A.Name = "Foo"
AND B.Title = "Bar"
if there is a single-column key on B.FK, and this one:
SELECT ...
FROM A
INNER JOIN
B
ON B.FK = A.PK
AND B.Title = "Bar"
WHERE A.Name = "Foo"
if there is a composite key on (B.FK, B.title).
The join conditions are more visual in this case.
I may be wrong on this, but i see the 1st approach as a more efficient one. Your queries are executed inside-out. So when you run your join first, it'll run faster just because it has to match on (probably indexed) PK & FK Fields and not anything extraneous like your Title field ( which is probably a varchar, making the match even slower). So, by the time you get to your filtering where clause, you have less data to perform matching on. Total speculation though. I wonder what others would say. Wouldn't hurt to see the execution plan on this :)
Related
Suppose I have a SQL like that:
SELECT a.*
FROM A a
LEFT JOIN
B b
JOIN C c ON (c.b_id = b.id AND c.whatever > 0)
ON (a.id = b.a_id)
Now I start coding using the criteria API:
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<A> criteria = builder.createQuery(A.class);
Root<A> a = criteria.from(A.class);
Now what?
I can't Join<A, B> b = a.join("b") yet.
The a.join(???) should be called over something else, that happens to have b defined.
That something else is the result of joining together B and C (plus a custom ON clause, stating that only some Cs - those with whatever bigger than zero - are acceptable). That something else still exposes B and C separately (for further predicate filtering with where and/or column projection with select).
UPDATE
What's the difference between
(1) A a LEFT JOIN (B b JOIN C c ON (c.b_id = b.id AND c.whatever > 0)) ON (a.id = b.a_id); and
(2) (A a LEFT JOIN B b ON (a.id = b.a_id)) JOIN C c ON (c.b_id = b.id AND c.whatever > 0)?
[parenthesis were added for clarity; they are never required]
The ON clause can be understood as "closing" the last "still open" JOIN. The JOIN clause (any type) can be understood as "opening" a new JOIN; these JOINs are stacked on "opening" and unstacked on "closing".
In (1), the order is: I-JOIN(B and C), then L-JOIN(A and that).
In (2), the order is: L-JOIN(A and B), then I-JOIN(that and C).
[I-JOIN is an INNER JOIN and L-JOIN is a LEFT OUTER JOIN]
For the sake of simplicity, just take the table C to be empty. So, no matter what JOIN you make, any reference to C (i.e. to its columns) will always be NULL.
Now, in (1) there will be one result per row in A. Because I-JOIN(B and C) will always produce no rows (remember, C is empty, so any INNER JOIN is empty too); then L-JOIN(A and empty) will produce A and all columns from B and C will be NULL.
In (2) the result will be totally empty. No matter how many rows L-JOIN(A and B) produces (there will be at least one row per row of A), the final INNER JOIN with empty C entails an empty result (no rows at all).
I need to identify any objects in a database containing "scenarioID" that has hard coded value.
I am looking to identify following cases:
scenarioId = XX (two digit value)
scenarioId=XX (two digit value)
scenarioId= XX (two digit value)
Below query I wrote seems to be pulling objects containing "scenarioid", but is giving me more than above cases.
SELECT DISTINCT a.[name]
FROM sysobjects a
INNER JOIN syscomments b on a.id = b.id
WHERE b.[text] LIKE '%scenarioId = __[^0-9]%'
I realized my mistake. I was using [^0-9] instead of using [0-9].
SELECT DISTINCT a.[name]
FROM sysobjects a
INNER JOIN syscomments b on a.id = b.id
WHERE b.[text] LIKE '%scenarioId = [0-9][0-9]%'
or b.[text] like '%scenarioId=[0-9][0-9]%'
or b.[text] like '%scenarioId =[0-9][0-9]%'
or b.[text] like '%scenarioId = [0-9][0-9]%'
or b.[text] like '%scenarioId = [0-9][0-9]%'
My basic question has to do with updating multiple columns at once from specified values in my query. The reason I want to do this is that I am updating my values from a ginormous table so I only want to query it once in order to reduce run time. Here is an example of an example select statement that returns the value I want for just one of the columns I need to update:
select a.Value
from Table1
left outer join
(
select ID, FilterCol1, FilterCol2, Value
from Table2
) a on a.ID = Table1.ID
where {Condition1a on FilterCol1}
and {Condition2a on FilterCol2}
In order to update multiple columns at once I would like to be able do something like this (but it returns NULL):
Update T1
set T1Value1 = (select a.Value where {Condition1a on FilterCol1}
and {Condition2a on FilterCol2)
,T1Value2 = (select a.Value where {Condition1b on FilterCol1}
and {Condition2b on FilterCol2})
from Table1 T1
left outer join
(
select ID, FilterCol1, FilterCol2, Value
from Table2
) a on a.ID = Table1.ID
Any help figuring this out would be greatly appreciated, let me know if you have any questions or if I made any errors. Thanks!
EDIT: I think I have identified the problem, but I'm not sure of a solution yet. I think seeing the issue requires a little more context: The select from table 2 is actually an unpivot on a wide table. This means that when the left outer join is applied, there will be multiple rows for a given ID. What the case statement that Earl suggested seems to be doing (and I assume this is happening with the where clause as well) is comparing my Conditions to only the first row of the columns from a. Since my conditions are meant to help determine which of the rows from a is chosen, they will always evaluate false for the first row (I know this just from what I know about the data), hence my perpetual NULL values. Does anyone know of a workaround to look at the other rows in a?
UPDATE T1
SET T1Value1 = CASE WHEN (FilterCol1 = Condition1a AND FilterCol2 = Condition2a) THEN a.Value END,
T1Value2 = CASE WHEN (FilterCol1 = Condition1b AND FilterCol2 = Condition2b) THEN a.Value END
FROM Table1 T1
left outer join
(
select ID, FilterCol1, FilterCol2, Value
) a on a.ID = Table1.ID
Can the following be rewritten to be more efficient?
I would use EXISTS if I didn't need fields from country but I do need those fields, and am not sure how to write this to make it more efficient.
SELECT distinct
p.ProvinceID,
p.Abbv as RegionCode,
p.name as RegionName,
cn.Code as CountryCode,
cn.Name as CountryName
FROM dbo.provinces AS p
INNER JOIN dbo.Countries AS cn ON p.CountryID = cn.CountryID
INNER JOIN dbo.Cities c on c.ProvinceID = p.ProvinceID
INNER JOIN dbo.Listings AS l ON l.CityID = c.CityID
WHERE l.IsActive = 1 AND l.IsApproved = 1
There are two things to note:
You're joining to dbo.Listings which results in many records, so you need to use DISTINCT (usually an expensive operator)
For any tables with columns not in the select you can move into an EXISTS (but the query planner effectively does this for you anyway)
So try this:
SELECT
p.ProvinceID,
p.Abbv as RegionCode,
p.name as RegionName,
cn.Code as CountryCode,
cn.Name as CountryName
FROM dbo.provinces AS p
INNER JOIN
dbo.Countries AS cn
ON p.CountryID = cn.CountryID
WHERE EXISTS (SELECT 1 FROM
dbo.Listings l
INNER JOIN dbo.Cities c
on l.CityID = c.CityID
WHERE c.ProvinceID = p.ProvinceID
AND l.IsActive = 1 AND l.IsApproved = 1
)
Check the query plans before and after - the query planner might be smart enough to do this anyway, but you have removed your distinct
The following will often perform even better by providing the optimizer more useful information:
SELECT
p.ProvinceID,
p.Abbv as RegionCode,
p.name as RegionName,
cn.Code as CountryCode,
cn.Name as CountryName
FROM dbo.provinces AS p
INNER JOIN
dbo.Countries AS cn
ON p.CountryID = cn.CountryID
INNER JOIN (
SELECT
p.ProvinceID
FROM
dbo.Listings l
INNER JOIN dbo.Cities c
on l.CityID = c.CityID
WHERE l.IsActive = 1 AND l.IsApproved = 1
GROUP BY
p.ProvinceID
) list
on list.ProvinceID = p.ProvinceID
I am trying to map a self join where the right table must be filtered, e.g. SQL such as this:
select t2.* from table t
left join table t2
on t2.parentID = t.ID and t2.active=1;
I can figure out the syntax if I wanted to filter the left table:
// works
var query = from t in table
where t.active= 1
join t2 in table
on t.parentID equals t2.ID into joined
from r in joined.DefaultIfEmpty() ...
But I can't figure out how to filter the right table. It seems like it should be something like this...
// does not work
var query = from t in table
join t2 in table
where t.field = 1
on t.parentID equals t2.ID into joined
from r in joined.DefaultIfEmpty() ...
(not valid... join can't have where). There is discussion of using multiple from clauses, but if I create more than one from clause, so I can add a where to the 2nd one, I can't figure out how to join the results of them into a new temporary table.
I can't just add a "where" after the join; the right table must be filtered first or matches will occur, and a where clause at the end would remove the row from the left table that I do want in the output. That is, the output should have rows where there's nothing matched from filtered right table. So I need to filter the right table before the join.
I think you are looking to do this:
var query = from t in table
join t2 in
(from t3 in table
where t3.field = 1
select t3)
on t.parentID equals t2.ID into joined
from r in joined.DefaultIfEmpty() ...
Another way is to use multiple from like this:
var query = from t in table
from t2 in table.Where(x => x.field = 1)
.Where(x => x.ID == t.parentID)
.DefaultIfEmpty()
select ....