I have written two queries to check the differences between two tables, both shown below. Query 2 shows me the correct results.
In each table there is one record that is not in the other. So I wanted a query which would show both these records, which Query 2 does. It show me the 90 rows where the tables match plus another 2 rows, one where a record is in tblIH but not in tblTempN and another record which is in tblTempN but not in tblIH.
Whereas Query 1 shows me only the 90 records where the tables match and one extra row where the record is in tblIH but not in tblTempN - it does NOT however show me the record in tblTempN which is not in tblIH - why? I thought using a full outer join would show me all records from both tables? I don't really understand the difference between the two queries as they seem the same to me?
Query 1
select coalesce(h.Sedol, nav.Sedol) Sedol,
coalesce(nav.Name, h.Name) Name,
isnull(h.Nominal, 0) - isnull(nav.Nominal, 0) NomDiff
from tblIH h full outer join tblTempN nav
on h.Sedol = nav.Sedol and h.Code = nav.Code
where h.FundCode = 'ABC' and h.DatePrice = '2015-03-20'
Query 2
;with hld as
(
select Sedol, Name, FX, Nominal from tblIH
where DatePrice = '2015-03-20' and FundCode = 'ABC'
), nav as
(
select Sedol, Name, Nominal from tblTempN
where DateAcc = '2015-03-20' and FundCode = 'ABC'
)
select coalesce(hld.Sedol, nav.Sedol) Sedol,
coalesce(nav.Name, hld.Name) Name,
isnull(hld.Nominal, 0) - isnull(nav.Nominal, 0) NomDiff
from hld full outer join nav
on hld.Sedol = nav.Sedol
In full outer join if you don't have satisfied condition field values from that table fetched as null
I suppose you missed to write conditions
nav.FundCode = 'ABC' and nav.DatePrice = '2015-03-20'
but apart from this you are missing one more fundamental that where clause will be applicable on the result from that full outer join.
So actually you are getting 90+1+1 out of full outer join but your where condition is filtering one record from this result because for one desired record h.FundCode and h.DatePrice value is NULL.
You can use NVL function while checking for these condition.
Related
I have two tables. One table has floor number(tb_FloorNumber.FloorNumber. records :For example 1 to 15) and another table which has Floor number and User_Id column(tb_Emp_Master.FloorNumber, tb_Emp_Master.User_Id). I want to bring all the records from tb_FloorNumber and only the records from tb_Emp_Master with the condition (User_Id = "fat35108").
I know I can do this with two queries like this :
Query 1:
SELECT DISTINCT tb_Emp_Master.FloorNumber
FROM tb_Emp_Master
WHERE (((tb_Emp_Master.User_Id)="fat35108"));
Query2:
SELECT DISTINCT tb_FloorNumber.FloorNumber, Query1.FloorNumber
FROM tb_FloorNumber LEFT JOIN Query1 ON tb_FloorNumber.FloorNumber = Query1.FloorNumber;
But I want to write this query with sing query instead of using Query1 inside the Query 2
I have tried like this:
SELECT DISTINCT tb_FloorNumber.FloorNumber, tb_Emp_Master.FloorNumber
FROM tb_FloorNumber LEFT JOIN tb_Emp_Master ON tb_FloorNumber.FloorNumber = tb_Emp_Master.FloorNumber
WHERE (((tb_Emp_Master.User_Id)="fat35108"));
But it brings only one record (For instance 8)
Please help me how to write this
If you set the condition:
tb_Emp_Master.User_Id = "fat35108"
in the WHERE clause, then you actually get an INNER JOIN instead of a LEFT JOIN because you filter only the matched rows from tb_Emp_Master.
Use tb_Emp_Master in the LEFT JOIN instead of Query1 and set the condition in the ON clause:
SELECT DISTINCT
tb_FloorNumber.FloorNumber,
tb_Emp_Master.FloorNumber
FROM tb_FloorNumber LEFT JOIN tb_Emp_Master
ON tb_FloorNumber.FloorNumber = tb_Emp_Master.FloorNumber AND tb_Emp_Master.User_Id = "fat35108";
I don't know why you need DISTINCT so I use it too.
The 1st query returns 27384 rows. The 2nd query returns 142899 rows. Can someone please explain what is happening with the RIGHT JOIN and LEFT JOIN that is causing the output difference?
1st query :
SELECT u.id AS id,
MIN(q.creation_date) AS q_creation_date,
MIN(a.creation_date) AS a_creation_date
FROM `bigquery-public-data.stackoverflow.posts_questions`AS q
FULL JOIN `bigquery-public-data.stackoverflow.posts_answers` AS a
ON q.owner_user_id = a.owner_user_id
LEFT JOIN `bigquery-public-data.stackoverflow.users` AS u
ON q.owner_user_id = u.id
WHERE u.creation_date >= '2019-01-01'
and u.creation_date < '2019-02-01'
GROUP BY id
2nd query :
SELECT u.id AS id,
MIN(q.creation_date) AS q_creation_date,
MIN(a.creation_date) AS a_creation_date
FROM `bigquery-public-data.stackoverflow.posts_questions` AS q
FULL JOIN `bigquery-public-data.stackoverflow.posts_answers` AS a
ON q.owner_user_id = a.owner_user_id
RIGHT JOIN `bigquery-public-data.stackoverflow.users` AS u
ON q.owner_user_id = u.id
WHERE u.creation_date >= '2019-01-01' and u.creation_date < '2019-02-01'
GROUP BY id
I expected the result from the 1st query to be 142899 rows but I don't know why the LEFT JOIN returns a massively different result.
The recordset produced by the 1st query includes ALL records of 'q' AND ALL records of 'a' (and where either table doesn't have data to match, the database will fill those empty cells with nulls) BUT LIMITED TO records where both 'q' and 'u' have a match.
So, in the 1st query, the recordset is basically limited by the rows in 'u'. The query will never return more than the maximum number of rows in 'u'.
The recordset produced by the 2nd query includes ALL records of 'q' AND ALL records of 'a' (and where either table doesn't have data to match, the database will fill those empty cells with nulls) AND ALSO ALL records of 'u' (and where either table doesn't have data to match, the database will fill those empty cells with nulls).
So, the 2nd query may produce a recordset with as many rows as the largest table.
When you use RIGHT JOIN the table with priority is always the one on the right. Similarly LEFT JOIN prioritizes the table to the left of JOIN. Thus the number of rows is different in that the priority table has the search data and the non-priority table does not have the required combination. More details here.
I need to limit the results of my query so that it only pulls results where the total number of lines on the ID is less than 4, and am unsure how to do this without losing the select statement columns.
select fje.journalID, fjei.ItemID, fjei.acccount, fjei.debit, fjei.credit
from JournalEntry fje
inner join JournalEntryItem fjei on fjei.journalID = fje.journalID
inner join JournalEntryItem fjei2 on fjei.journalID = fjei2.journalID and
fjei.ItemID != fjei2.ItemID
order by fje.journalID
So if journalID 1 has 5 lines, it should be excluded, but if it has 4 lines, I should see it in my query. Just need a push in the right direction. Thanks!
A subquery with an alias has many names, but it's effectively a table. In your case, you would do something like this.
select your fields
from your tables
join (
select id, count(*) records
from wherever
group by id ) derivedTable on someTable.id = derivedTable.id
and records < 4
I guess the problem comes down to this: what are some extreme scenarios where using a LEFT OUTER JOIN DOES NOT return the values as expected? Because in the result set I'm expecting the fields I joined on (item and ID) + any NULL values where the rows don't match, but item and ID don't show up.
Info:
qry_HersheySAPMaxDate2 returns 95 rows.
qry_HersheySAPMaxDate2 could have NULL values for MaxOfMaxOfjob_Date, SumOfSumOfqty_on_hand, product_code, and whse, whereas ID and item will always have a value.
qry_HersheySAPMaxDate3 returns 85 rows.
qry_HersheySAPMaxDate3 does not have any NULL values in any field, but excludes 10 id and item rows.
The query:
SELECT
qry_HersheySAPMaxDate3.ID,
qry_HersheySAPMaxDate3.item,
qry_HersheySAPMaxDate3.MaxOfMaxOfjob_date, qry_HersheySAPMaxDate3.SumOfSumOfqty_on_hand, qry_HersheySAPMaxDate3.product_code,
qry_HersheySAPMaxDate3.whse,
qry_HersheySAPMaxDate3.jobnumber
FROM
qry_HersheySAPMaxDate2
LEFT JOIN qry_HersheySAPMaxDate3 ON (qry_HersheySAPMaxDate2.item = qry_HersheySAPMaxDate3.item) AND (qry_HersheySAPMaxDate2.ID = qry_HersheySAPMaxDate3.ID);
Result set using my query + the suggestion in one of the answers to use LEFT OUTER JOIN instead:
Screenshot
You complain about your query producing entirely blank rows. Let's see why:
You outer join qry3 to qry2. That means when there is no match for a qry2 record in qry3, then a pseudo qry3 record with all columns set to null gets joined.
In your query you select only fields from qry3, so in an outer join case they are all null. Select qry2.ID and qry2.item instead of qry3.ID and qry3.item to see the values that have no match:
SELECT
qry_HersheySAPMaxDate2.ID,
qry_HersheySAPMaxDate2.item,
You should use LEFT OUTER JOIN if you want the NULL values to be included in the result
I am trying to filter a single table (master) by the values in multiple other tables (filter1, filter2, filter3 ... filterN) using only joins.
I want the following rules to apply:
(A) If one or more rows exist in a filter table, then include only those rows from the master that match the values in the filter table.
(B) If no rows exist in a filter table, then ignore it and return all the rows from the master table.
(C) This solution should work for N filter tables in combination.
(D) Static SQL using JOIN syntax only, no Dynamic SQL.
I'm really trying to get rid of dynamic SQL wherever possible, and this is one of those places I truly think it's possible, but just can't quite figure it out. Note: I have solved this using Dynamic SQL already, and it was fairly easy, but not particularly efficient or elegant.
What I have tried:
Various INNER JOINS between master and filter tables - works for (A) but fails on (B) because the join removes all records from the master (left) side when the filter (right) side has no rows.
LEFT JOINS - Always returns all records from the master (left) side. This fails (A) when some filter tables have records and some do not.
What I really need:
It seems like what I need is to be able to INNER JOIN on each filter table that has 1 or more rows and LEFT JOIN (or not JOIN at all) on each filter table that is empty.
My question: How would I accomplish this without resorting to Dynamic SQL?
In SQL Server 2005+ you could try this:
WITH
filter1 AS (
SELECT DISTINCT
m.ID,
HasMatched = CASE WHEN f.ID IS NULL THEN 0 ELSE 1 END,
AllHasMatched = MAX(CASE WHEN f.ID IS NULL THEN 0 ELSE 1 END) OVER ()
FROM masterdata m
LEFT JOIN filtertable1 f ON join_condition
),
filter2 AS (
SELECT DISTINCT
m.ID,
HasMatched = CASE WHEN f.ID IS NULL THEN 0 ELSE 1 END,
AllHasMatched = MAX(CASE WHEN f.ID IS NULL THEN 0 ELSE 1 END) OVER ()
FROM masterdata m
LEFT JOIN filtertable2 f ON join_condition
),
…
SELECT m.*
FROM masterdata m
INNER JOIN filter1 f1 ON m.ID = f1.ID AND f1.HasMatched = f1.AllHasMatched
INNER JOIN filter2 f2 ON m.ID = f2.ID AND f2.HasMatched = f2.AllHasMatched
…
My understanding is, filter tables without any matches simply must not affect the resulting set. The output should only consist of those masterdata rows that have matched all the filters where matches have taken place.
SELECT *
FROM master_table mt
WHERE (0 = (select count(*) from filter_table_1)
OR mt.id IN (select id from filter_table_1)
AND (0 = (select count(*) from filter_table_2)
OR mt.id IN (select id from filter_table_2)
AND (0 = (select count(*) from filter_table_3)
OR mt.id IN (select id from filter_table_3)
Be warned that this could be inefficient in practice. Unless you have a specific reason to kill your existing, working, solution, I would keep it.
Do inner join to get results for (A) only and do left join to get results for (B) only (you will have to put something like this in the where clause: filterN.column is null) combine results from inner join and left join with UNION.
Left Outer Join - gives you the MISSING entries in master table ....
SELECT * FROM MASTER M
INNER JOIN APPRENTICE A ON A.PK = M.PK
LEFT OUTER JOIN FOREIGN F ON F.FK = M.PK
If FOREIGN has keys that is not a part of MASTER you will have "null columns" where the slots are missing
I think that is what you looking for ...
Mike
First off, it is impossible to have "N number of Joins" or "N number of filters" without resorting to dynamic SQL. The SQL language was not designed for dynamic determination of the entities against which you are querying.
Second, one way to accomplish what you want (but would be built dynamically) would be something along the lines of:
Select ...
From master
Where Exists (
Select 1
From filter_1
Where filter_1 = master.col1
Union All
Select 1
From ( Select 1 )
Where Not Exists (
Select 1
From filter_1
)
Intersect
Select 1
From filter_2
Where filter_2 = master.col2
Union All
Select 1
From ( Select 1 )
Where Not Exists (
Select 1
From filter_2
)
...
Intersect
Select 1
From filter_N
Where filter_N = master.colN
Union All
Select 1
From ( Select 1 )
Where Not Exists (
Select 1
From filter_N
)
)
I have previously posted a - now deleted - answer based on wrong assumptions on you problems.
But I think you could go for a solution where you split your initial search problem into a matter of constructing the set of ids from the master table, and then select the data joining on that set of ids. Here I naturally assume you have a kind of ID on your master table. The filter tables contains the filter values only. This could then be combined into the statement below, where each SELECT in the eligble subset provides a set of master ids, these are unioned to avoid duplicates and that set of ids are joined to the table with data.
SELECT * FROM tblData INNER JOIN
(
SELECT id FROM tblData td
INNER JOIN fa on fa.a = td.a
UNION
SELECT id FROM tblData td
INNER JOIN fb on fb.b = td.b
UNION
SELECT id FROM tblData td
INNER JOIN fc on fc.c = td.c
) eligible ON eligible.id = tblData.id
The test has been made against the tables and values shown below. These are just an appendix.
CREATE TABLE tblData (id int not null primary key identity(1,1), a varchar(40), b datetime, c int)
CREATE TABLE fa (a varchar(40) not null primary key)
CREATE TABLE fb (b datetime not null primary key)
CREATE TABLE fc (c int not null primary key)
Since you have filter tables, I am assuming that these tables are probably dynamically populated from a front-end. This would mean that you have these tables as #temp_table (or even a materialized table, doesn't matter really) in your script before filtering on the master data table.
Personally, I use the below code bit for filtering dynamically without using dynamic SQL.
SELECT *
FROM [masterdata] [m]
INNER JOIN
[filter_table_1] [f1]
ON
[m].[filter_column_1] = ISNULL(NULLIF([f1].[filter_column_1], ''), [m].[filter_column_1])
As you can see, the code NULLs the JOIN condition if the column value is a blank record in the filter table. However, the gist in this is that you will have to actively populate the column value to blank in case you do not have any filter records on which you want to curtail the total set of the master data. Once you have populated the filter table with a blank, the JOIN condition NULLs in those cases and instead joins on itself with the same column from the master data table. This should work for all the cases you mentioned in your question.
I have found this bit of code to be faster in terms of performance.
Hope this helps. Please let me know in the comments.