Show only one result in sql LEFT JOIN - mysql

Table A
id | kind | item_name
---+-------+----------
1 | one | item_1
2 | one | item_2
Table B
tableA_id | kind | price
----------+-------+------
1 | all | $10
1 | one | $2
then i have a query like this
SELECT a.id, a.kind, a.name, b.price FROM table_a a
LEFT JOIN table_b b ON b.tableA_id = a.id
AND (a.kind = b.kind OR a.kind = 'all')
WHERE a.id = 1
Basically i want to show the price of all if the kind does not exist in table b, however if the kind is exist then show only the price one as example. but my query show both all and one.
The result of the about query is like this:
id | kind | item_name | price
---+-------+-----------+------
1 | all | item_1 | $10
1 | one | item_1 | $2
I expect this
id | kind | item_name | price
---+-------+-----------+------
1 | one | item_1 | $2

Update:
Basically i want to show the price of all if the kind does not exist
in table b, however if the kind is exist then show only the price one
as example.
For this, you can use CASE expression to do this:
SELECT a.id, a.kind, a.item_name,
CASE WHEN b.price IS NULL
THEN (SELECT b2.Price
FROM table_b AS b2
WHERE b2.kind = 'all')
ELSE b.price
END AS PRice
FROM table_a a
LEFT JOIN table_b b ON b.tableA_id = a.id
AND b.kind = 'one'
WHERE a.id = 1
updated fiddle demo

This should work in theory, but you're doing two joins:
SELECT a.id, a.kind, a.name, COALESCE(b1.price, b2.price) AS price
FROM table_a AS a
LEFT JOIN table_b AS b1
ON b1.tableA_id = a.id
AND b1.kind = a.kind
LEFT JOIN table_b AS b2
ON b1.tableA_id = a.id
AND b1.kind = 'all'
WHERE a.id = 1;
Could be more efficient. Basically you're first finding whether there is something with matching kind price and fallback to 'all' price.
Also - if table_b is always going to have item price for 'all' you could change that left join to inner join.

To pick single row for your scenario you would need another self join in your query with additional comparison check to pick single row if another kind exist in table b else show a row with kind = all
SELECT a.id, a.kind, a.item_name, b.price
FROM table_a a
INNER JOIN table_b b ON b.tableA_id = a.id
LEFT JOIN table_b b1 ON b.tableA_id = b1.tableA_id
AND b.kind < b1.kind
WHERE b1.tableA_id IS NULL;
DEMO where kind exist
DEMO where kind doesn't exist
Make sure the keyword is 'all' starts with a then this b.kind < b1.kind can work properly because if you do
select 'all' < 'one'; // will return true
select 'all' > 'one'; // will return false
Demo

Related

Handle null in sql SELECTS with JOIN ON

Lets put I have these 3 tables:
main table:
ID | OtherStuff1 | OtherStuff2 | IdProvince | IdTown
-----+-------------+-------------+------------+--------
1 | Stuff1 | Stuff2 | NULL | 1
-----+-------------+-------------+------------+--------
2 | Stuff3 | Stuff4 | 1 | NULL
province table:
ID | ProvinceName
---+--------------
1 | ProvName1
town table:
ID | TownName
---+--------------
1 | TwName1
Then I use one of these sql to print the information so it appears the name instead of the Id number.
SELECT a.Id, OtherStuff1, OtherStuff2, ProvinceName, TownName
FROM main AS a
LEFT JOIN (province AS b, town AS c)
ON (a.IdProvince = b.Id AND a.IdTown = c.Id)
WHERE a.IdProvince=1;
or
SELECT a.Id, OtherStuff1, OtherStuff2, ProvinceName, TownName
FROM main AS a
LEFT JOIN (province AS b, town AS c)
ON (a.IdProvince = b.Id AND a.IdTown = c.Id)
WHERE a.IdTown=1;
The problem comes that when it prints the result it comes like this:
ID | OtherStuff1 | OtherStuff2 | ProvinceName | TownName
-----+-------------+-------------+--------------+--------
1 | Stuff1 | Stuff2 | NULL | NULL
If one of the id's is put in NULL then even if there are other id's with numbers it puts NULL too. How do I avoid that without just delete the join of that column that is null likes this:
SELECT a.Id, OtherStuff1, OtherStuff2, ProvinceName, IdTown
FROM main AS a
LEFT JOIN (province AS b)
ON (a.IdProvince = b.Id)
WHERE a.IdProvince=1;
This would work if IdTown is NULL but I don't want this solution because there are much many rows and a lot other columns with Id's and it's impossible to go one by one checking which one is NULL and which one is not.
So I want a query like this but when there is a null it doesn't mess the other Id's putting all of them in NULL.
SELECT a.Id, OtherStuff1, OtherStuff2, ProvinceName, TownName
FROM main AS a
LEFT JOIN (province AS b, town AS c)
ON (a.IdProvince = b.Id AND a.IdTown = c.Id)
WHERE a.IdProvince=1;
Sorry the expected result for this query would be like this:
ID | OtherStuff1 | OtherStuff2 | ProvinceName | TownName
-----+-------------+-------------+--------------+--------
2 | Stuff1 | Stuff2 | ProvName1 | NULL
Any ideas?
Thanks in advance for the help!
don't mix join notations (a from clause should avoid the use of , if you're using the ANSI92 standards. If using the ANSI 89 standards, then there should be no left join syntax)
I see no reason not to do two left joins back to main table (A) one for Province and one for Town...
.
SELECT a.Id, OtherStuff1, OtherStuff2, ProvinceName, TownName
FROM main AS a
LEFT JOIN province AS b
ON a.IdProvince = b.Id
LEFT JOIN town as c
ON a.IdTown = c.Id
WHERE a.IdProvince=1;
This is because the condition in your ON clause:
a.IdProvince = b.Id AND a.IdTown = c.Id
Here you are asking to LEFT JOIN 2 tables only when both conditions are met, which in your case they don't.
I would try to keep the 2 joins separate like so:
SELECT a.Id, OtherStuff1, OtherStuff2, b.ProvinceName, c.TownName
FROM main AS a
LEFT JOIN province AS b ON (a.IdProvince = b.Id)
LEFT JOIN town AS c ON (a.IdTown = c.Id)
WHERE a.IdProvince = 1;

How to find exact match item with AND condition from left outer join

I have a MySQL table
Booktable
+--------+-------------+-----+
| bookno | bookname | ... |
+--------+-------------+-----+
| 1 | FINALFANTASY| ... |
+--------+-------------+-----+
Authortable
+--------+-------------+-----+
| bookno | Authorname | ... |
+--------+-------------+-----+
| 1 | SQUARE | ... |
+--------+-------------+-----+
| 1 | ENIX | ... |
+--------+-------------+-----+
so I would like to make a search condition to get the book that match with the result.
I try with
select b.bookname,a.authorname from booktable as b
left outer join authortable a on b.bookno = a.bookno
where a.authorname = "square" and a.authorname = "enix"
It only work with only one where condition.but when I try with two authorname there is no result found. what should I do ?
(this query it working with "OR" but not "AND" but I really want the value that match the search condition or if there are some search condition that not match but not blank it should not be showing(so or it not working in this case)
Use aggregation to identify which books have both the authors you want:
SELECT t1.bookname,
t2.authorname
FROM booktable t1
INNER JOIN authortable t2
ON t1.bookno = t2.bookno
INNER JOIN
(
SELECT bookno
FROM authortable
WHERE authorname IN ('square', 'enix')
GROUP BY bookno
HAVING COUNT(DISTINCT authorname) = 2
) t3
ON t1.bookno = t3.bookno
Demo here:
SQLFiddle
Tim Biegeleisen's answer is great, but in case you need exactly match, the the last SQL in the following is correct:
SELECT * FROM book;
SELECT * FROM author;
/* this SQL will return book's author name more than 2 also true */
SELECT b.bookname, a.authorname
FROM book AS b
JOIN author AS a ON b.bookno = a.bookno
JOIN (
SELECT bookno FROM author
WHERE authorname in ('SQUARE', 'ENIX')
GROUP BY 1
HAVING count(*) = 2
) AS a2 ON b.bookno = a2.bookno;
/* this sQL will return only 2 and all matched authors: */
SELECT b.bookname, a.authorname
FROM book AS b
JOIN author AS a ON b.bookno = a.bookno
JOIN (
SELECT bookno FROM author
WHERE authorname in ('SQUARE', 'ENIX')
GROUP BY 1
HAVING count(*) = 2
) AS a2 ON b.bookno = a2.bookno
JOIN (
SELECT bookno FROM author
GROUP BY 1
HAVING count(distinct authorname) = 2
) AS a3 ON b.bookno = a3.bookno
PS1 - no need left join
PS2 - no need count distinct - unless your author table not design properly
If title is FANTASY genre is Adventure,fantasy, and search condition is
[ADVENTURE] = found
[FANTASY] = found
[ADVENTURE,FANTASY] = found
[ADVENTURE,FANTASY,ACTION] = not found
Then the SQL will be:
SELECT b.bookname, a.authorname
FROM book AS b
JOIN author AS a ON b.bookno = a.bookno
JOIN author AS a1 ON b.bookno = a1.bookno AND a1.authorname = 'SQUARE'
JOIN author AS a2 ON b.bookno = a2.bookno AND a2.authorname = 'ENIX'
Above is working, and I m wondering if there is a performance improvement

Mysql nested select with multiple joins with condition on join table

I've got a SELECT with multiple JOINS for a paginated Tableview. In general this is working for unfiltered results.
The query looks like this:
SELECT seltable.*,
tbl2.name AS tbl2name,
tbl3.name AS tbl3name,
tbl4.name AS tbl4name
FROM
( SELECT * FROM selecttable
WHERE value = 99
ORDER BY datetime DESC
LIMIT 50 OFFSET 0 )
AS seltable
LEFT JOIN table1 AS tbl1 ON seltable.tbl1_uid = tbl1.uid
LEFT JOIN table2 AS tbl2 ON tbl1.tbl2_uid = tbl2.uid
LEFT JOIN table3 AS tbl3 ON tbl2.tbl3_uid = tbl3.uid
LEFT JOIN table4 AS tbl4 ON tbl3.tbl4_uid = tbl4.uid;
Now I've got no clue how to accomplish filtering the results with a condition related to one of the join tables.
When I just set a:
LEFT JOIN tablex AS table ON foreign_table.tblx_uid = table.uid AND {condition}
this condition regards only to the 50 results of the nested SELECT.
Is there any way to achieve using WHERE clauses on the JOIN tables in this scenario?
For sample data see http://sqlfiddle.com/#!2/fad4d/2
Expected results:
to get x team records limited to 5 team uids, where Tournament2 is one of the related tournaments for the team.
Best regards
w1ll1
Try not controlling the pagination in that subquery, instead just use a more conventional query with a composite where clause. HOWEVER, because you are using left joins take care adding filters through the where clause that would override the outer join to produce the effect of an inner join.
SELECT seltable.*,
tbl2.name AS tbl2name,
tbl3.name AS tbl3name,
tbl4.name AS tbl4name
FROM selecttable AS seltable
LEFT JOIN table1 AS tbl1 ON seltable.tbl1_uid = tbl1.uid
LEFT JOIN table2 AS tbl2 ON tbl1.tbl2_uid = tbl2.uid
LEFT JOIN table3 AS tbl3 ON tbl2.tbl3_uid = tbl3.uid
LEFT JOIN table4 AS tbl4 ON tbl3.tbl4_uid = tbl4.uid
WHERE seltable.value = 99
...
ORDER BY seltable.datetime DESC
LIMIT 50 OFFSET 0
Alternatively use more subqueries, like this:
SELECT seltable.*,
tbl2.name AS tbl2name,
tbl3.name AS tbl3name,
tbl4.name AS tbl4name
FROM
( SELECT * FROM selecttable
WHERE value = 99
ORDER BY datetime DESC
LIMIT 50 OFFSET 0 )
AS seltable
LEFT JOIN ( SELECT uid, name
FROM table1
WHERE 1=1 -- amend to suit
) AS tbl1 ON seltable.tbl1_uid = tbl1.uid
LEFT JOIN ( SELECT uid, name
FROM table2
WHERE 1=1 -- amend to suit
) AS tbl2 ON tbl1.tbl2_uid = tbl2.uid
LEFT JOIN ( SELECT uid, name
FROM table3
WHERE 1=1 -- amend to suit
) AS tbl3 ON tbl2.tbl3_uid = tbl3.uid
LEFT JOIN ( SELECT uid, name
FROM table4
WHERE 1=1 -- amend to suit
) AS tbl4 ON tbl3.tbl4_uid = tbl4.uid;
Here is another attempt, based on your sqlfiddle it appears that INNER JOINS may be used:
SELECT theteam.*,
trnmnt.name AS tournamentname,
cat.name AS categoryname,
sport.name AS sportname
FROM (
SELECT * FROM team
ORDER BY team.name ASC )
AS theteam
INNER JOIN tournament_team AS tntm ON tntm.team_uid = theteam.uid
INNER JOIN tournament AS trnmnt ON tntm.tournament_uid = trnmnt.uid AND trnmnt.name = 'Tournament2'
INNER JOIN category AS cat ON trnmnt.category_uid = cat.uid
INNER JOIN sport ON cat.sport_uid = sport.uid
LIMIT 5 OFFSET 0
;
The result of that query is:
| UID | NAME | TOURNAMENTNAME | CATEGORYNAME | SPORTNAME |
|-----|--------|----------------|--------------|-----------|
| 2 | Team02 | Tournament2 | Germany | Soccer |
| 3 | Team03 | Tournament2 | Germany | Soccer |
| 4 | Team04 | Tournament2 | Germany | Soccer |
| 5 | Team05 | Tournament2 | Germany | Soccer |
| 6 | Team06 | Tournament2 | Germany | Soccer |

Why my right join isn't working?

I need to show all categories, even categories with no items.
I have this query.
SELECT
i.id,
incident_active 'Approved',
incident_verified 'Verified',
category_title 'Category',
ParentCategory 'Parent Category'
FROM
incident i
INNER JOIN
incident_category ic ON i.id = ic.incident_id
RIGHT JOIN
incident_person ip ON i.id = ip.incident_id
RIGHT JOIN
(SELECT
c1.id,
c1.parent_id,
c2.category_title ParentCategory,
CONCAT_WS(' -> ', c2.category_title, c1.category_title) category_title
FROM
category c1
left outer join category c2 ON c1.parent_id = c2.id WHERE c1.parent_id != 0) AS c ON c.id = ic.category_id
WHERE incident_dateadd > DATE_SUB(NOW(), INTERVAL 1 MONTH)
which return:
and this query:
SELECT
c1.id,
c1.parent_id,
c2.category_title ParentCategory,
CONCAT_WS(' -> ', c2.category_title, c1.category_title) category_title
FROM
category c1
left outer join category c2 ON c1.parent_id = c2.id WHERE c1.parent_id != 0
which return:
I've read several times this answer but I can not see why my right join isn't working.
The first result set should have 8 more columns, the columns of categories which parent is Protesta
UPDATE
I got it working whith the following query:
SELECT * FROM (SELECT
i.id,
incident_title 'Título',
incident_description 'Descripción',
incident_date 'Fecha',
incident_active 'Aprobado',
incident_verified 'Veficado',
person_first 'Nombres',
person_last 'Apellidos',
person_email 'Email',
category_id
-- category_title 'Categoría',
-- ParentCategory 'Categoría Padre'
FROM
incident i
INNER JOIN
incident_category ic ON i.id = ic.incident_id
RIGHT JOIN
incident_person ip ON i.id = ip.incident_id
WHERE (incident_dateadd > DATE_SUB(NOW(), INTERVAL 1 MONTH) OR incident_dateadd IS NULL)) a
RIGHT JOIN
(SELECT
c1.id,
c1.parent_id,
c2.category_title ParentCategory,
CONCAT_WS(' -> ', c2.category_title, c1.category_title) category_title
FROM
category c1
left outer join category c2 ON c1.parent_id = c2.id WHERE c1.parent_id != 0) b ON a.category_id = b.id
Although I still don't understand why it was not working with the first version, in my mind both queries are equivalent.
If anyone could explain the differences...
It's the location of your final where clause.
In your fist query, you pull all of your categories and associate them with a bunch of data, getting a compilation of rows. You then use a where clause to filter out many of those rows, some of which happen to be category rows.
Let's look at a simple example.
Table A:
X | Y
-----
1 | hi
2 | bye
3 | what
Table B:
Z | X
-----
A | 1
B | 1
C | 2
Given these tables, if I say the following
SELECT * FROM `B` RIGHT JOIN `A` ON A.X = B.X
my result will be:
Z | X | Y
---------
A | 1 | hi
B | 1 | hi
C | 2 | bye
- | 3 | what
If, however, I add a where clause on the end of that so my query becomes
SELECT * FROM `B` RIGHT JOIN `A` ON A.X = B.X WHERE B.Z > 'A'
some of table A is filtered out. Now I have:
Z | X | Y
---------
B | 1 | hi
C | 2 | bye
However, if my query does the filtering before the join, like so:
SELECT * FROM
(SELECT * FROM `B` WHERE B.Z > 'A') AS B
RIGHT JOIN `A` ON A.X = B.X
my table still contains all the rows from A.
Z | X | Y
---------
B | 1 | hi
C | 2 | bye
- | 3 | what
It's just a matter of order. In your original query, you select all the rows then filter out some. In your working query, you first filter, then you get all the category rows you need.

How can I select the users which are belonging to group A?

How can I select the users which are belonging to group A?
My tables are below.
my user table.
ID | name |sex
1 | bob |1
2 | kayo |2
3 | ken |1
my fos_group table
ID | name
1 | student
2 | teacher
my fos_user_user_group
user_id | group_id
1 | 1
2 | 2
3 | 1
Bob and Ken are belonging to group_1(student)
Kayo is belonging to group_2(teacher)
ex) I can select 'Bob' from user table like this
$query = $em->createQuery(
"SELECT p.name,p.sex
FROM UserBundle:User p WHERE
p.id = '1' );
But I would like to select the users which belongs to student group(Bob and Ken)
How should I change the sentence in createQuery?
I just guess I need to join the tables though...
additional....
I have tried like this accroding to Fabio's answer
$query = $em->createQuery(
"SELECT p,p.id,p.username,p.userKey
FROM UserBundle:User p
INNER JOIN fos_user_user_group b
ON a.ID = b.user_id
INNER JOIN fos_group c
ON b.group_id = c.ID
WHERE c.group_id = '1'");
$this->data["teachers"] = $query->getResult();
but it says
[Semantical Error] line 0, col 94 near 'fos_user_user_group': Error: Class 'fos_user_user_group' is not defined.
I guess it means I dont have entity for 'fos_user_user_group'.
I have only entity class for Group and User,other tables were created automatically.
In meanwhile,I used like this in other place in $formmapper.
->add('teacher',
null,
array(
'query_builder' =>
function (\Doctrine\ORM\EntityRepository $rep) {
return $rep->
createQueryBuilder('s')
->join('s.groups', 'g')
->where('g.name = :group')->setParameter('group','TeacherGroup');
})
)
it works well,
how can I change this sentence for createQuery()?
I think you can use a INNER JOIN query
SELECT p.name,p.sex
FROM User p
INNER JOIN fos_user_user_group b
ON a.ID = b.user_id
INNER JOIN fos_group c
ON b.group_id = c.ID
WHERE c.group_id = '1'