Can we add an empty Table in From clause - sql-server-2005

In my SQL query from clause there is a table (table 1)I have noticed if this table is empty my whole query results null.Also if i do not use this table in where and select clause.
What is reason behind this and how to prevent this.
select ps.ProductName from
productsale ps,StockTransfer dtc
where
ps.productcode = '010134600223'
and ps.productcode=
(case when ps.productcode=dtc.productcode then
dtc.productcode else ps.productcode
end)
This query works fine if I not add StockTransfer table in form clause(If StockTransfer table is empty) else it works fine.

Depends on how you did your join.
If you do
select * from tablea join tableb on tablea.id=tableb.id
and tableb is empty, then nothing could result as nothing was there.
left or right joins depending on which is the empty table, work to do exactly that.
left join will return all data in the first table and any data from the second table that matches so
select * from tablea left join tableb on tablea.id=tableb.id
would return in short, all but just tablea data, because thats all there is (but any additional fields you asked for from tableb, but obviously no data as there was none)

You do a cross join. That means that for every row in Table1 you will get all rows in Table2. So if Table1 has 4 rows and Table2 has 2 rows you will get 8 rows in the result set (4*2=8). If Table2 contains 0 rows you will get 0 rows in result set (4*0=0).
To fix this you need to add a join condition.

Related

duplicate query result when join table

I face issue about duplicate data when join table, here my sample data table I have
-- Table A
I want to join with
-- Table B
this my query notation for join both table,
select a.trans_id, name
from tableA a
inner join tableB b
on a.ID_Trans = b.trans_id
and this the result, why I get the duplicating data which should show only two lines of data, please help me to solve this case.
Firstly, as you have been told multiple times in the comments, this is working exactly as you have written, and (more importantly) as intended. You have 2 rows in tableA and those 2 rows match 2 rows in your table tableB according to the ON clause. This means that each join operation, for the each of the rows in tableA, results in 2 rows as well; thus 4 rows (2 * 2 = 4).
Considering that your table, TableA only has one column then it seems that you should be cleaning up that data and deleting the duplicates. There are plenty of examples on how to do that already (example).
Perhaps the column you show us in TableA is one many, and thus instead you have a denormalisation issue, and instead there should be another table with the details of Id_trans and a PRIMARY KEY or UNIQUE CONSTRAINT/INDEX on it. Then you would join fron that table to TableB.
Finally, what you might be after is an EXISTS, which would look like this:
SELECT B.trans_id, B.[name]
FROM dbo.TableB B
WHERE EXISTS(SELECT 1
FROM dbo.TableA A
WHERE A.ID_Trans = B.trans_id); --Odd that it's called ID_Trans in one table, and Trans_ID in another
As the comments mentioned your query does exactly what you asked it to do but I think you wanted something like:
select a.trans_id, a.name, b.name
from tableA a
inner join tableB b on a.trans_id = b.trans_id
group by a.trans_id, a.name, b.name
Since there are two rows in both table with same ID join will make them four. You can use distinct to remove duplicates:
select distinct a.trans_id, name
from tableA a
inner join tableB b
on a.id_trans = b.trans_id
But I would suggest to use exists:
select trans_id, name
from tableB b
exists (select 1 from tableA a where a.trans_id=b.trans_id)

SQL help, left join with filter

Please have a look at the SQL in the SQLFiddle link below.
http://sqlfiddle.com/#!9/22e094/4
My goal is to get all the records from Table1, and if SecId exists in Table2, join only if the status is 'Y'.
Result should be that it pulls from table 1: ID 1 and 2. And for ID 1, it successfully left joins Table2 and pulls 'Y'
As you can see in the fiddle, I tried 3 different ways but can't seem to get it.
It's got me stumped... Help would be awesome! :)
The left join, with just the join condition in the ON clause, is fine.
But then, you said you want to keep the rows where status is 'Y' only when the secid exists in table2. So this means you also want to keep the rows where the secid from table2 is NULL.
select * won't fly, because you have columns by the same name, secid, in both tables. You must distinguish them (by giving them aliases - or at least one of them; I gave aliases to both of them) if you need to reference them in the where clause, or anywhere else in the query, to break the ambiguity. And, since the aliases can only be given in the SELECT clause, which is evaluated after the WHERE clause, you need to do the left join in a subquery.
select id, secid_a, secid_b, status
from (
select a.id, a.secid as secid_a, b.secid as secid_b, b.status
from table1 a left join table2 b
on a.secid = b.secid
)
where status = 'Y' or secid_b is null;

SQL filter LEFT TABLE before left join

I have read a number of posts from SO and I understand the differences between filtering in the where clause and on clause. But most of those examples are filtering on the RIGHT table (when using left join). If I have a query such as below:
select * from tableA A left join tableB B on A.ID = B.ID and A.ID = 20
The return values are not what I expected. I would have thought it first filters the left table and fetches only rows with ID = 20 and then do a left join with tableB.
Of course, this should be technically the same as doing:
select * from tableA A left join table B on A.ID = B.ID where A.ID = 20
But I thought the performance would be better if you could filter the table before doing a join. Can someone enlighten me on how this SQL is processed and help me understand this thoroughly.
A left join follows a simple rule. It keeps all the rows in the first table. The values of columns depend on the on clause. If there is no match, then the corresponding table's columns are NULL -- whether the first or second table.
So, for this query:
select *
from tableA A left join
tableB B
on A.ID = B.ID and A.ID = 20;
All the rows in A are in the result set, regardless of whether or not there is a match. When the id is not 20, then the rows and columns are still taken from A. However, the condition is false so the columns in B are NULL. This is a simple rule. It does not depend on whether the conditions are on the first table or the second table.
For this query:
select *
from tableA A left join
tableB B
on A.ID = B.ID
where A.ID = 20;
The from clause keeps all the rows in A. But then the where clause has its effect. And it filters the rows so on only id 20s are in the result set.
When using a left join:
Filter conditions on the first table go in the where clause.
Filter conditions on subsequent tables go in the on clause.
Where you have from tablea, you could put a subquery like from (select x.* from tablea X where x.value=20) TA
Then refer to TA like you did tablea previously.
Likely the query optimizer would do this for you.
Oracle should have a way to show the query plan. Put "Explain plan" before the sql statement. Look at the plan both ways and see what it does.
In your first SQL statement, A.ID=20 is not being joined to anything technically. Joins are used to connect two separate tables together, with the ON statement joining columns by associating them as keys.
WHERE statements allow the filtering of data by reducing the number of rows returned only where that value can be found under that particular column.

sql left join returns

I am trying to run a left join on 2 tables. I do not have a group by and the only where condition i have is on the second table. But, the returned rows are less than the first table. isn't the left join suppose to bring all the data from the first table?
Here is my SQL:
select *
from tbl_a A left join tbl_b B
ON
A.Cnumber=B.Cnumber
and A.CDNUmber=B.CDNumber
and abs(A.duration - B.Duration)<2
and substr(A.text,1,3)||substr(A.text,5,8)||substr(A.text,9,2)=substr(B.text,1,8)
where B.fixed = 'b580'
There are 140,000 records in table A but the result returned is less than 100,000 records. What is the problem and how can I solve it?
As soon as you put a condition in the WHERE clause that references the right table and doesn't accommodate the NULLs that will be produced when the join is unsuccessful, you've transformed it (effectively) back into an INNER JOIN.
Try:
where B.fixed = 'b580' OR B.fixed IS NULL
Or add this condition to the ON clause for the JOIN.
You should add the where clause to the join:
select *
from tbl_a A left join tbl_b B
ON
A.Cnumber=B.Cnumber
and A.CDNUmber=B.CDNumber
and abs(A.duration - B.Duration)<2
and substr(A.text,1,3)||substr(A.text,5,8)||substr(A.text,9,2)=substr(B.text,1,8)
and B.fixed = 'b580'
If you use where statemen all records where b is not existing will not returned.

How can a LEFT OUTER JOIN return more records than exist in the left table?

I have a very basic LEFT OUTER JOIN to return all results from the left table and some additional information from a much bigger table. The left table contains 4935 records yet when I LEFT OUTER JOIN it to an additional table the record count is significantly larger.
As far as I'm aware it is absolute gospel that a LEFT OUTER JOIN will return all records from the left table with matched records from the right table and null values for any rows which cannot be matched, as such it's my understanding that it should be impossible to return more rows than exist in the left table, but it's happening all the same!
SQL Query follows:
SELECT SUSP.Susp_Visits.SuspReason, SUSP.Susp_Visits.SiteID
FROM SUSP.Susp_Visits LEFT OUTER JOIN
DATA.Dim_Member ON SUSP.Susp_Visits.MemID = DATA.Dim_Member.MembershipNum
Perhaps I have made a mistake in the syntax or my understanding of LEFT OUTER JOIN is incomplete, hopefully someone can explain how this could be occurring?
The LEFT OUTER JOIN will return all records from the LEFT table joined with the RIGHT table where possible.
If there are matches though, it will still return all rows that match, therefore, one row in LEFT that matches two rows in RIGHT will return as two ROWS, just like an INNER JOIN.
EDIT:
In response to your edit, I've just had a further look at your query and it looks like you are only returning data from the LEFT table. Therefore, if you only want data from the LEFT table, and you only want one row returned for each row in the LEFT table, then you have no need to perform a JOIN at all and can just do a SELECT directly from the LEFT table.
Table1 Table2
_______ _________
1 2
2 2
3 5
4 6
SELECT Table1.Id,
Table2.Id
FROM Table1
LEFT OUTER JOIN Table2 ON Table1.Id=Table2.Id
Results:
1,null
2,2
2,2
3,null
4,null
It isn't impossible. The number of records in the left table is the minimum number of records it will return. If the right table has two records that match to one record in the left table, it will return two records.
In response to your postscript, that depends on what you would like.
You are getting (possible) multiple rows for each row in your left table because there are multiple matches for the join condition. If you want your total results to have the same number of rows as there is in the left part of the query you need to make sure your join conditions cause a 1-to-1 match.
Alternatively, depending on what you actually want you can use aggregate functions (if for example you just want a string from the right part you could generate a column that is a comma delimited string of the right side results for that left row.
If you are only looking at 1 or 2 columns from the outer join you might consider using a scalar subquery since you will be guaranteed 1 result.
Each record from the left table will be returned as many times as there are matching records on the right table -- at least 1, but could easily be more than 1.
Could it be a one to many relationship between the left and right tables?
LEFT OUTER JOIN just like INNER JOIN (normal join) will return as many results for each row in left table as many matches it finds in the right table. Hence you can have a lot of results - up to N x M, where N is number of rows in left table and M is number of rows in right table.
It's the minimum number of results is always guaranteed in LEFT OUTER JOIN to be at least N.
If you need just any one row from the right side
SELECT SuspReason, SiteID FROM(
SELECT SUSP.Susp_Visits.SuspReason, SUSP.Susp_Visits.SiteID, ROW_NUMBER()
OVER(PARTITION BY SUSP.Susp_Visits.SiteID) AS rn
FROM SUSP.Susp_Visits
LEFT OUTER JOIN DATA.Dim_Member ON SUSP.Susp_Visits.MemID = DATA.Dim_Member.MembershipNum
) AS t
WHERE rn=1
or just
SELECT SUSP.Susp_Visits.SuspReason, SUSP.Susp_Visits.SiteID
FROM SUSP.Susp_Visits WHERE EXISTS(
SELECT DATA.Dim_Member WHERE SUSP.Susp_Visits.MemID = DATA.Dim_Member.MembershipNum
)
Pay attention if you have a where clause on the "right side' table of a query containing a left outer join...
In case you have no record on the right side satisfying the where clause, then the corresponding record of the 'left side' table will not appear in the result of your query....
It seems as though there are multiple rows in the DATA.Dim_Member table per SUSP.Susp_Visits row.
if multiple (x) rows in Dim_Member are associated with a single row in Susp_Visits, there will be x rows in the resul set.
Since the left table contains 4935 records, I suspect you want your results to return 4935 records. Try this:
create table table1
(siteID int,
SuspReason int)
create table table2
(siteID int,
SuspReason int)
insert into table1(siteID, SuspReason) values
(1, 678),
(1, 186),
(1, 723)
insert into table2(siteID, SuspReason) values
(1, 678),
(1, 965)
select distinct t1.siteID, t1.SuspReason
from table1 t1 left join table2 t2 on t1.siteID = t2.siteID and t1.SuspReason = t2.SuspReason
union
select distinct t2.siteID, t2.SuspReason
from table1 t1 right join table2 t2 on t1.siteID = t2.siteID and t1.SuspReason = t2.SuspReason
The only way your query would return more number of rows than the left table ( which is SUSP.Susp_Visits in your case), is that the condition (SUSP.Susp_Visits.MemID = DATA.Dim_Member.MembershipNum) is matching multiple rows in the right table, which is DATA.Dim_Member. So, there are multiple rows in the DATA.Dim_Member where identical values are present for DATA.Dim_Member.MembershipNum. You can verify this by executing the below query:
select DATA.Dim_Member.MembershipNum, count(DATA.Dim_Member.MembershipNum) from DATA.Dim_Member group by DATA.Dim_Member.MembershipNum
Simply, LEFT OUTER JOIN is the Cartesian product within each join key, along with the unmatched rows of the left table
(i.e. for each key_x that has N records in table_L and M records in table_R the result will have N*M records if M>0, or N records if M=0)