MySQL - IF within SELECT - mysql

I have the following MySQL query:
SELECT id, name, parent_id AS pID,
(SELECT parent_id
FROM category
WHERE id = pID)
AS grandparent_id,
(SELECT parent_id
FROM category
WHERE id = grandparent_id)
AS greatgrandparent_id,
(SELECT name
FROM category
WHERE id = pID)
AS parent,
(SELECT name
FROM category
WHERE id = grandparent_id)
AS grandparent,
(SELECT name
FROM category
WHERE id = greatgrandparent_id)
AS greatgrandparent
FROM category
WHERE active = '1'
HAVING grandparent IS NOT NULL
ORDER BY grandparent_id, parent_id, sort, id ASC
When the greatgrandparent IS NULL I want it to display the grandparent value instead. Is this possible?
I've tried CASE WHEN but must be getting the syntax wrong somewhere as it keeps throwing an error.

Simple solution: IFNULL(greatgrandparent, grandparent).

You cannot have a having clause without a group by clause.

May be this code helps you.
SELECT id, name, parent_id AS pID,
case when grandparent IS NOT NULL then
(SELECT parent_id FROM category WHERE id = grandparent_id)
else
(SELECT parent_id FROM category WHERE id = pID)
end as _parent_id,
case when grandparent IS NOT NULL then
(SELECT name FROM category WHERE id = grandparent_id)
else
(SELECT name FROM category WHERE id = pID)
end AS _parent_name
FROM category WHERE active = '1'
ORDER BY grandparent_id, parent_id, sort, id ASC

I would rewrite this using left joins with aliases and coalesce.
Given this
/*
CREATE TABLE CATEGORY(ID INT, NAME VARCHAR(20), PARENT_ID INT);
TRUNCATE TABLE CATEGORY;
INSERT INTO CATEGORY VALUES
(1,'AAAA',2),(2,'AAA',3),(3,'AA',4),(4,'A',5),
(10,'BBBB',11),(11,'BBB',12)
;
*/
This code
SELECT C.ID,C.NAME,C.PARENT_ID ,
CP.NAME AS PARENTNAME,CP.PARENT_ID AS PARENT_PARENT_ID,
CG.NAME AS GRANDPARENTNAME,
CG.PARENT_ID AS GRANDPARENT_PARENT_ID,
CGG.NAME AS GREATGRANDPARENTNAME ,
CGG.PARENT_ID AS GREATGRANDPARENT_PARENT_ID
FROM CATEGORY C
LEFT JOIN CATEGORY CP ON CP.ID = C.PARENT_ID
LEFT JOIN CATEGORY CG ON CG.ID = CP.PARENT_ID
LEFT JOIN CATEGORY CGG ON CGG.ID = CG.PARENT_ID
ORDER BY C.ID;
Results in
+------+------+-----------+------------+------------------+-----------------+-----------------------+----------------------+----------------------------+
| ID | NAME | PARENT_ID | PARENTNAME | PARENT_PARENT_ID | GRANDPARENTNAME | GRANDPARENT_PARENT_ID | GREATGRANDPARENTNAME | GREATGRANDPARENT_PARENT_ID |
+------+------+-----------+------------+------------------+-----------------+-----------------------+----------------------+----------------------------+
| 1 | AAAA | 2 | AAA | 3 | AA | 4 | A | 5 |
| 2 | AAA | 3 | AA | 4 | A | 5 | NULL | NULL |
| 3 | AA | 4 | A | 5 | NULL | NULL | NULL | NULL |
| 4 | A | 5 | NULL | NULL | NULL | NULL | NULL | NULL |
| 10 | BBBB | 11 | BBB | 12 | NULL | NULL | NULL | NULL |
| 11 | BBB | 12 | NULL | NULL | NULL | NULL | NULL | NULL |
+------+------+-----------+------------+------------------+-----------------+-----------------------+----------------------+----------------------------+
and this code
SELECT C.ID,C.NAME,C.PARENT_ID ,
CP.NAME AS PARENTNAME,CP.PARENT_ID AS PARENT_PARENT_ID,
COALESCE(CG.NAME,CP.NAME) AS GRANDPARENTNAME,
COALESCE(CG.PARENT_ID,CP.PARENT_ID) AS GRANDPARENT_PARENT_ID,
COALESCE(CGG.NAME,CG.NAME) AS GREATGRANDPARENTNAME ,
COALESCE(CGG.PARENT_ID,CG.PARENT_ID) AS GREATGRANDPARENT_PARENT_ID
FROM CATEGORY C
LEFT JOIN CATEGORY CP ON CP.ID = C.PARENT_ID
LEFT JOIN CATEGORY CG ON CG.ID = CP.PARENT_ID
LEFT JOIN CATEGORY CGG ON CGG.ID = CG.PARENT_ID
ORDER BY C.ID
results in
+------+------+-----------+------------+------------------+-----------------+-----------------------+----------------------+----------------------------+
| ID | NAME | PARENT_ID | PARENTNAME | PARENT_PARENT_ID | GRANDPARENTNAME | GRANDPARENT_PARENT_ID | GREATGRANDPARENTNAME | GREATGRANDPARENT_PARENT_ID |
+------+------+-----------+------------+------------------+-----------------+-----------------------+----------------------+----------------------------+
| 1 | AAAA | 2 | AAA | 3 | AA | 4 | A | 5 |
| 2 | AAA | 3 | AA | 4 | A | 5 | A | 5 |
| 3 | AA | 4 | A | 5 | A | 5 | NULL | NULL |
| 4 | A | 5 | NULL | NULL | NULL | NULL | NULL | NULL |
| 10 | BBBB | 11 | BBB | 12 | BBB | 12 | NULL | NULL |
| 11 | BBB | 12 | NULL | NULL | NULL | NULL | NULL | NULL |
+------+------+-----------+------------+------------------+-----------------+-----------------------+----------------------+----------------------------+

Related

Joining INTO many-to-many table (From Primary Key Table)

This is how far i have gotten, though i don't think it can be done with 1 SQL-statement, i just want to confirm whether or not it is possible to do this with ONLY 1 statement:
SELECT * FROM users
INNER JOIN users_mentors ON users_mentors.id=users.mentoruser_id
INNER JOIN mentor_types ON (mentor_types.id=users_mentors.mentor_type OR users_mentors.mentor_type IS NULL)
INNER JOIN mentor_geographies ON mentor_geographies.mentor_id=users_mentors.id
INNER JOIN communes ON communes.id=mentor_geographies.commune_id
LIMIT 0,10
users table with foreignkey to users_mentors:
+------+---------+---------------+
| id | user_id | mentoruser_id |
+------+---------+---------------+
| 1886 | NULL | 4 |
| 1885 | NULL | NULL |
| 1884 | NULL | NULL |
| 1883 | NULL | NULL |
| 1882 | NULL | NULL |
+------+---------+---------------+
users_mentors table (in a many-to-many relationship with communes):
+----+-------------+
| id | mentor_type |
+----+-------------+
| 4 | NULL |
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
+----+-------------+
communes table (in a many-to-many relationship with users_mentors):
+----+--------------+-------+----------+
| id | name | short | contract |
+----+--------------+-------+----------+
| 1 | København | NULL | 0 |
| 2 | Aarhus | NULL | 0 |
| 3 | Aalborg | NULL | 0 |
| 4 | Odense | NULL | 0 |
| 5 | Esbjerg | NULL | 0 |
+----+--------------+-------+----------+
mentor_geographies table (the m2m table that has FK to communes & users_mentors):
+----+-----------+------------+
| id | mentor_id | commune_id |
+----+-----------+------------+
| 1 | 4 | 1 |
| 2 | 4 | 2 |
+----+-----------+------------+
Is it possible to get all rows from users_mentors and a list of all their commune.type's, IF THEY EXIST (if mentor_geographies is empty, i want empty list of commune.type). In all cases i want the user.
If you want all users, use left join:
SELECT *
FROM users LEFT JOIN
users_mentors
ON users_mentors.id = users.mentoruser_id LEFT JOIN
mentor_types
ON mentor_types.id=users_mentors.mentor_type OR
users_mentors.mentor_type IS NULL LEFT JOIN
mentor_geographies
ON mentor_geographies.mentor_id = users_mentors.id LEFT JOIN
communes
ON communes.id = mentor_geographies.commune_id
LIMIT 0, 10;
I would also recommend that you use table aliases. They make the queries easier to write and to read:
SELECT *
FROM users u LEFT JOIN
users_mentors um
ON um.id = u.mentoruser_id LEFT JOIN
mentor_types mt
ON mt.id = um.mentor_type OR
um.mentor_type IS NULL LEFT JOIN
mentor_geographies mg
ON mg.mentor_id = um.id LEFT JOIN
communes c
ON c.id = mg.commune_id
LIMIT 0, 10

Compare two database table

I have two database tables, tmp1 and tmp2
tmp1:
+----+------+---------+---------+
| id | name | add1 | add2 |
+----+------+---------+---------+
| 1 | NULL | NULL | NULL |
| 2 | NULL | NULL | NULL |
| 3 | mum1 | rajpur1 | rajpur2 |
| 4 | mum3 | rajpur3 | rajpur4 |
| 5 | mum4 | rajpur4 | rajpur5 |
+----+------+---------+---------+
tmp2:
+----+------+---------+---------+
| id | name | add1 | add2 |
+----+------+---------+---------+
| 1 | NULL | NULL | NULL |
| 2 | mum | rajpur | rajpur1 |
| 3 | NULL | NULL | NULL |
| 4 | mum2 | rajpur2 | rajpur3 |
| 5 | mum4 | rajpur4 | rajpur5 |
+----+------+---------+---------+
select a.id
from tmp1 a
inner join tmp2 b
where a.id = b.id
and (a.name is NULL or a.add1 is NULL or a.add2 is NULL)
or (a.name!=b.name or a.add1!=b.add1 or a.add2!=b.add2);
Here I have tried the above code to show the following result:
+----+
| id |
+----+
| 1 |
| 2 |
| 3 |
| 4 |
+----+
But it doesn't show me the desired result.
Here i want to print the first table's id where one or many other row's values are either null or one or many other row's values are unmatched with tmp2's those rows values.
You were missing the ON. And as The Impaler astutely pointed out, the columns in the temp2 also need to be checked for NULL.
select a.id
from tmp1 a
inner join tmp2 b on a.id=b.id
where coalesce(a.name,a.add1,a.add2,b.name,b.add1,b.add2) is null or
coalesce(a.name,a.add1,a.add2)<>coalesce(b.name,b.add1,b.add2));
You are missing the ON clause that specifies how to join the table(s). Also, your search condition is incomplete.
select a.id
from tmp1 a
join tmp1 b on a.id = b.id
where a.name is null or a.add1 is null or a.add2 is null
or b.name is null or b.add1 is null or b.add2 is null
or (a.name <> b.name or a.add1 <> b.add1 or a.add2 <> b.add2)

Mysql, Dynamic query for rows to column

I've three tables with name employee, employee_products, product_type
reference : http://sqlfiddle.com/#!9/00436/4
I'm trying to get data as
Let this table is Table_1
using this query:
select emp.name,
(select count(*) from employee_products where product_type_id = 1 and employee_id = emp.id) as Service,
(select count(*) from employee_products where product_type_id = 2 and employee_id = emp.id) as Product,
(select count(*) from employee_products where product_type_id = 3 and employee_id = emp.id) as Other
from employee as emp;
but, i think it is not efficient and from every new product_type_id i've to alter this query, can I do this dynamically.
+------------+---------+---------+-------+--+
| Name | Service | Product | Other | |
+------------+---------+---------+-------+--+
| Bezos | 1 | 0 | 0 | |
+------------+---------+---------+-------+--+
| Steve | 0 | 3 | 0 | |
+------------+---------+---------+-------+--+
| Bill gates | 1 | 0 | 0 | |
+------------+---------+---------+-------+--+
| Tim Cook | 0 | 0 | 1 | |
+------------+---------+---------+-------+--+
and
Let this table is Table_2
In this I can't figure out how this is even possible in mysql as there is no pivot feature in mysql.
+------------+---------+---------+---------+---------+-----------+-------+
| Name | Amazon | iPhone | iPad | iPod | Microsoft | IDK |
+------------+---------+---------+---------+---------+-----------+-------+
| Bezos | Service | NULL | NULL | NULL | NULL | NULL |
+------------+---------+---------+---------+---------+-----------+-------+
| Steve | NULL | Product | Product | Product | NULL | NULL |
+------------+---------+---------+---------+---------+-----------+-------+
| Bill gates | NULL | NULL | NULL | NULL | PRODUCT | NULL |
+------------+---------+---------+---------+---------+-----------+-------+
| Tim Cook | NULL | NULL | NULL | NULL | NULL | OTHER |
+------------+---------+---------+---------+---------+-----------+-------+
Please help.
Note : There can be more than 100 items in product_type, employee_products table.
Try this for Table_1
Select name, Max(Service) as Service, Max(Product) as Product, Max(Other) as Other
From (
select e.name,
count(case when ep.product_type_id = 1 then 1 else null end) as Service,
count(case when ep.product_type_id = 2 then 1 else null end) as Product,
count(case when ep.product_type_id = 3 then 1 else null end) as Other,
from employee e
inner join employee_products ep on (e.id = ep.employee_id)
)
Group by name;
Note: same way you can try for Table_2
There are several bits of code out there for dynamically building and running the SELECT with the columns dynamically deduced. Here is mine.

WHERE AND clause for the same column but different rows

Prior Information Notice
I have 3 tables:
types
+----+-------------+-----------------------+------------+------------+
| id | category_id | name | created_at | updated_at |
+----+-------------+-----------------------+------------+------------+
| 1 | 1 | T-Shirts | NULL | NULL |
+----+-------------+-----------------------+------------+------------+
prototypes
+----+-----------------------------------------+------------+------------+
| id | name | created_at | updated_at |
+----+-----------------------------------------+------------+------------+
| 1 | Gildan Softstyle Adult Ringspun T-shirt | NULL | NULL |
+----+-----------------------------------------+------------+------------+
filters
+----+-------------+---------------------+-------+------------+------------+
| id | name | value | extra | created_at | updated_at |
+----+-------------+---------------------+-------+------------+------------+
| 1 | gender | male | NULL | NULL | NULL |
| 2 | gender | female | NULL | NULL | NULL |
| 3 | age_group | adult | NULL | NULL | NULL |
| 4 | age_group | child | NULL | NULL | NULL |
| 5 | age_group | baby | NULL | NULL | NULL |
+----+-------------+---------------------+-------+------------+------------+
They are related one another through n-m relationship, so there are respective junction tables types_prototypes, types_filters, prototypes_filters as well. For more details please check also out my dump file.
Problem itself
I'm trying to set up filtering system (with Laravel), so I need to query all Prototypes that are related to all given Filters (logical AND). Until now I have managed to get the, as long as use chose only one Filter:
select * from `prototypes`
inner join `types_prototypes` on `prototypes`.`id` = `types_prototypes`.`prototype_id`
inner join `prototypes_filters` on `prototypes`.`id` = `prototypes_filters`.`prototype_id`
inner join `filters` on `prototypes_filters`.`filter_id` = `filters`.`id`
where `types_prototypes`.`type_id` = ? and `filter_id` = ? group by `prototypes`.`id`
The problem itself consists in the fact that this query is inapplicable, as soon as we have several filters that should be valid simultaneously:
...
where `types_prototypes`.`type_id` = ? and `filter_id` = ? and `filter_id` = ? group by `prototypes`.`id`
I know, where ... and doesn't work, because I have due to join only one column filter_id that can contain only one single value at the same time (what actually groupBy() takes care of). So in this sense I have a new one row for the relation of the same Prototype with another Filter, e.g.:
+----+-----------------------------------------+------------+------------+---------+--------------+--------------+-----------+----+-----------+-------+-------+------------+------------+
| id | name | created_at | updated_at | type_id | prototype_id | prototype_id | filter_id | id | name | value | extra | created_at | updated_at |
+----+-----------------------------------------+------------+------------+---------+--------------+--------------+-----------+----+-----------+-------+-------+------------+------------+
| 1 | Gildan Softstyle Adult Ringspun T-shirt | NULL | NULL | 1 | 1 | 1 | 1 | 1 | gender | male | NULL | NULL | NULL |
| 1 | Gildan Softstyle Adult Ringspun T-shirt | NULL | NULL | 1 | 1 | 1 | 3 | 3 | age_group | adult | NULL | NULL | NULL |
+----+-----------------------------------------+------------+------------+---------+--------------+--------------+-----------+----+-----------+-------+-------+------------+------------+
I have already tried several different methods, including where 'filter_id' in(?,?), where FIND_IN_SET('filter_id', '?,?') and even restructured my database in accord with EAV-pattern (when the filters is divided into filter_names and filter_values). But every time I obtain only entries that fulfill one requirement of the whole set (equals logical OR), for instance (here we have prototypes for adults and men, but not only for adult men):
+----+-----------------------------------------+------------+------------+---------+--------------+--------------+-----------+----+-----------+-------+-------+------------+------------+
| id | name | created_at | updated_at | type_id | prototype_id | prototype_id | filter_id | id | name | value | extra | created_at | updated_at |
+----+-----------------------------------------+------------+------------+---------+--------------+--------------+-----------+----+-----------+-------+-------+------------+------------+
| 1 | Gildan Softstyle Adult Ringspun T-shirt | NULL | NULL | 1 | 1 | 1 | 1 | 1 | gender | male | NULL | NULL | NULL |
| 2 | American Apparel Womans T-Shirt | NULL | NULL | 1 | 2 | 2 | 3 | 3 | age_group | adult | NULL | NULL | NULL |
| 3 | Gildan Adult Cotton T-shirt | NULL | NULL | 1 | 3 | 3 | 1 | 1 | gender | male | NULL | NULL | NULL |
| 4 | American Apparel Mens T-Shirt | NULL | NULL | 1 | 4 | 4 | 1 | 1 | gender | male | NULL | NULL | NULL |
| 5 | American Apparel Kids T-Shirt | NULL | NULL | 1 | 5 | 5 | 1 | 1 | gender | male | NULL | NULL | NULL |
+----+-----------------------------------------+------------+------------+---------+--------------+--------------+-----------+----+-----------+-------+-------+------------+------------+
I'm almost desperate, does anybody have a clue?
Thanks you in advice for your help and sorry for so much text, I just wanted to describe all circumstances.
You have to join with the filters table repeatedly for each criterion.
select * from prototypes AS p
inner join types_prototypes AS tp1 on p.id = tp1.prototype_id
inner join prototypes_filters AS pf1 on p.id = pf1.prototype_id
inner join filters AS f1 on pf1.filter_id = f1.id
inner join types_prototypes AS tp2 on p.id = tp2.prototype_id
inner join prototypes_filters AS pf2 on p.id = pf2.prototype_id
inner join filters AS f2 on pf2.filter_id = f2.id
where tp1.type_id = ? and f1.filter_id = ?
AND tp2.type_id = ? and f2.filter_id = ?
group by prototypes.id

Multiple inner join on a table where rows represent data attributes

I have the following user table (don't ask me why :) )
| id | cid | attr | text | rdate |
---------------------------------------
| 1 | 1 | name | joe | NULL |
| 2 | 1 | date | NULL | 10.05.2014 |
| 3 | 1 | stat | 2 | NULL |
----------------------------------------
| 4 | 2 | name | joe | NULL |
| 5 | 2 | date | NULL | 05.05.2014 |
| 6 | 2 | stat | 1 | NULL |
----------------------------------------
| 7 | 3 | name | joe | NULL |
| 8 | 3 | date | NULL | 03.05.2014 |
| 9 | 3 | stat | 2 | NULL |
As you can see every user's attribute (name, date, stat) is a row in the table.
Attributes with the same cid belong to the same user.
I would like to delete all the entries which refer to a user whose attribute date is before 08.05.2014 AND whose attribute stat is not 2. So after running this query the table will be:
| id | cid | attr | text | rdate |
---------------------------------------
| 1 | 1 | name | joe | NULL |
| 2 | 1 | date | NULL | 10.05.2014 |
| 3 | 1 | stat | 2 | NULL |
----------------------------------------
| 7 | 3 | name | joe | NULL |
| 8 | 3 | date | joe | 03.05.2014 |
| 9 | 3 | stat | 2 | NULL |
Is it possible? Is this a inner join on the same table?
Group by the cid and use the having clause to run group functions to check out your requirements in every single group
delete from your_table
where cid in
(
select * from
(
select cid
from your_table
group by cid
having sum(attr = 'date' and `date` < '2014-05-08') > 0
and sum(attr = 'stat' and `text` = 2) = 0
) tmp_tbl
)
In MySQL you can't delete from the same table you are selecting from. But you can trick MySQL with another subquery like in the example above.
You can do this with delete/join:
delete t
from table t join
(select cid
from table t
group by cid
having max(case when attr = 'date' and date < '2014-05-08') > 0 and
max(case when attr = 'stat' and text <> '2') > 0
) s
on t.cid = s.cid;
I would do a fairly simple JOIN in the delete statement:-
DELETE a
FROM some_table a
INNER JOIN some_table b
ON a.cid = b.cid
INNER JOIN some_table c
ON a.cid = c.cid
WHERE b.attr = 'date' AND b.date < '2014-05-08'
AND c.attr = 'stat' AND c.text != '2'
Join will do :
Delete from mytable where cid in (select cid from
(select t1.cid FROM mytable t1 INNER JOIN mytable t2 ON t1.cid = t2.cid
WHERE t1.attr = 'date' AND t1.rdate < '2014-05-08'
AND t2.attr = 'stat' AND t2.text != 2) as sq)