The opposite of INNER JOIN - sql

I have query with Inner Join.
Query 1:
select *
from vehicle_models vmodel
Inner join ogpo_voilure_model md on md.Name = vmodel.VEHICLE_MODEL
Now, i Need data that not exists in these id. With another word - opposite Inner JOIN.
I tried to make query that I need, but not successfull.
Query 2:
Select top 500 *
from ogpo_voilure_model md
Where md.id not in
(
select md.id
from Novelty.dbo.vehicle_models vmodel
Inner join [ogpo_voilure_model] md on md.Name = vmodel.VEHICLE_MODEL
)
I find in StakOverflow answer like this(sixth example). But my fields are not NULL.
How I can achieve it?

The inner join means you want everything is in set table vehicle_models but also got one or more correlated row in ogpo_voilure_model
If you define the oposite to it as everything is in vehicle_models but don't got a correlated in ogpo_voilure_model
All you needs is:
select *
from [Novelty].[dbo].[vehicle_models] vmodel
Where vmodel.VEHICLE_MODEL not in
(
select md.Name
from [Novelty].[dbo].[ogpo_voilure_model] md
)
And following that definition it's right even if results returns zero rows.
If it's not the right answer you must first define what the opposite of inner join means to you. For example you maybe want to swap the tables.

What do you mean by "my fields are not NULL"?
The link that you gave in the question has an answer: FULL JOIN.
SELECT *
FROM
vehicle_models vmodel
FULL JOIN ogpo_voilure_model md on md.Name = vmodel.VEHICLE_MODEL
WHERE
md.Name IS NULL
OR vmodel.VEHICLE_MODEL IS NULL
;
This will return rows from vehicle_models and ogpo_voilure_model that don't have common Vehicle Model Name.
It would help if you could add few sample rows to the question and your expected result.

Related

Minus operation gives wrong answer

I am trying to get the result of the minus operation of join tables which means that I am finding unmatched records.
I tried:
SELECT count(*) FROM mp_v1 mp
left join cm_v1 sop
on mp.study_name=sop.study_name and
sop.site_id=sop.site_id
--where mp.study_name='1101'
MINUS
SELECT count(*) FROM iv_mpv1 mp
inner join cm sop
on mp.study_name=sop.study_name and
sop.site_id=sop.site_id
--where mp.study_name='1101'
output: the count of this gives me 171183251
but when I run the first query individually I get 171183251 for left outer join and 171070345 for inner join so the output needs to be 112906. I am not sure where my query is wrong. Could anyone please give your opinion.
If you want unmatched records you wouldn't use MINUS on the counts. The query would look more like:
SELECT COUNT(*)
FROM ((SELECT *
FROM mp_v1 mp LEFT JOIN
cm_v1 sop
USING (study_name, site_id)
) MINUS
(SELECT *
FROM iv_mpv1 mp LEFT JOIN
iv_cmv1 sop
USING (study_name, site_id)
)
) x;
Also note that MINUS removes duplicates, so if you have duplicates within each set of tables, then they only count as one row.
The SELECT * assumes that the tables have the same columns and compatible types -- which makes sense given the gist of the question. You may need to list the particular columns you care about.

LEFT JOIN help in sql

I have to make a list of customer who do not have any invoice but have paid an invoice … maybe twice.
But with my code (stated below) it contains everything from the left join. However I only need the lines highlighted with green.
How should I make a table with only the 2 highlights?
Select paymentsfrombank.invoicenumber,paymentsfrombank.customer,paymentsfrombank.value
FROM paymentsfrombank
LEFT OUTER JOIN debtors
ON debtors.value = paymentsfrombank.value
You only want to select columns from paymentsfrombank. So why do you even join?
select invoice_number, customer, value from paymentsfrombank
except
select invoice_number, customer, value from debtors;
(This requires exact matches as in your example, i.e. same amount for the invoice/customer).
There are two issues in your SQL. First, you need to join on Invoice number, not on value, as joining on value is pointless. Second, you need to only pick those payments where there are no corresponding debts, i.e. when you left-join, the table on the right has "null" in the joining column. The SQL would be something like this:
SELECT paymentsfrombank.invoicenumber,paymentsfrombank.customer,paymentsfrombank.value
FROM paymentsfrombank
LEFT OUTER JOIN debtors
ON debtors.InvoiceNumber = paymentsfrombank.InvoiceNumber
WHERE debtors.InvoiceNumber is NULL
in mysql we usually have this way to flip the relation and extract the rows that dosen't have relation.
Select paymentsfrombank.invoicenumber,paymentsfrombank.customer,paymentsfrombank.value
FROM paymentsfrombank
LEFT OUTER JOIN debtors
ON debtors.value = paymentsfrombank.value where debtors.value is null
You can use NOT EXISTS :
SELECT p.*
FROM paymentsfrombank p
WHERE NOT EXISTS (SELECT 1 FROM debtors d WHERE d.invoice_number = p.invoice_number);
However, the LEFT OUTER JOIN would also work if you add filtered with WHERE Clause to filtered out only missing customers that haven't any invoice information :
SELECT p.invoicenumber, p.customer, p.value
FROM paymentsfrombank P LEFT OUTER JOIN
debtors d
ON d.InvoiceNumber = p.InvoiceNumber
WHERE d.InvoiceNumber IS NULL;
Note : I have used table alias (p & d) that makes query to easier read & write.

Why is LEFT JOIN deleting rows?

I have been using sql for a long time, but I am now working in Databricks and I am getting a very strange result. I have a table called block_durations with a set of ids (called block_ts), and I have another table called mergetable, which I want to left join to that table. Mergetable is indexed by acct_id and block_ts, so it has many different records for each block_ts. I want to keep the rows in block_durations that don't match, and if there are multiple matches in mergetable I want there to be multiple corresponding entries in the resulting join, as you would expect from a left join.
But this is not happening. In order to demonstrate this, I am showing the result of joining mergetable, after filtering for a single acct_id so that there is at most one match per block_ts.
select count(*) from mergetable where acct_id = '0xfbb1b73c4f0bda4f67dca266ce6ef42f520fbb98'
16579
select count(*) from block_durations
82817
select count(*) from
(
SELECT
mt.*,
bd.block_duration
FROM
block_durations bd
left outer JOIN mergetable mt
ON mt.block_ts = bd.block_ts
where acct_id='0xfbb1b73c4f0bda4f67dca266ce6ef42f520fbb98'
) countTable
16579
As you can see, even though there are >80000 records in block_durations, most of them are getting lost in the left join. Why is this happening? I thought the whole point of a left join is that the non-matching rows of the left table are kept. This is exactly the behavior I would expect from an inner join -- and indeed when I switch to an inner join nothing changes.
Could someone please help me figure out what's going on?
-Paul
All rows from left side of the join are preserved, but later on you run WHERE ... condition on that which removed rows not matching the condition.
Merge your WHERE condition into JOIN condition:
SELECT
mt.*,
bd.block_duration
FROM
block_durations bd
left outer JOIN mergetable mt
ON mt.block_ts = bd.block_ts AND acct_id='0xfbb1b73c4f0bda4f67dca266ce6ef42f520fbb98'
You can also filter mergetable before you run JOIN on the results:
SELECT
mt.*,
bd.block_duration
FROM
block_durations bd
left outer JOIN (SELECT * FROM mergetable WHERE acct_id='0xfbb1b73c4f0bda4f67dca266ce6ef42f520fbb98') mt
ON mt.block_ts = bd.block_ts

Getting all records including non matching records

I have the following tables
Payment
PayTypeId, Description
0 , Credit
1, Debit
2,Master
ActualPayment
Id,PayTypeId,Amount
1,1,10
Here is the output i am looking at
Id,PayTypeId,Amount
1,0,NULL
1,1,10
1,2,NULL
Basically I want all the records of ActualPayment including all payment types.
Here is the query i have used but am not getting any records
select
*
from #ActualPayments ap
left join #Payment p on ap.paytypeid = p.paytypeid
where p.paytypeid is null
If you want one record for each of the three PayTypeID values, then you need those three records on the left-hand side of the LEFT JOIN.
Equally, if you want the ActuallPaymentID on each output line, that value needs to be on the left hand side.
It's all leading you down the wrong avenue with the data that you have, and the tables that you have described.
With just those two tables in your question, I would use this layout instead...
SELECT
ap.ActualPaymentID,
p.PayTypeID,
SUM(CASE WHEN ap.PayTypeID = p.PayTypeID THEN ap.Amount END) AS Amount
FROM
ActualPayments AS ap
CROSS JOIN
Payment AS p
GROUP BY
ap.ActualPaymentID,
p.PayTypeID
You aren't receiving any records because you are filtering everything out with the WHERE clause p.paytypeid is null
Try running it without the WHERE clause.
Edit: The below SQL should return the correct information. I've used a CROSS JOIN to create an in-line view. This should remove the unwanted NULLs.
SELECT t1.id, t1.paytypeid, t2.amount
FROM (
SELECT id, payment.paytypeid
FROM #ActualPayments
CROSS JOIN #Payment
) t1
LEFT OUTER JOIN #ActualPayments t2
ON t1.paytypeid = t2.paytypeid
;
I think you want a FULL OUTER JOIN:
select
*
from #ActualPayments ap
full outer join #Payment p
on ap.paytypeid = p.paytypeid
;
This will return all rows from the ActualPayments table along with their corresponding values from Payment - if there is any. Additional it will return all rows from Payment for which no ActualPayments exist.
Please note: The where clause of your sample query must not be used!
I'm confused why you would want to do this, but here's one way
select ap.Id, pt.PayTypeId, ap2.Amount
from #ActualPayments ap
cross join #PaymentTypes pt
left join #ActualPayments ap2
on pt.PayTypeId = ap2.PayTypeId
and ap.Id = ap2.id

Select that finds IF multiple

I am doing a inner join between two tables where one is an association table, so there is a many to one relationship. I am trying to come up with a query that can decide if the key on the join exist more than once than store a value multiple in the update column, but not sure the efficient way to make this happen:
SELECT
MainTable.Name
FROM MainTable
INNER JOIN ASSN_Main ON MainTable.AppID = ASSN_Main.AppID
WHERE
EXISTS (SELECT
COUNT(MainTable.AppID)
FROM MainTable
INNER JOIN ASSN_Main ON MainTable.AppID = ASSN_Main.AppID
GROUP BY
MainTable.AppID
HAVING
(COUNT(MainTable.AppID)>1));
The problem is the subquery grabs the correct ones that have duplicates on the appid, but the main SELECT query grabs all appid names instead of only the ones that exist in the subquery. Not sure whats going wrong since the subquery is correct?
There is no relation between the items in the main query and the items in the subquery, so what the query is returning is "all items, if there are any items that have duplicates". What you want is "all items where there are duplicates":
SELECT
MainTable.Name
FROM MainTable m
INNER JOIN ASSN_Main a ON m.AppID = a.AppID
WHERE
EXISTS (SELECT AppID
FROM ASSN_Main
WHERE AppID = m.AppID
GROUP BY AppID
HAVING COUNT(*)>1);
I don't know access SQL, but something like this would work in SQL Server:
SELECT
MainTable.Name
FROM
MainTable INNER JOIN ASSN_Main ON MainTable.AppID = ASSN_Main.AppID
WHERE
MainTable.AppID IN
(SELECT
MainTable.AppID
FROM MainTable INNER JOIN ASSN_Main ON MainTable.AppID = ASSN_Main.AppID
GROUP BY
MainTable.AppID
HAVING
(COUNT(MainTable.AppID)>1));
So basically replacing the EXISTS with IN and return the AppID from the subquery.
I'm not really sure what you're trying to do, but to debug Access queries in general, I'd break it into multiple queries so you can see what's going on at each step. Then if you want you can combine them into one query that does all the steps.
Try changing the INNER JOIN to a WHERE clause. When you change it to a WHERE, ASSN_Main in the subquery will refer to the table from the parent query.
There's a good overview of the EXISTS clause here:
http://www.techonthenet.com/sql/exists.php