Say I have 2 tables, one called categories and one called cat_pages.
The categories table has columns ID, title and timestamp. For example:
CREATE TABLE categories (
id INT UNSIGNED PRIMARY KEY,
title VARCHAR(32),
`timestamp` TIMESTAMP,
INDEX (title)
) Engine=InnoDB;
The cat_pages has 2 columns, cat_id and page_id:
CREATE TABLE cat_pages (
cat_id INT UNSIGNED
REFERENCES categories (id)
ON DELETE CASCADE ON UPDATE CASCADE,
page_id INT UNSIGNED
REFERENCES pages (id)
ON DELETE CASCADE ON UPDATE CASCADE,
UNIQUE INDEX (cat_id, page_id),
INDEX (page_id, cat_id),
) Engine=InnoDB;
I'm trying to join the categories table with the cat_pages table on the ID, such that
Only categories with id's in the category_pages table are retrieved and
Each category is only displayed once in the resultset
The query:
SELECT * FROM categories as c
LEFT JOIN cat_pages as p ON c.id = p.cat_id
produces a result set that has the categories repeated multiple times (as there are multiple matches in the cat_pages table. What do I need so that each category is only shown once, and not at all if there are no matches in the cat_pages table?
If you don't want categories that aren't in cat_pages, don't use a left join; use an inner join. A left join includes every row from the left table, even if there isn't a matching row in the right table (the missing fields are given NULL values). A right join is similar, but includes all rows from the right table. An outer join includes all rows from the left and right tables, joining rows that have matches and joining rows without matches with NULL values. An inner join, by contrast, only includes matching rows. To put it another way, the intersection of left and right joins is an inner join; their union is an outer join. Jeff Atwood posted some nice Venn diagrams describing joins, though it should be noted that the sets in the circles aren't properly the left and right tables, but rather the results of the left and right joins of the left and right tables.
To get distinct rows, you can use a DISTINCT modifier:
SELECT DISTINCT c.*
FROM categories AS c
INNER JOIN cat_pages AS cp ON c.id = cp.cat_id
As for SELECT * ..., see "What is the reason not to use select *?"
Another approach to getting distinct rows would be to use an EXISTS clause or IN operator, but the join is likely more performant (though only an EXPLAIN would tell you for certain). Just make sure you have appropriate indices set.
Why don't you use an Inner Join?
SELECT * FROM categories as c INNER JOIN cat_pages as p ON c.id = p.cat_id
Or
SELECT * FROM categories as c LEFT JOIN cat_pages as p ON c.id = p.cat_id WHERE p.cat_id IS NOT NULL
Left Join selects all on the left table and the matches on the right table.
Related
I have a table with a bunch of columns, but we only need to look at two of them. I'm trying to join another table on this table, but all we know about these two columns is that one will be null and the other won't:
client_id | note_id
The main table wants to join client_id (if not null) on clients.id OR note_id on notes.id if clients.id is null.
This will work for you. This is very basic query I wrote. Make changes if required.
SELECT * FROM YOUR_TABLE t
LEFT OUTER JOIN clients c ON t.client_id = c.id
LEFT OUTER JOIN notes n ON t.note_id = n.id
WHERE c.id IS NOT NULL OR n.id IS NOT NULL
Assuming there are 3 tables involved (the main table that contains client_id and note_id columns, clients table, and notes table), you can use a query such as this:
(select *
from mainTable inner join clients on mainTable.client_id = clients.id)
union
(select *
from mainTable inner join notes on mainTable.note_id = notes.id
where mainTable.client_id is NULL);
The above query contains 2 queries where each query will output rows where the joining column is not null. The results are then combined using union.
You can use coalesce in the join on clause. See demo here:
http://sqlfiddle.com/#!9/99911/2. If client id is null then use note id to join table1 and table2.
Select t1.client_id, t1.note_id,t2.client_id, t2.note_id
From table1 t1
Join table2 t2
on coalesce(t1.client_id, t1.note_id) =coalesce(t2.client_id, t2.note_id)
What is the correct way to write this query for mysql (this one seems to work, but seems idiotic) (191 is hard code for a variable)
select t1.item_id, t1.item_name, t1.item_desc, t.quantity, t.price
from (select * from items i where i.item_id = 191) as T1
LEFT JOIN (select * from item_properties ip) as T
on t1.item_id = t.fk_item_id and t1.item_id=191;
T1.item_id is PK, T.fk_item_id is foreign key (? -- only can exist if parent T1.item_id exists)
This was my way of returning t.values as null when they don't exist (and can't be joined).
Thanks
SELECT
i.item_id, i.item_name, i.idem_desc,
p.quantity, p.price
FROM items i
LEFT JOIN item_properties p ON i.item_id = p.fk_item_id
WHERE i.item_id = 191
Left join is required so rows from left table are always returned, even when there are no matching row in the right table. But those SELECT in the FROM were not necessary. Try to keep things simple when they are.
if you want to read how to implement joins correctly in mysql, read this
and if you want result only if matches in both tables use inner join for FK and PK
I am a newbie at MySQL..... I am trying to left join 3 tables one contains some_id,name,count,descr and second one has id,some_id,uni_id and the last one has uni_id,price,added,etc So when i try to join these three tables it says that there's no such field named descr
What could be the best way to join these tables without modifying structure of them?
Assuming the following schema:
table1(some_id, name, count, descr), where some_id is the primary key;
table2(id, some_id, uni_id), where some_id is a foreign key to table1;
table3(uni_id, price, added), where uni_id is a foreign key to table2.
All you need to do is a LEFT OUTER JOIN between the three tables:
SELECT *
FROM table1 t1 LEFT JOIN table2 t2 ON (t1.some_id = t2.some_id)
LEFT JOIN table3 ON (t2.uni_id = t3.uni_id)
References:
Left Outer Join
Join Syntax
It would be ideal if you could post the schema for your tables. Without seeing the query, it sounds like you've made a reference to a field that you may have aliased to the wrong table.
At the most basic level, "descr" doesn't exist as you've tried to reference it, but beyond that, its hard to say without seeing the query itself.
SELECT descr
FROM table1
LEFT JOIN table2 ON table2.some_id = table1.some_id
LEFT JOIN table3 ON table3.uni_id = table2.uni_id
Should do the trick.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Mysql: Perform of NOT EXISTS. Is it possible to improve permofance?
Is there a better/optimal way to do it. Should I use exists instead of join? Or two separate queries? And what about temporary tables, as I was reading about those but uncertain.
Getting members email from a group. Checking that they have not received a item yet.
SELECT m.email,g.id
FROM group g
LEFT JOIN members m
ON g.mid = m.id
AND g.gid='1'
WHERE NOT EXISTS
( SELECT id
FROM items AS i
WHERE i.mid=m.id
AND i.item_id='5'
)
Here's the same thing written as a JOIN:
SELECT m.email, g.id
From members m
JOIN group g ON g.mid = m.id AND g.gid = '1'
LEFT JOIN items i ON i.mid = m.id AND i.item_id = '5'
WHERE i.id IS NULL
Use the following compound indexes:
group (mid, gid)
items (mid, item_id)
I reversed the LEFT JOIN on members and group because it seems like you're returning members, not groups, and I changed the LEFT JOIN into an INNER JOIN since you only want members from that group.
I think this one might read better:
SELECT m.email, g.id
From members m
JOIN group g ON g.mid = m.id
LEFT JOIN items i ON i.mid = m.id AND i.item_id = 5
WHERE g.gid = 1
AND i.id IS NULL
You might be wondering if we can move the i.item_id = 5 part to the WHERE clause also. You can't because there are no rows where i.id IS NULL and i.item_id = 5. You must do the join first and then eliminate the NULL rows in the WHERE clause.
I don't believe a temporary table is necessary. We'd really only go that route if we can't get acceptable performance.
From your query, we gather your schema looks like this:
group (id INT PK, gid INT, mid INT)
items (id INT PK, item_id INT, mid INT)
members (id INT PK, email VARCHAR)
It looks like your group table is really a "membership" table, which resolves/implements a many-to-many relationship between a group and a person. (That is, a person can be a member of zero, one or more groups; a group can have zero, or or more persons as members.)
You are using a LEFT JOIN between group and members. This will return a row for group (returning group.id) when there are no matching members, with a NULL for members.email (which may be what you want). But if you only want to return email addresses, then this can be changed to an INNER JOIN.
The NOT EXISTS predicate can be replaced with an OUTER JOIN and a test for a NULL value returned from the JOINED table. If the group.gid and/or items.item_id columns are numeric datatype, then you can remove the quotes from around the integer literals in the predicates.
Here is an alternative which will return an equivalent resultset, and may perform better:
SELECT m.email
, g.id
FROM members m
JOIN group g ON g.mid = m.id AND g.gid = 1
LEFT
JOIN items i ON i.mid = m.id AND i.item_id = 5
WHERE i.id IS NULL
ADDENDUM:
TEST CASE (provided in comment on selected answer) demonstrates difference in result set between queries with the predicate items.item_id = 5 in the ON clause and in the WHERE clause. (Moving this predicate to the WHERE clause messes with the anti-join.)
CREATE TABLE `group` (`id` INT PRIMARY KEY, `gid` INT, `mid` INT);
CREATE TABLE `items` (`id` INT PRIMARY KEY, `item_id` INT, `mid` INT);
CREATE TABLE `members` (`id` INT PRIMARY KEY, `email` VARCHAR(40));
INSERT INTO `group` VALUES (1,1,1), (2,1,2);
INSERT INTO `items` VALUES (1,5,1);
INSERT INTO `members` VALUES (1,'one#m.com'),(2,'two#m.com');
I have a reference table (main) of products names and a few other tables with alternative names.
At this moment I have 2 tables of alternative names and I display those rows where a FK to the reference table from the table A exist only using
SELECT main.id,main.name,tabA.name,tabB.name FROM main INNER JOIN tabA ON tabA.fk=main.id LEFT JOIN tabB ON tabB.fk=main.id ORDER BY main.name
How to get all rows where a FK exist from any of the alternative tables?
SELECT main.id,main.name,tabA.name,tabB.name
FROM main
LEFT JOIN tabA ON tabA.fk=main.id
LEFT JOIN tabB ON tabB.fk=main.id
ORDER BY main.name