SQL Delete on inner join on MISSING data - mysql

My question is almost identical to SQL DELETE with INNER JOIN ; but I want to delete on non equal!
MY PROBLEM IN A BRIEF:
There are 2 tables, bus_stops, bus_routes ;
bus_routes {id, bus_route_id,..other columns..}
bus_stops {id, bus_route_id,..other columns..}
Some routes had been deleted, but bus stops remaining, I need to delete them too. Means, I need to delete only bus_stops, which have NO associated bus route!
It means something like:
DELETE bs.* FROM bus_stops AS bs
INNER JOIN bus_routes AS br
ON bs.bus_route_id <> br.bus_route_id
But the above code will definitely not work.

You should use LEFT JOIN, below query will work:
DELETE bs.*
FROM bus_stops AS bs
LEFT JOIN bus_routes AS br
ON bs.bus_route_id = br.bus_route_id
WHERE br.bus_route_id IS NULL

A join in SQL is first of all the Cartesian product of both tables. Meaning every record of table A is combined with every record of table B. The join condition then reduces the records by eleminating records that do not match the condition.
If you use an INNER JOIN with not equal (<>) every record is going to be deleted if you have at least to distinct values. A small example:
Table A | B Table C | D
============= =============
| 1 | 1
| 2 | 2
The Cartesian product of A X B is:
| B | D
==========
| 1 | 1
| 1 | 2
| 2 | 1
| 2 | 2
If you now use B <> C to select the values, the result will be:
| B | D
==========
| 1 | 2
| 2 | 1
This would delete both records.
As a solution try an outer join or a subquery.
Example (subquery):
DELETE FROM C WHERE NOT EXISTS(SELECT * FROM A WHERE A.B = C.d)
Example (outer join):
DELETE FROM C LEFT JOIN A ON C.D = A.B WHERE A.B IS NULL

Related

Select from table where id is not in another

I want to select all the planes that aren't belong to a certain company. I have three tables in this case: Planes, Companies, and CompanyPlanes.
Here is my query:
SELECT *
FROM planes p,
companyplanes cp,
companies c
WHERE c.id = ?
AND cp.idCompany != c.id
AND (cp.idPlane = p.id OR p.id NOT IN (SELECT idPlane FROM companyplanes))
ORDER BY name ASC
But this query returned nothing! what is the wrong here?
example:
| Plane |
---------
id | name
---------
1 | p1
2 | p2
3 | p3
|Company|
---------
id | name
---------
1 | c1
2 | c2
| companyPlanes |
------------------------
id | idCompany | idPlane
------------------------
1 | 1 | 1
2 | 1 | 2
3 | 2 | 2
if I want to get the planes that are not belong to the company c2 the result should be: p1, p3.
Update Answer
We can get the result in following way
Get all planes of the unexpected company
SELECT idplane from CompanyPlanes
WHERE idCompany = ?
Get all planes without those planes of the unexpected company
SELECT * FROM Planes
WHERE id NOT IN
(
SELECT idplane from CompanyPlanes
WHERE idCompany = ?
)
You don't need to join with Company table as you already get idCompany from CompanyPlanes table.
The inner join requires that the query return rows from planes which have a corresponding row in companyplanes but the subselect excludes any rows which have corresponding records in companyplanes.
Assuming that you want the records from planes which don't have a record in companyplanes, then why are you also selecting from companies?
Select p.*
From planes p
Left join
Companyplanes do
On p.id=cp.idplane
Where cp.idplane is null;
If I understand your question the right way, this is what you might be looking for..
select p.*
from planes p
join companyplanes cp on cp.idPlane=p.id
join companies c on c.id=cp.idCompany
where c.id != ?

SQL: Update table by mapping two columns to each other

I have the following two tables:
Table A
+-------------------+
|___User___|__Value_|
| 3 | a |
| 4 | b |
| 5 | c |
|____6_____|__d_____|
Table B
+-------------------+
|___User___|__Value_|
| 1 | |
| 4 | |
| 5 | |
|____9_____|________|
My job is to take user from Table A (and their correspondings value) and then map it to Table B and insert those values in there. So from the above example Table B should look like this after running the script:
Table B
+-------------------+
|___User___|__Value_|
| 1 | |
| 4 | b |
| 5 | c |
|____9_____|________|
My question is how can I construct an SQL query that will do this for me in an efficient way, if Table A contains 300,000 + entries and Table B contains 70,000 entries?
NOTES: In Table A the User field is not unique and neither is the Value field. However in Table B, both the User and Value fields are unique and should not appear more than once. Neither are primary keys for either tables.
Could be this
update table_b as b
inner join table_a as a on a.User = b.User
set b.value = a.value
In real-world situations, it would be more likely that you want a predictable value, such as the greatest value for any given user. In that case you would want
update table_b as b
inner join (
select user, max(value) from table_a
group by user ) as a_max on a.user = b.user
set b.value = a_max.value
Your question is unclear about what to do about any values that are already in b. If you use a left join, then these will explicitly be set to NULL:
update table_b b left join
table_a a
on a.User = b.User
set b.value = a.value;
If you want to keep the existing values for non-matches, then use inner join.
Note that this might be inefficient, but should be ok if an index exists on a(user).
If you had very few users in a and lots and lots of duplicates, then you might want to aggregate a before doing the join.

Incorrect results from three table join

I have these three tables in my database:
tblCustomer (id,name,address)
tblLoan (id,customerId,LoanAmount,date)
tblPayment (id,customerId,ReceivedAmount,date)
I want to find the total loanAmount for a customer and how much they have paid.
I wrote this query:
SELECT c.fname, SUM(l.amount), SUM(p.amount)
FROM tblCustomer c
JOIN tblLoan l ON (l.customerId = c.id)
JOIN tblPayment p ON (p.customerId = c.id)
WHERE c.id = 3;
It returns results but they are incorrect.
First, as others have mentioned, your syntax is likely incorrect because you do not have matching column names, but you said you had incorrect results, so I would assume that's not your problem as you were able to run your query..
The problem that I think you are most likely having is that by joining the two tables together like that, rows appear twice for each customer. Am I correct in assuming that your 'incorrect' results are double what you would expect? Let me illustrate for those who don't understand. Consider this data set, with shortened column values:
tblCustomer:
| id | name |
+----+------+
| 1 | Adam |
| 2 | John |
| 3 | Jane |
tblLoan, and for simplicity we'll say the payment table looks the same:
| customerID | loanAmount |
+------------+------------+
| 1 | 100 |
| 2 | 200 |
| 3 | 300 |
| 3 | 300 |
| 2 | 200 |
If I perform the following query (without summing values, just getting the values I want:
SELECT c.id, c.name, l.loanAmount, p.receivedAmount
FROM tblCustomer c
JOIN tblLoan l ON l.customerid = c.id
JOIN tblPayment p ON p.customerid = c.id
WHERE c.id = 3;
It returns this result set:
| id | name | loanAmount | receivedAmount |
+----+------+------------+----------------+
| 3 | Jane | 100 | 100 |
| 3 | Jane | 100 | 300 |
| 3 | Jane | 300 | 100 |
| 3 | Jane | 300 | 300 |
So notice that because we're joining two tables based on a relationship to a third table, were actually creating a cartesian product which is causing the problem. So, what I recommend you do is use subqueries for these two tables. One subquery will pull the loan values, one the payment values, and you can join those together on the id value.
It will look like this:
SELECT t.id, t.totalLoan, w.totalReceived
FROM(SELECT c.id, SUM(l.loanAmount) AS totalLoan
FROM tblCustomer c
JOIN tblLoan l ON l.customerid = c.id
WHERE c.id = 3) t
JOIN(SELECT c.id, SUM(p.receivedAmount) AS totalReceived
FROM tblCustomer c
JOIN tblPayment p ON p.customerid = c.id
WHERE c.id = 3) w
ON t.id = w.id;
And this should give you the values you want. Here is what I tested on SQL Fiddle.
FYI, YOUR COLUMN NAMES ARE WRONG!!!
There is no such column named fname in table tblCustomer
There is no such column named amount in table tblLoan
There is no such column named amount in table tblPayment
You won't get the right result if you don't have the appropriate column names. Even when using aliases, your column name should be EXACTLY THE SAME as in your database table. That's because, you are aliasing TABLES in JOIN queries, not COLUMNS.
So, re-write your query in the following way:
SELECT c.name, SUM(l.LoanAmount), SUM(p.ReceivedAmount)
FROM tblCustomer c
JOIN tblLoan l ON l.customerId = c.id
JOIN tblPayment p ON p.customerId = c.id
WHERE c.id = 3
Note that there's no need to get brackets around the ON clause in JOIN.

How do I inner join multiple tables?

I have tables A, B and C and I want to get matching values for from all tables (tables have different columns).
Table A (primary key = id)
+------+-------+
| id | name |
+------+-------+
| 1 | Ruby |
| 2 | Java |
| 3 | JRuby |
+------+-------+
Table B (pid is reference to A(id) - No primary key)
+------+------------+
| pid | name |
+------+------------+
| 1 | Table B |
+------+------------+
Table C (primary key = id, pid is reference to A(id))
+------+------+------------+
| id | pid | name |
+------+------+------------+
| 1 | 2 | Table C |
+------+------+------------+
So my below query returned nothing. Whats wrong here? Is it treated as AND when multiple inner joins present?
Select A.* from A
inner join B ON a.id = b.pid
inner join C ON a.id = c.pid;
As you first join
1 | Ruby | Table B
and then try to join Table C, there is no match for pid 2 in the aforementioned result, the result is therefore empty.
An inner join excludes everything that doesn't match. So after you joined against B, you were left with only one record (id=1). Your inner join against C doesn't have any matches from what's left, so you get nothing.
I suppose a union would do the trick:
select A.* from A join B on a.id = b.pid
union
select A.* from A join C on a.id = c.pid
Or there are other ways, like where a.id in (select pid from b) or a.id in (select pid from c)
When you inner-join like this, a single row from A needs to exist such that a.id = b.pid AND a.id = c.pid are true at the same time. If you examine the rows in your examples, you would find that there is a row in A for each individual condition, but no rows satisfy both conditions at once. That is why you get nothing back: the row that satisfies a.id = b.pid does not satisfy a.id = c.pid, and vice versa.
You could use an outer join to produce two results:
select *
from A
left outer join B ON a.id = b.pid
left outer join C ON a.id = c.pid;
a.id a.name b.pid b.name c.id c.pid c.name
1 | Ruby | 1 | Table B | NULL | NULL | NULL
2 | Java | NULL | NULL | 1 | 2 | Table C
Of course you return nothing!
Table A and B inner join returns 1st record of Table A (table A ID = 1)
then you join table C, there is no matching rows to join table C, and vice versa.

Select unrelated ids on relate / lookup table

I have seen various examples for this, but I can't find a definitive example of how to return all rows from table_A that are not related to a row in table_B using relate table_A_B without a given value for either table_A.id or table_B.id. The closest matchs I can find to this are:
Get a list of A not related to one or more rows in B but without knowing which value in B is the issue.
Get a list of A not related to given B.
Get list of A with comma-separated field of unrelated B (not sure I saw that, but it seemed like one example could expand as such).
A list of As and Bs not related to each other but no indication of which is not related to the other.
I can also get a list of all potential A_B tuples with:
SELECT A.id, B.id FROM A
INNER JOIN B ON A.id <> B.id
And I could hypothetically use one of the EXCEPT workarounds (I think) against the relate table, but all attempts were unsuccessful and I imagine that once there are millions of potential combinations returned by that join it will be much less efficient anyway.
So given the table values:
A
id | name
1 | X
2 | y
3 | z
B
id | name
7 | e
8 | f
9 | g
A_B
id | a_id | b_id
1 | 1 | 7
2 | 1 | 8
3 | 1 | 9
1 | 2 | 7
2 | 2 | 8
1 | 3 | 7
Is there a query that would return:
A | B
2 | 9
3 | 8
3 | 9
Or even better:
A | B
y | g
z | f
z | g
Or is this asking for trouble?
Start with a cross join between A and B to get all possible pairs. Then do a left join to the relation table and choose where there is no match:
select driver.aID, driver.bID
from (select a.id as aID, b.id as bID
from table_A A cross join table_B B
) driver left outer join
table_A_B ab
on ab.aID = driver.aID and ab.bID = bID
where ab.aID is null
This works, assuming that the ids are never NULL.
I haven't tested the SQL so it might have syntax errors.
This version gets you the names:
select driver.aName, driver.bName
from (select a.id as aID, b.id as bID, a.name as aName, b.name as Bname
from table_A A cross join table_B B
) driver left outer join
table_A_B ab
on ab.aID = driver.aID and ab.bID = bID
where ab.aID is null