Translating Oracle SQL to Access Jet SQL, Left Join - sql

There must be something I'm missing here. I have this nice, pretty Oracle SQL statement in Toad that gives me back a list of all active personnel with the IDs that I want:
SELECT PERSONNEL.PERSON_ID,
PERSONNEL.NAME_LAST_KEY,
PERSONNEL.NAME_FIRST_KEY,
PA_EID.ALIAS EID,
PA_IDTWO.ALIAS IDTWO,
PA_LIC.ALIAS LICENSENO
FROM PERSONNEL
LEFT JOIN PERSONNEL_ALIAS PA_EID
ON PERSONNEL.PERSON_ID = PA_EID.PERSON_ID
AND PA_EID.PERSONNEL_ALIAS_TYPE_CD = 1086
AND PA_EID.ALIAS_POOL_CD = 3796547
AND PERSONNEL.ACTIVE_IND = 1
LEFT JOIN PERSONNEL_ALIAS PA_IDTWO
ON PERSONNEL.PERSON_ID = PA_IDTWO.PERSON_ID
AND PA_IDTWO.PERSONNEL_ALIAS_TYPE_CD = 3839085
AND PA_IDTWO.ACTIVE_IND = 1
LEFT JOIN PERSONNEL_ALIAS PA_LIC
ON PERSONNEL.PERSON_ID = PA_LIC.PERSON_ID
AND PA_LIC.PERSONNEL_ALIAS_TYPE_CD = 1087
AND PA_LIC.ALIAS_POOL_CD = 683988
AND PA_LIC.ACTIVE_IND = 1
WHERE PERSONNEL.ACTIVE_IND = 1 AND PERSONNEL.PHYSICIAN_IND = 1;
This works very nicely. Where I run into problems is when I put it into Access. I know, I know, Access Sucks. Sometimes one needs to use it, especially if one has multiple database types that they just want to store a few queries in, and especially if one's boss only knows Access. Anyway, I was having trouble with the ANDs inside the FROM, so I moved those to the WHERE, but for some odd reason, Access isn't doing the LEFT JOINs, returning only those personnel with EID, IDTWO, and LICENSENO's. Not everybody has all three of these.
Best shot in Access so far is:
SELECT PERSONNEL.PERSON_ID,
PERSONNEL.NAME_LAST_KEY,
PERSONNEL.NAME_FIRST_KEY,
PA_EID.ALIAS AS EID,
PA_IDTWO.ALIAS AS ID2,
PA_LIC.ALIAS AS LICENSENO
FROM ((PERSONNEL
LEFT JOIN PERSONNEL_ALIAS AS PA_EID ON PERSONNEL.PERSON_ID=PA_EID.PERSON_ID)
LEFT JOIN PERSONNEL_ALIAS AS PA_IDTWO ON PERSONNEL.PERSON_ID=PA_IDTWO.PERSON_ID)
LEFT JOIN PERSONNEL_ALIAS AS PA_LIC ON PERSONNEL.PERSON_ID=PA_LIC.PERSON_ID
WHERE (((PERSONNEL.ACTIVE_IND)=1)
AND ((PERSONNEL.PHYSICIAN_IND)=1)
AND ((PA_EID.PRSNL_ALIAS_TYPE_CD)=1086)
AND ((PA_EID.ALIAS_POOL_CD)=3796547)
AND ((PA_IDTWO.PRSNL_ALIAS_TYPE_CD)=3839085)
AND ((PA_IDTWO.ACTIVE_IND)=1)
AND ((PA_LIC.PRSNL_ALIAS_TYPE_CD)=1087)
AND ((PA_LIC.ALIAS_POOL_CD)=683988)
AND ((PA_LIC.ACTIVE_IND)=1));
I think that part of the problem could be that I'm using the same alias (lookup) table for all three joins. Maybe there's a more efficient way of doing this? Still new to SQL land, so any tips as far as that goes would be great. I feel like these should be equivalent, but the Toad query gives me back many many tens of thousands of imperfect rows, and Access gives me fewer than 500. I need to find everybody so that nobody is left out. It's almost as if the LEFT JOINs aren't working at all in Access.

To understand what you are doing, let's look at simplified version of your query:
SELECT PERSONNEL.PERSON_ID,
PA_EID.ALIAS AS EID
FROM PERSONNEL
LEFT JOIN PERSONNEL_ALIAS AS PA_EID ON PERSONNEL.PERSON_ID=PA_EID.PERSON_ID
WHERE PERSONNEL.ACTIVE_IND=1
AND PERSONNEL.PHYSICIAN_IND=1
AND PA_EID.PRSNL_ALIAS_TYPE_CD=1086
AND PA_EID.ALIAS_POOL_CD=3796547
If the LEFT JOIN finds match, your row might look like this:
Person_ID EID
12345 JDB
If it doesn't find a match, (disregard the WHERE clause for a second), it could look like:
Person_ID EID
12345 NULL
When you add the WHERE clauses above, you are telling it to only find records in the PERSONNEL_ALIAS table that meet the condition, but if no records are found, then the values are considered NULL, so they will never satisfy the WHERE condition and no records will come back...
As Joe Stefanelli said in his comment, adding a WHERE clause to a LEFT JOIN'ed table make it act as an INNER JOIN instead...

Further to #Sparky's answer, to get the equivalent of what you're doing in Oracle, you need to filter rows from the tables on the "outer" side of the joins before you join them. One way to do this might be:
For each table on the "outer" side of a join that you need to filter rows from (that is, the three instances of PERSONNEL_ALIAS), create a query that filters the rows you want. For example, the first query (say, named PA_EID) might look something like this:SELECT PERSONNEL_ALIAS.* FROM PERSONNEL_ALIAS WHERE PERSONNEL_ALIAS.PERSONNEL_ALIAS_TYPE_CD = 1086 AND PERSONNEL_ALIAS.ALIAS_POOL_CD = 3796547
In your "best shot in Access so far" query in the original post: a) replace each instance of PERSONNEL_ALIAS with the corresponding query created in Step 1, and, b) remove the corresponding conditions (on PA_EID, PA_IDTWO, and PA_LIC) from the WHERE clause.

Related

ORA-01417 - Two outer joins error. New join syntax?

I never got my head around the "new" SQL join syntax, and therefore use the "old" join system, with the (+). I know it's about time I learned it - however I just find the old syntax a lot more intuitive, especially when working with multiple tables with multiple joins.
However I now have an operation which requires two outer joins on the same table. My code is:
SELECT
C.ID,
R.VALUE,
R.LOG_ID,
LOG.ACTION
FROM
C,
R,
LOG
WHERE
C.DELETED IS NULL
AND R.DELETED IS NULL
-- Two joins below
AND R.C_ID(+) = C.ID
AND R.LOG_ID(+) = LOG.ID
However this results in an error:
ORA-01417 - A table may be outer joined to at most one table.
Searching for this error I find that the solution is to use the new syntax For example this answer on SO:
Outer join between three tables causing Oracle ORA-01417 error
So I am aware that some may consider this question a duplicate as it technically already has an answer. However the "old" syntax posed in that question does not contain exactly the same number of tables and joins as I have here, and try as I might, I'm not sure how I would factor this in to my own code.
Is anyone able to assist? Thanks.
I think you want:
SELECT C.ID, R.VALUE, R.LOG_ID, LOG.ACTION
FROM C LEFT JOIN
R
ON R.C_ID = C.ID LEFT JOIN
LOG
ON R.LOG_ID = LOG.I
WHERE C.DELETED IS NULL AND
R.DELETED IS NULL;
The "new" (it is 25 years old) outer join syntax is actually very easy to follow, particularly for a simple example with just LEFT JOIN.
The idea is you want to keep all rows from one table (perhaps subject to filters in the WHERE clause). That is the first table. Then you use a chain of LEFT JOIN to bring in other tables.
All rows from the first table are in the result set. If there are matching rows in the other tables, then columns from those tables come from matching rows. If there are no matches, then the row from the first table is kept.

Left join or Select in select (SQL - Speed of query)

I have something like this:
SELECT CompanyId
FROM Company
WHERE CompanyId not in
(SELECT CompanyId
FROM Company
WHERE (IsPublic = 0) and CompanyId NOT IN
(SELECT ShoppingLike.WhichId
FROM Company
INNER JOIN
ShoppingLike ON Company.CompanyId = ShoppingLike.UserId
WHERE (ShoppingLike.IsWaiting = 0) AND
(ShoppingLike.ShoppingScoreTypeId = 2) AND
(ShoppingLike.UserId = 75)
)
)
It has 3 select, I want to know how could I have it without making 3 selects, and which one has better speed for 1 million record? "select in select" or "left join"?
My experiences are from Oracle. There is never a correct answer to optimising tricky queries, it's a collaboration between you and the optimiser. You need to check explain plans and sometimes traces, often at each stage of writing the query, to find out what the optimiser in thinking. Having said that:
You could remove the outer SELECT by putting the entire contents of it's subquery WHERE clause in a NOT(...). On the face of it will prevent that outer full scan of Company (or it's index of CompanyId). Try it, check the output is the same and get timings, then remove it temporarily before trying the below. The NOT() may well cause the optimiser to stop considering an ANTI-JOIN against the ShoppingLike subquery due to an implicit OR being created.
Ensure that CompanyId and WhichId are defined as NOT NULL columns. Without this (or the likes of an explicit CompanyId IS NOT NULL) then ANTI-JOIN options are often discarded.
The inner most subquery is not correlated (does not reference anything from it's outer query) so can be extracted and tuned separately. As a matter of style I'd swap the table names round the INNER JOIN as you want ShoppingLike scanned first as it has all the filters against it. It wont make any difference but it reads easier and makes it possible to use a hint to scan tables in the order specified. I would even question the need for the Company table in this subquery.
You've used NOT IN when sometimes the very similar NOT EXISTS gives the optimiser more/alternative options.
All the above is just trial and error unless you start trying the explain plan. Oracle can, with a following wind, convert between LEFT JOIN and IN SELECT. 1M+ rows will create time to invest.

Beginner SQL trouble with simple Join/Subquery

I know this is a quite a basic query, but I can't find exactly the solution for my needs, so here goes...
I have a table of customers INT_AUX_LISTING, a table of folders in which they're contained INT_AUX_DIRECTORY and a joining table INT_AUX_DIR_LIST.
I need to return a list of all customers who are in folder 40017, and also not in folder 2. Any other folder in which they're contained is irrelevant.
So far I've come up with
SELECT *
FROM INT_AUX_LISTING l
LEFT JOIN INT_AUX_DIR_LIST dl
ON l.LISTING_ID=dl.LISTING_ID
WHERE dl.CONTAIN_DIR_ID=40017
AND dl.CONTAIN_DIR_ID <> 2
However this (obviously) isn't correct, and is returning far too many values. I think I need to introduce a subquery but things go awry when I try.
Apologies for the entry level nature of the question, but it's only my 3rd day working with SQL!
First, clarification on left-join and where. If you have a query that has a left-join to a table (per your example where "dl" was the right-side of the join), and then add the "dl" alias to a where clause, that in essence converts it to an INNER JOIN (unless you explicitly tested with an OR dl.listing_ID IS NULL).
Now, back to your primary objective. Without the table structures, and not exactly clear on your column intent, it APPEARS that the "listing_id" column is your client as found in the int_aux_listing table. the "contain_dir_id" is the directory (one of many that may be possible) as found in the int_aux_dir_list.
That said, you want a list of all people who are in 40017 AND NOT in 2. For this, you can do a left-join but using the int_aux_listing table twice but with different aliases. Once you get the list of clients, you can then join that to your client table. Since you are new, take the sample one step at a time.
Just get clients within 40017
select
dl.listing_id
from
int_aux_dir_list dl
where
dl.contain_dir_id = 40017
Now, exclude those if they are found in directory 2
select
dl.listing_id
from
int_aux_dir_list dl
left join int_aux_dir_list dl2
on dl.listing_id = dl2.listing_id
AND dl2.contain_dir_id = 2
where
dl.contain_dir_id = 40017
AND dl2.listing_id IS NULL
By doing a left-join to the directory listing table a second time (via alias "dl2"), but specifically for its directory id = 2 (via AND dl2.contain_dir_id = 2), you are getting all clients from dl that are within directory 40017 and looking for that same client ID in the second instance ("dl2").
Here's the kicker. Notice the where clause is stating "AND dl2.listing_id IS NULL".
This is basically saying WHEN Looking at the DL2 instance, I only want records that DO NOT have a corresponding listing ID matched.
Now, wrap this up to get the client information you need. In this case, getting all columns from the directory listing (in case other columns of importance), AND getting all columns from the "l"isting table.
select
dl.*,
l.*
from
int_aux_dir_list dl
left join int_aux_dir_list dl2
on dl.listing_id = dl2.listing_id
AND dl2.contain_dir_id = 2
join INT_AUX_LISTING l
on dl.listing_id = l.listing_id
where
dl.contain_dir_id = 40017
AND dl2.listing_id IS NULL
Hopefully this clears things up for you some.
Your WHERE clause is the problem; it brings back ALL rows except the one where that id is not 2, including the one where id is 40017 (since it's not equal to 2). I think you should revisit it.
If you use a LEFT JOIN[1] on INT_AUX_LISTING, you will get all customers even if they don't have a corresponding folder. In your case it's best to use INNER JOIN[2] instead.
In the where clause, you're first statement selects customers who are in folder 40017 (dl.CONTAIN_DIR_ID=40017), because of this your second statement (dl.CONTAIN_DIR_ID <> 2) will always be true (40017 will never be equal to 2). So basically your selecting all customers in folder 40017.
Your notion of a subquery was correct. This is one way of getting the desired result:
SELECT *
FROM INT_AUX_LISTING l
INNER JOIN INT_AUX_DIR_LIST dl1
ON l.LISTING_ID = dl1.LISTING_ID
WHERE dl1.CONTAIN_DIR_ID = 40017
AND l.LISTING_ID NOT IN (
SELECT dl2.LISTING_ID FROM INT_AUX_DIR_LIST dl2 WHERE dl2.CONTAIN_DIR_ID = 2
)

INNER JOIN vs multiple table names in "FROM" [duplicate]

This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
INNER JOIN versus WHERE clause — any difference?
What is the difference between an INNER JOIN query and an implicit join query (i.e. listing multiple tables after the FROM keyword)?
For example, given the following two tables:
CREATE TABLE Statuses(
id INT PRIMARY KEY,
description VARCHAR(50)
);
INSERT INTO Statuses VALUES (1, 'status');
CREATE TABLE Documents(
id INT PRIMARY KEY,
statusId INT REFERENCES Statuses(id)
);
INSERT INTO Documents VALUES (9, 1);
What is the difference between the below two SQL queries?
From the testing I've done, they return the same result. Do they do the same thing? Are there situations where they will return different result sets?
-- Using implicit join (listing multiple tables)
SELECT s.description
FROM Documents d, Statuses s
WHERE d.statusId = s.id
AND d.id = 9;
-- Using INNER JOIN
SELECT s.description
FROM Documents d
INNER JOIN Statuses s ON d.statusId = s.id
WHERE d.id = 9;
There is no reason to ever use an implicit join (the one with the commas). Yes for inner joins it will return the same results. However, it is subject to inadvertent cross joins especially in complex queries and it is harder for maintenance because the left/right outer join syntax (deprecated in SQL Server, where it doesn't work correctly right now anyway) differs from vendor to vendor. Since you shouldn't mix implicit and explict joins in the same query (you can get wrong results), needing to change something to a left join means rewriting the entire query.
If you do it the first way, people under the age of 30 will probably chuckle at you, but as long as you're doing an inner join, they produce the same result and the optimizer will generate the same execution plan (at least as far as I've ever been able to tell).
This does of course presume that the where clause in the first query is how you would be joining in the second query.
This will probably get closed as a duplicate, btw.
The nice part of the second method is that it helps separates the join condition (on ...) from the filter condition (where ...). This can help make the intent of the query more readable.
The join condition will typically be more descriptive of the structure of the database and the relation between the tables. e.g., the salary table is related to the employee table by the EmployeeID column, and queries involving those two tables will probably always join on that column.
The filter condition is more descriptive of the specific task being performed by the query. If the query is FindRichPeople, the where clause might be "where salaries.Salary > 1000000"... thats describing the task at hand, not the database structure.
Note that the SQL compiler doesn't see it that way... if it decides that it will be faster to cross join and then filter the results, it will cross join and filter the results. It doesn't care what is in the ON clause and whats in the WHERE clause. But, that typically wont happen if the on clause matches a foreign key or joins to a primary key or indexed column. As far as operating correctly, they are identical; as far as writing readable, maintainable code, the second way is probably a little better.
there is no difference as far as I know is the second one with the inner join the new way to write such statements and the first one the old method.
The first one does a Cartesian product on all record within those two tables then filters by the where clause.
The second only joins on records that meet the requirements of your ON clause.
EDIT: As others have indicated, the optimization engine will take care of an attempt on a Cartesian product and will result in the same query more or less.
A bit same. Can help you out.
Left join vs multiple tables in SQL (a)
Left join vs multiple tables in SQL (b)
In the example you've given, the queries are equivalent; if you're using SQL Server, run the query and display the actual exection plan to see what the server's doing internally.

joining more tables makes me get less data from the query

I have one problem with one SP, when I am joining some specific tables, I am getting less data from the SP then I am getting when they are not included in the SP.
I am not getting any data from them yet, I am just joining them and only that makes the SP to send me less data.
Any idea what the problem can be? Thanks
It sounds like there are no matching rows in the tables you're joining to.
If you change the join to a LEFT OUTER JOIN, you should get the rows you are expecting (but, obviously, check the output to make sure you do!)
Joins usually have a join condition in the "ON" clause. That condition says how to match rows between the tables being joined. If, for a particular row on one side, there is no matching row on the other side, then we need to consider what type of join we're dealing with:
For an INNER JOIN, the row will be discarded.
For a LEFT JOIN, and if the row comes from the "LEFT" table source, then this row will appear, but with NULLs present for all columns from the "RIGHT" table source.
RIGHT JOIN is similar to left join, with the directions swapped over.
regardless if you are asking for data from those tables or not, they have been included as part of the join, and the resultset is going to return rows that meet the criteria you have specified.
You may want to post 2 versions of the SP, one with few tables, then a second with one or more of the joins so that it can be better explained what is happening behind the scenes.
Sounds like you're doing an INNER JOIN. This will only return a record if whatever property you're joining on is in both tables. As an example:
Customers
ID Name
1 Mike
2 Steve
3 Amy
Address
ID Address
1 123 Main
3 456 Oak
If you
SELECT Name, Adddress FROM NAME N
Join Address A ON N.Id = A.Id
Only The records for Mike and Amy will be returned because Steve doesn't have an Address record.
I don't know what kind of join you are doing but it exists three kind of join
INNER JOIN : Retrieves datas that matches on both side
LEFT (OUTER) JOIN : Retrieves datas that only match on the left side, even if right is null
RIGHT (OUTER) JOIN Retrieves datas that only mach on the right side, even if left is null
According to which one you are using, datas can be retrieved or not.
But posting your query will let us tell you what might be the real problem.
Hope I could help,
See this blog post by Jeff Atwood. It explains SQL Joins very well. I think it will answer your question about why certain sets of data may or may not be missing.