MySQL, Select all that don't have a sum in other table - mysql

I have two tables
_id___|_description___
3433 | Apple-Pie
3699 | Strawberry-Pie
6952 | Banana-Pie
...
and
_cakeId__|_ ingredientCode _
3433 | SUGAR
3433 | APPLE
3433 | E_200_SRT_05
3433 | CREAM
3699 | SUGAR
3699 | STRABERRY
6952 | E_200_SRT_08
6952 | E_200_KJ_84
...
I now want to select all cakes (by id) that have 0 entries in the ingredients table, while ingredients starting with E_ do not count.
In my example above only the cake with id 6952 would be selected.
I tried several combinations of joins and subselect counts but I never got the right result
select
c.id
t1.cntIngr
from
cakes c
join (
select
cakeId,
count(ingredientCode) as cntIngr
from
cake_ingredients
group by cakeId
having ingredientCode not like 'E_%'
) as t1 on t1.cakeId = c.id

http://sqlfiddle.com/#!9/d3d375/2
SELECT c.*
FROM cakes c
LEFT JOIN cake_ingredients i
ON i.cakeId = c.id
AND LEFT(i.ingredientCode,2)<>'E_'
WHERE i.cakeId IS NULL

Try this query
SELECT description
FROM tab1
INNER JOIN (SELECT cakeId, COUNT(ingredientCode)
FROM tab2
WHERE ingredientCode NOT LIKE "E_%"
GROUP BY cakeId
HAVING COUNT(ingredientCode) = 0) AS tab2
ON tab1.id = tab2.cakeId

That's a NOT EXISTS query: You want all cakes for which no non-E ingredient exist:
select id
from cakes c
where not exists
(
select *
from cake_ingredients ci
where ci.ingredientcode not like 'E\_%' escape '\'
and ci.cakeId = c.id
);
Here is the same with NOT IN:
select id
from cakes
where id not in
(
select cakeId
from cake_ingredients
where ingredientcode not like 'E\_%' escape '\'
);
(I usually prefer NOT IN over NOT EXISTS for its simplicity.)

SELECT
id
,description
FROM cakes WHERE id NOT IN(
SELECT
cakeId
FROM cake_ingredients
WHERE ingredientCode NOT LIKE 'E_%'
)
http://sqlfiddle.com/#!9/8f850/5

Related

MySQL Join two querys with same number of rows as results

I'm trying to Join two query results as one with no luck so far.
I tried Union but that just adds the second query result after the first query result.
Tried scipping the second query alltogether and use something like cross join but that always returned the same row data from the table in the second query.
The first query is this:
SELECT
`namelist`.`id`,
`name`.`id` AS `name_id`,
`name_item`.`content`,
`order`.`create_time`
FROM
`namelist`
LEFT JOIN `name` ON `name`.`namelist_id` = `namelist`.`id`
LEFT JOIN `name_item` ON `name_item`.`name_id` = `name`.`id`
LEFT JOIN `order` ON `namelist`.`order_id` = `order`.`id`
LEFT JOIN `items` ON `items`.`id` = `name_item`.`items_id`
WHERE
`namelist`.`order_id`=1380 AND `items`.`key`='Name'
GROUP BY `name_item`.`content`
ORDER BY `name`.`id`
The second query:
SELECT `validity`, `code`, `image` FROM `code` WHERE `code`.`order_id` = 1380 ORDER BY `id`
And as a result I would like to get something like this:
id | name_id | content | create_time | validity | code | image
--------------------------------------------------------------
1 | 1 | nameone | 2022-10-01 | somedate | 123 | 123.png
1 | 2 | nametwo | 2022-10-01 | somedate | 567 | 567.png
The querys return the same number of rows but they have no common identifier, and the reseult of the second query can not be duplicated because they have unique code colum.
SELECT id,
name_id,
content,
create_time,
validity,
code,
image
FROM
(-- first query
SELECT `namelist`.`id`,
`name`.`id` AS `name_id`,
`name_item`.`content`,
`order`.`create_time`,
ROW_NUMBER() OVER (ORDER BY `name`.`id`) AS rn
FROM `namelist`
LEFT JOIN `name` ON `name`.`namelist_id` = `namelist`.`id`
LEFT JOIN `name_item` ON `name_item`.`name_id` = `name`.`id`
LEFT JOIN `order` ON `namelist`.`order_id` = `order`.`id`
LEFT JOIN `items` ON `items`.`id` = `name_item`.`items_id`
WHERE `namelist`.`order_id`=1380
AND `items`.`key`='Name'
GROUP BY `name_item`.`content`
-- ORDER BY `name`.`id`
) AS subquery1
JOIN
(-- second query
SELECT `validity`,
`code`,
`image` ,
ROW_NUMBER() OVER (ORDER BY `id`) rn
FROM `code`
WHERE `code`.`order_id` = 1380
-- ORDER BY `id`
) AS subquery2 USING (rn)
ORDER BY rn

How to select an entry without specific entries?

I have two SQL-tables like this:
T1
Animal Name
Cat Paul
Cat Miau
Cat Paul
Cat Peter
T2
Legs Name
4 Paul
4 Miau
3 Paul
4 Peter
What I want to have is a table like this:
Animal Legs Name
Cat 4 Miau
Cat 4 Peter
I want to have all animals with a specific number of legs but not when we have two cats with the same name.
I tried doing something like these:
select a.animal, b.legs, a.name
from animallistA as a join animallistB as b
on a.name = b.name
where b.legs = 4 and not b.legs = 3
group by a.animal, b.legs, a.name
If I say where b.legs = 4 then I'll also receive 'Paul' but If I say where b.legs = 4 and not b.legs = 3 I receive nothing at all.
Is there a way not to receive the cats who have the same name and 4 legs but not the one that have the same name but either 4 or less(or more) legs.
NOT EXISTS may do the job.
SELECT t1.animal, t2.legs, t2.name
FROM t1
JOIN t2 ON t1.name = t2.name
WHERE t2.legs = 4 AND
NOT EXISTS
(
SELECT 1 FROM t2 WHERE t2.name = t1.name AND t2.legs != 4
)
You can first take all the unique names of 4 legs animals ( from subquery ) and then join with the main table to get required result.
select a.animal, b.legs, a.name from animallistA as a join
(select name,max(legs) legs from animallistB group by name having count(distinct legs)=1) as b
on a.name = b.name and b.legs=4
From subquery we can get result
Legs Name
4 Miau
4 Peter
Because distinct values in having condition fail for Paul. Joining this with main table gives the requested result
AS far as I understood your question I suggest following query:
SELECT A.ANIMAL, A.NAME, B.LEGS
FROM ANIMALLISTA A
INNER JOIN ANIMALLISTB B ON A.NAME = B.NAME
LEFT JOIN (SELECT NAME, COUNT(*) AS RC FROM ANIMALLISTB GROUP BY NAME) C ON A.NAME = C.NAME
WHERE B.LEGS=4
AND C.RC=1
Output:
ANIMAL NAME LEGS
Cat Miau 4
Cat Peter 4
You might wanna try something like this, worked for me on My SQL 8.0.2
|| cat_1 = T1 and cat_2 = T2
WITH CTE AS
(
SELECT distinct *
FROM cat_1
INNER JOIN cat_2 ON cat_1.names=cat_2.name
),
SCTE AS
(
SELECT CTE.animal,
CTE.legs,
CTE.names,
COUNT(CTE.names) over(PARTITION BY CTE.names) as Count
from CTE
)
SELECT
SCTE.animal,
SCTE.legs,
SCTE.names
FROM SCTE
WHERE legs = 4
AND Count = 1;
Output:
animal legs names
Cat 4 Miau
Cat 4 Peter

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 |