MySQL Join AND EXISTS in combination - mysql

Case
I got the following query:
SELECT * FROM `parking_parking`
JOIN `parking_address` ON `parking_parking`.`parking_address` = `parking_address`.`address_id`
WHERE `parking_id` = 3
This query selects information about a parking (address and data about the parking itself)
And in general.. IT WORKS!
Problem
There is a small problem though..
Whenever the address has been deleted from the database and the parking itself still exists. The entire query returns 0. Simply because it looks for and A AND B as linked. But if one not found the second won't be returned either.
Now there is a solution..
EXISTS
However I do not know how to use it.
I tried:
EXISTS JOIN
JOIN EXISTS
JOIN `parking_address` ON EXISTS
But to no avail.
I hope (and guess) I have overlooked a small thing.
Note
!! I do not use this in real life! !!
SELECT * FROM
I did this one when I was still at the veeeery basics and I found out the hard way that even the simplest pages took ages to load.
Solution by : GolezTrol
SELECT * FROM `parking_parking`
LEFT JOIN `parking_address` ON `parking_parking`.`parking_address` = `parking_address`.`address_id`
WHERE `parking_id` = 3

Change join (which is short for inner join) to left join (= left outer join). This will return all parkings and will just return null for the address fields if there is no matching address:
SELECT * FROM `parking_parking`
LEFT JOIN `parking_address`
ON `parking_parking`.`parking_address` = `parking_address`.`address_id`
WHERE `parking_id` = 3

Related

MySQL - Why does WHERE ignore RIGHT JOIN?

I have the following MySQL query:
SELECT inv.inventory_id, inv.item_id, item.description, inv.quantity, item.class_id, class.description AS class,
class.is_spool, inv.location_id, location.description AS location, location.division_id, division.name AS division,
inv.service_date, inv.reel_number, inv.original_length, inv.current_length, inv.outside_sequential,
inv.inside_sequential, inv.next_sequential, inv.notes, inv.last_modified, inv.modified_by
FROM reel_inventory AS inv
INNER JOIN reel_items AS item ON inv.item_id = item.item_id
INNER JOIN reel_locations AS location ON inv.location_id = location.location_id
INNER JOIN locations AS division ON location.division_id = division.location_id
RIGHT JOIN reel_classes AS class on item.class_id = class.class_id;
The query works exactly as expected as is. What I was trying to do was add a WHERE clause to this query with one qualifier. For example:
RIGHT JOIN reel_classes AS class ON item.class_id = class.class_id
WHERE inv.current_length > 0;
When I do this, all of the results from the RIGHT JOIN are not included in the result. I've not had a ton of experience with advanced queries, but could someone explain why the RIGHT JOIN is excluded from the result set when a WHERE is used, and how to property write the query to include the RIGHT JOIN information?
Thanks in advance.
What you want is:
RIGHT JOIN reel_classes AS class
ON item.class_id = class.class_id AND
inv.current_length > 0;
Your question is why the RIGHT JOIN turns into an INNER JOIN with the WHERE clause.
The reason is simple. For the non-matching rows, inv.current_length is NULL and this fails the comparison.
I would also suggest that you use LEFT JOIN, starting with the table where you want to keep all the rows. Most people find it much easier to understand logic that is "keep all rows in the first table" rather than "keep all rows in some table whose name will come up".

Convert a MySQL NOT EXISTS into an INNER JOIN

I'm a little new to joins, so I'm not even sure if this is possible. I've been Googling and trying a few things..
What I need:
Select data.id where the corresponding user2data.user_id does not exist where user2data.user_id = 'X'
Exciting right? :D
What works:
SELECT * FROM data WHERE NOT EXISTS (SELECT * FROM user2data WHERE user2data.user_id=1 AND user2data.data_id=data.id) LIMIT 100;
However, it's slow, even though all 3 columns are indexed. I tried an OUTER JOIN for this purpose from another SO answer, but it's EVEN SLOWER than the above. What I need is an INNER JOIN.
Please let me know if this is actually possible, or if there is an alternative that takes advantage of the indexes.
Thanks and best
Could be you can use left join
SELECT *
FROM data WHERE
LEFT JOIN user2data ON ( user2data.user_id=1 AND user2data.data_id=data.id )
where user2data.data_id is null
LIMIT 100;

How can I filter out results based on another table. (A reverse join I guess?)

Basically, I have a table which contains two fields: [id, other] which have user tokens stored in them. The goal of my query is to select a random user that has not been selected before. Once the user is selected it is stored in the table shown above. So if Jack selects Jim randomly, Jack cannot select Jim again, and on the flip side, Jim cannot select Jack.
Something like this is what comes to mind:
SELECT * FROM users
WHERE (SELECT * FROM selected WHERE (id=? AND other=?) OR (id=? AND other=?));
Well, first of all I've read that uses sub-queries like this is extremely inneficient, and I'm not even sure if I used the correct syntax, the problem is however, that I have numerous tables in my scenario which I need to filter by, so it would look more like this.
SELECT * FROM users u
WHERE (SELECT * FROM selected WHERE (id=? AND other=?) OR (id=? AND other=?))
AND (SELECT * FROM other_table WHERE (id=? AND other=?) OR (id=? AND other=?))
AND (SELECT * FROM diff_table WHERE (id=? AND value=?))
AND u.type = 'BASIC'
LIMIT = 1
I feel like there's a much, much more efficient way of handling this.
Please note: I don't want a row returned at all if the users id is present in any of the nested queries. Returning "null" is not sufficient. The reason I have the OR clause is because the user's id can be stored in either the id or the other field, so we need to check both.
I am using Postgre 9.5.3, but I added the MySQL tag as the code is mostly backwards comptable, Fancy Postgre only solutions are accepted(if any)
You can left join to another table, which produces nulls where no record is found:
Select u.* from users u
left selected s on s.id = u.id or s.other = u.other
where s.id is null
The or in a join is different, but should work. Example is kinda silly...but as long as you understand the logic. Left join first table to second table, where second table column is not null means there was atleast one record found that matched the join conditions. Where second table column is null means no record was found.
And you are right...avoid the where field = (select statement) logic when you can, poor performer there.
Use an outer join filtered on missed joins:
SELECT * FROM users u
LEFT JOIN selected s on u.id in (s.id, s.other) and ? in (s.id, s.other)
WHERE u.id != ?
AND s.id IN NULL
LIMIT 1

mariadb - LEFT OUTER JOIN

recently have migrated a server, and I have found this "error", I had mysql as a DB, and what I wanted (I'm not an expert on SQL), was to join 2 related tables by 1:N, as an example,
Table 1: Badges_Person
Table 2: Badges
Badges is a table with the badges, and Badges_Person contains a relation like (id_badge, id_person), easy, uh?
Well this SQL query always seemed to work fine:
SELECT id, nombre, descripcion, insignias.time, obtained
FROM insignias LEFT OUTER JOIN
(SELECT *, '1' as obtained
FROM insignias_user
WHERE insignias_user.username = 'Octal'
) as insignias_user_seleccionado
ON insignias.id = insignias_user_seleccionado.id_insignia;
The output of this query was the list of badges with a 'obtained' column (0 or 1) which says if the user 'Octal' has that badge or not.
So..., now, I have mariadb as DB, and it returns a different output, where all the rows are being marked with 'obtained' = 1.
I came here because as far as I have tried I have discarded all the silly posible errors.
I cannot speak to why the query is not working. That would seem to be a data issue -- all the rows match.
But, there is a better way to write the query:
SELECT i.id, i.nombre, i.descripcion, i.time, ius.obtained
FROM insignias i LEFT OUTER JOIN
insignias_user iu
ON i.id = ius.id_insignia AND ius.username = 'Octal';
This is much more efficient because the intermediate table does not need to be materialized and the database can make use of appropriate indexes on insgnias_user.
Also note: I changed the column references to qualified column names. The table alias may not be correct.
SELECT i.id, i.nombre, i.descripcion, i.time, IF(ius.id_insignia IS NULL, 0, 1)
FROM insignias i LEFT OUTER JOIN insignias_user ius
ON i.id = ius.id_insignia AND ius.username = 'Octal';
Ok, it works again, thank you.

Select from 3 tables if result is empty

I'm using 3 tables to collect data from. The proces looks like:
User write VIN to form
Script search in table 1 for case_id and country base on that vin
number
After that he use case_id and country for search in table number 2
and get calculation id from there
Base on that calculation id and case id it search in 3th table
.
My script looks like this:
SELECT
cases.case_id,
cases.lastcalc_model_options,
cases.country,
calculations.calculation_id,
calculations.license,
positions.text
FROM cases
INNER JOIN calculations ON(cases.case_id =calculations.case_id
AND cases.country = calculations.country)
INNER JOIN positions ON(calculations.case_id = positions.case_id
AND calculations.calculation_id = positions.calculation_id)
WHERE vin ='ABCDEFGH'
This select work correctly, problem start when for example there is for example no result in table positions with that case_id and calculation_id. Instead of give back atleast everything it found in other tables it return NOTHING.
Is there a way to change this kind of komplex SELECT to return everything it found not return something only when every condition is TRUE?
Your problem is the INNER JOIN. Using INNER JOIN your result only contains entries present in all tables. Try using LEFT JOIN instead.
SELECT
cases.case_id,
cases.lastcalc_model_options,
cases.country,
calculations.calculation_id,
calculations.license,
positions.text
FROM cases
LEFT JOIN calculations ON(cases.case_id =calculations.case_id
AND cases.country = calculations.country)
LEFT JOIN positions ON(calculations.case_id = positions.case_id
AND calculations.calculation_id = positions.calculation_id)
WHERE vin ='ABCDEFGH'
See this stackoverlow answer for some more indepth information.
INNER JOIN returns rows from both tables only if there is a match between the columns in both tables.
You may try LEFT JOIN or FULL OUTER JOIN instead.