LEFT JOIN Duplicating Rows - sql

Table 1 has the join field (fieldY) duplicated many times within this table although every row in totality is unique.
When I try to run a left join I am getting 20x more rows than expected. I have tried to use solutions this post with no luck.
My expectation is that the join would yield exactly as many rows as table1 without the join. The join would just bring in one more column (fieldX)
Any ideas?
SELECT
table1.*, table2.fieldZ
FROM
table1
LEFT JOIN
table2
ON
table2.fieldX = table1.fieldY
WHERE
criteria1 = '01/01/2019'
AND
criteria2 > '0'
ORDER BY
criteria2
In below photo:
fieldz = Routing #
fieldX = Bank Account # (From table2)
fieldY = Bank Account # (From table1)
*This can be joined by student ID or Bank Account #, but the problem is the same regardless because both Student ID & Bank Account # appear multiple times in Table 1`

Table2 must have Table 1's key across multiple records. Needs to be 1:1 to achieve what you are describing.

With this query:
select t1.*, t2.routing
from table1 t1 left join table2 t2
on t2.studentid = t1.studentid
or this:
select t1.*, t2.routing
from table1 t1 left join table2 t2
on t2.bankaccount = t1.bankaccount
you get these results:
> studentid | bankaccount | gpa | semester | routing
> --------: | ----------: | ---: | :------- | :------
> 1 | 123456 | | Fall | abc
> 1 | 123456 | | Spring | abc
> 1 | 123456 | | Summer | abc
> 2 | 456789 | | Fall | def
> 2 | 456789 | | Spring | def
> 2 | 456789 | | Summer | def
> 3 | 321654 | | Fall | ghi
> 3 | 321654 | | Spring | ghi
> 3 | 321654 | | Summer | ghi
You can check the demo.

Related

difference between 'where' null and 'on' in a left join

Could someone explain to me why
select "talent".* from "talent"
left join "push" on "push"."talentId" = "talent"."id"
where ("push"."offerId" = '403' or "push"."offerId" is null)
yields less results than
select "talent".* from "talent"
left join "push" on "push"."talentId" = "talent"."id" and "push"."offerId" in ('403')
The way I see it, it should boil down to the same result, but it doesn’t, and I’m not sure what I miss to get it.
first one does not contain rows that have no entry in the push table.
I’d expect them to be caught by the or "push"."offerId" is null.
EDIT:
here is an example:
talent table
+----+------+
| id | name |
+----+------+
| 1 | John |
| 2 | Bob |
| 3 | Jack |
+----+------+
push table
+----+----------+---------+
| id | talentId | offerId |
+----+----------+---------+
| 1 | 1 | 403 |
| 2 | 1 | 42 |
| 3 | 2 | 123 |
| 3 | 2 | 456 |
+----+----------+---------+
With this data, the query with the where clause returns only
+----+------+---------+
| id | name | offerId |
+----+------+---------+
| 1 | John | 403 |
+----+------+---------+
while the one with the on condition returns all wanted rows
+----+------+---------+
| id | name | offerId |
+----+------+---------+
| 1 | John | 403 |
| 2 | Bob | null |
| 3 | Jack | null |
+----+------+---------+
The difference is when there is a match but on another row. This is best shown with a small example.
Consider:
t1:
x y
1 abc
1 def
2 xyz
t2:
x y
1 def
Then the left join version returns all three rows in t1:
select *
from t1 left join
t2
on t1.x = t2.x and t1.y = t2.y;
The filtering in the where clause version:
select *
from t1 left join
t2
on t1.x = t2.x
where t2.y = 'abc' or t2.y is null;
returns only one rows. The row that is returned is 1/abc. x = 2 matches in t2. So, t2.y is not null. And it is not 'abc' either. So it is filtered out.
Here is a db<>fiddle.
Yes, there is something you are missing.
WHERE and join conditions are only exchangeable for inner joins.
An outer join a LEFT JOIN b ON ... is defined as:
the result of the inner join
in addition, for every row in a that did not find a match that way, we get a result row where the b values are replaced with NULL.
So, no matter what the join condition is, the result will always contain at least one row for each value of a.
But a WHERE condition is evaluated (logically) after the join, so it can exclude rows from a from the query result.

How can I subtract two row's values within same column using sql query in access?

(query access)
This is the table structure:
+-----+--------+--------+
| id | name | sub1 |
+-----+--------+--------+
| 1 | ABC | 6.27% |
| 2 | ABC | 7.47% |
| 3 | PQR | 3.39% |
| 4 | PQR | 2.21% |
+-----+--------+--------+
I want to subtract Sub1
Output should be:
+-----+--------+---------+------------------------------------+
| id | name | sub1 | |
+-----+--------+---------+------------------------------------+
| 1 | ABC | 6.27% | 0 First Rec no need Subtract |
| 2 | ABC | 7.47% | 1.2% <=(7.47-6.27) |
| 3 | PQR | 3.39% | 0 First Rec no need Subtract |
| 4 | PQR | 2.21% | -1.18% <=(2.21-3.39) |
+-----+--------+---------+------------------------------------+
Thank you so much.
If you can guarantee consecutive id values, then the following presents an alternative:
select t.*, nz(t.sub1-u.sub1,0) as sub2
from YourTable t left join YourTable u on t.name = u.name and t.id = u.id+1
Change YourTable to the name of your table.
This is painful, but you can do:
select t.*,
(select top 1 t2.sub1
from t as t2
where t2.name = t.name and t2.id < t.id
order by t2.id desc
) as prev_sub1
from t;
This gives the previous value or NULL for the first row. You can just use - for the subtraction.
An index on (name, id) would help a bit with performance. However, if you can upgrade to a better database, you can then just use lag().

SQL Server join where not exist on other table

+-------------------+ +-------------------+ +---------------------+
| Service | | Asset | | AssetService |
+-------------------+ +-------------------+ +---------------------+
| Id | Name | | Id | Name | | AssetId | ServiceId |
|-------------------| |-------------------| |---------------------|
| 1 | Service 1 | | 1 | Asset 1 | | 1 | 1 |
| 2 | Service 2 | | 2 | Asset 2 | | 1 | 2 |
| 3 | Service 3 | | 3 | Asset 3 | | 2 | 2 |
+-------------------+ +-------------------+ | 2 | 3 |
+---------------------+
So I have these tables. I want to get the Services that is not on AssetService where AssetId = 1
Like this:
+-------------------+
| Service |
| Id | Name |
+-------------------+
| 3 | Service 3 |
+-------------------+
Is this possible with just inner/left/right join? because I already tried different combinations of inner join but it's not working, like this inner join Asset a on a.Id != as.AssetId. I event tried left and right join.
Can somebody help me?
Thanks.
You can you use an intelligent left join to return non-matching rows only from left table(Service)
SELECT S.Id, S.Name FROM [Service] S
LEFT JOIN ServiceAsset SA
ON S.Id = SA.ServiceId
WHERE SA.ServiceId IS NULL
Note: INNER JOIN returns the matching rows whereas you want the non matching rows then use LEFT JOIN instead
The simplest I can think of:
select * from Service
where Id not in (
select ServiceId
from AssetService
where AssetId = 1);
SQLFiddle link
I don't think it's possible using inner join, because that would only retrieve records that match some criteria and you are looking for records that do not match.
It is, however, possible to do it with left join as Ctznkane525 shows in his answer.
Edit
As jarlh pointed out in the comments, not in might lead to surprising results when there are nulls in the subquery. So, here is the not exists version:
select Id, Name
from Service s
where not exists (
select *
from AssetService a
where AssetId = 1
and ServiceId = s.Id);
SQLFiddle link
Try this:
select * from Service where Id not in (
select ServiceId from AssetService where AssetId = 1
-- we have to filter out NULLs, in case of NULL values query result will be empty
and ServiceId not null
)
It doesn't require any join.
Here is solution with join:
select Id, Name from Service
except
select S.Id, S.Name from Service S join AssetService [AS] on S.Id = [AS].ServiceId
where [AS].AssetId = 1

how to get a Distinct Count of users from two related but different tables

Apologies for this but SQL is not a strong point for me, and whilst appears similar to lots of other queries I cannot translate those to this situation successfully.
I have two tables that will be related by a common value (id and Issue) if a row in table 2 exists.
I need to get a distinct count of users raising particular issues. I have users in both tables, with the table 2 user taking precedence if it exists.
There is always a REPORTER in Table 1, but there may not be a Stringvalue of Name (fieldtype = 1) in table 2. If there is a Stringvalue then that is the "User" and the Reporter can be ignored.
Table 1
| id | Reporter| Type |
| 1 | 111111 | 1 |
| 2 | 111111 | 2 |
| 3 | 222222 | 2 |
| 4 | 333333 | 1 |
| 5 | 111111 | 1 |
| 6 | 666666 | 1 |
Table 2
|issue | Stringvalue | fieldType|
| 1 | Fred | 1 |
| 1 | bananas | 2 |
| 2 | Jack | 1 |
| 5 | Steve | 1 |
I have a total of 4 issues of the right type (1,4,5,6), three reporters (111111,333333,666666) and two Stringvalues(Fred, Steve).
My total count of Distinct Users = 4 (Fred, 333333, Steve, 666666)
Result Table
| id| T1.Reporter | T2.Name |
| 1| Null | Fred |
| 4| 333333 | Null |
| 5| Null | Steve |
| 6| 666666 | Null |
How do I get this result in SQL!
Closest try so far:
SELECT
table1.REPORTER,
TO_CHAR(NULL) "NAME"
FROM table1
Where table1.TYPE =1
AND table1.REPORTER <> '111111'
Union
SELECT
TO_CHAR(NULL) "REPORTER",
table2.STRINGVALUE "NAME"
FROM table2,
table1
WHERE table2.ISSUE = table1.ID
AND table2.fieldtype= 1
and table1.issuetype = 1
Without explicitly excluding the default table 1 Reporter, this gets returned in my results even when there is a name value in table 2.
I have tried exists and in but cannot get syntax right or the correct results. As soon as try any Join that links the ID and Issue values the results always end up constrained to the matching rows or for all values. And added additional conditions to the ON does not return correct results.
I have tried too many permutations to list, logically this sounds like should be able to do union with where exists, or left outer join but my skills are lacking to make this work.
You need to use a LEFT JOIN and that is where you specify the fieldtype = 1 clause:
SELECT
table1.id,
CASE
WHEN table2.Stringvalue IS NOT NULL THEN table2.Stringvalue
ELSE table1.Reporter
END AS TheUser
FROM table1
LEFT JOIN table2 ON table1.id = table2.issue AND table2.fieldType = 1
WHERE table1.Type = 1
Result:
+------+---------+
| id | TheUser |
+------+---------+
| 1 | Fred |
| 4 | 333333 |
| 5 | Steve |
| 6 | 666666 |
+------+---------+
If I understand correctly, you want a left join and count(distinct). Here is what I think you are looking for:
select count(distinct coalesce(stringval, reporter) )
from table1 t1 left join
table2 t2
on t1.id = t2.issue and t2.fieldtype = 1
where t1.id in (1, 4, 5, 6);
You need to learn how to use explicit JOIN syntax. As a simple rule: Never use commas in the FROM clause. Always use explicit JOIN syntax. For one thing, it is more powerful, making it easy to express outer joins.

Oracle SQL - Making a one to many join one to one based on logic

Sorry for the broad title, I had a hard time coming up with a brief way of describing what I am looking to do. I have two tables (examples below) that I want to join but under a certain condition.
The main table has a field called "DateVal", the second table has a field called "Day". After joining on field "JoinField" I only want to keep rows where the day value in "DateVal" is less than the value of "Day". However, if this criteria is met for multiple values of "Day" I only want it to keep the first instance.
In the second table below, for JoinField "A" there are three rows, for the first I only want it to return times when the day of the month is between 1-10, the second only with the day of the month is between 11-20, and the last 20-31.
A left or inner join will bring back all values, the only way I can think of to get around this is to do a complete join and only return for min("Day"). Can anyone think of a more efficient way?
Thanks in advance.
Table 1
-------------------------------
| ID | JoinField | DateVal |
-------------------------------
| 1 | A | 01/01/2014 |
| 2 | A | 01/16/2014 |
| 3 | B | 05/20/2013 |
-------------------------------
Table 2
--------------------------------
| JoinField | Day | FieldToAdd |
--------------------------------
| A | 10 | A |
| A | 20 | AA |
| A | 31 | AAA |
| B | 15 | B |
| B | 31 | BB |
--------------------------------
Desired Results
--------------------------------------------
| ID | JoinField | DateVal | FieldToAdd |
--------------------------------------------
| 1 | A | 01/01/2014 | A |
| 2 | A | 01/16/2014 | AA |
| 3 | B | 05/20/2014 | BB |
--------------------------------------------
You can do this in a variety of ways. I think a correlated subquery is the easiest way to express it, but unfortunately, the following doesn't work in Oracle:
select t1.*,
(select *
from (select t2.*
from table2 t2
where t2.day < extract(day from t1.dateval)
order by t2.day desc
) t
where rownum = 1
)
from table1 t1;
You can instead do this with join fancy window functions:
select *
from (select t1.*,
row_number() over (partition by t1.id order by t2.day desc) as seqnum
from table1 t1 left outer join
table2 t2
on t2.day < extract(day from t1.dateval)
) t
where seqnum = 1;