I'm trying to use MySQL 8 to create a SELECT query that "creates" 5 new fields based on the results from 5 other queries, however, I'm struggling to work out how I get this to work.
Consider this scenario:
product_attributes
productid
attribute_name
attribute_value
1
Diameter
1mm
1
Width
2mm
1
Weight
1kg
1
Length
10mm
2
Diameter
12mm
2
Width
22mm
2
Weight
2kg
2
Length
20mm
Now consider that my "user input" wants the first Attribute to be "Width" and the second attribute to be "Weight", i.e. att1 = Width, att2=Weight, att3..... etc
I wish to construct a SELECT query that returns the following:
productid
att1
att2
1
2mm
1kg
2
22mm
2kg
I can get this to work with attribute 1, but I'm struggling to figure out how to make this work for attribute 2.
This is the query I have:
select `att1`,ATT_TABLE.productid FROM
(select `attribute_value` as att1, `productid` from `product_attributes` where attribute_name='Width') ATT_TABLE
JOIN products on ATT_TABLE.productid = products.productid
WHERE products.catid='12345'
Ignore the JOIN/WHERE - that is just some extra logic I need to grab a particular categories products, but I have included it in case it would break any answers received.
How do I modify the query to include another sub query SELECT statement to get my second attribute, or is there another method to achieve the same?
Kind of like this:
select `att1`,`att2`,ATT_TABLE.productid FROM
(select `attribute_value` as att1, `productid` from `product_attributes` where attribute_name='Width') ATT_TABLE
(select `attribute_value` as att2, `productid` from `product_attributes` where attribute_name='Weight') ATT_TABLE2
EDIT: An alternative suggestion by slashroot was to use CASE / GROUP BY.
select productid,
CASE
WHEN attribute_name ='Width' THEN attribute_value
END as att1,
CASE
WHEN attribute_name ='Weight' THEN attribute_value
END as att2
FROM product_attributes
WHERE productid='1'
)
However, this then returns multiple rows for the same productID and I'm struggling to figure out the GROUP BY statement.
This is the return from that statement:
productid
att1
att2
1
NULL
NULL
1
NULL
NULL
1
2mm
NULL
1
NULL
2kg
How do I get a group by to condense this down?
That was a good start you did in your EDIT above. But you were missing a couple steps.
Have a look at this query, it should return the desired results
SELECT productid,
MAX(CASE WHEN attribute_name = 'Width' THEN attribute_value ELSE NULL END) as att1,
MAX(CASE WHEN attribute_name = 'Weight' THEN attribute_value ELSE NULL END) as att2
FROM product_attributes
GROUP BY productid
Check the results from this DB FIDDLE
Related
id
quantity
A
100
A
100
B
100
A
200
C
400
id
product_name
A
almond
B
broccoli
D
durian
F
fries
If I understand your question correctly, then try using CASE in the select statement.
SELECT CASE
WHEN pn.product_name IS NULL THEN 'Other Product'
ELSE pn.product_name END AS ProductName,
SUM(p.quantity) AS Quantity
FROM Products p
LEFT JOIN ProductName pn ON p.id = pn.id
GROUP BY pn.product_name
What it's doing is, if it finds product_name as null then it will select the name as 'Other Product', otherwise it will show the corresponding Product Name.
I need to get the id_user by values given in other columns. My table look like this:
id(AI,PK) id_user attr_name attr_value
----------------------------------------------------
1 1 hair brown
2 1 eyes green
3 2 hair blond
4 1 age 40
5 1 sex male
6 2 eyes green
7 2 age 40
8 2 sex male
When I try a query like this:
select distinct id_user where (attr_name='hair' and attr_value='blond') or (attr_name='eyes' and attr_value='green')
I will obviously get id_user=1 and 2, because both of them have green eyes.
If I change "or" to "and" it seems that the query does not work at all. But I need "and" because 2 (or even more, I shortened my example) conditions must be met, to get the specified id_user:
select distinct id_user where (attr_name='hair' and attr_value='blond') and (attr_name='eyes' and attr_value='green')
How to "pair" those 2 brackets, so I will get only a user where both conditions met: green eyes and blond hair?
Use post aggregate filtering with HAVING.
WHERE filters rows, HAVING with aggregate functions filters groups.
SELECT id_user FROM t
GROUP BY id_user
HAVING SUM(attr_name='hair')>0 AND SUM( attr_value='blond') >0
AND SUM(attr_name='eyes')>0 AND SUM( attr_value='green') >0
Another approach is.
SELECT `u1`.`id_user`,
IF(`u1`.`attr_name` RLIKE 'hair' AND `u1`.`attr_value` = 'blond',1,0) AS `blond`,
IF(`u2`.`attr_name` RLIKE 'eyes' AND `u2`.`attr_value` = 'green',1,0) AS `green`
FROM `users` `u1`
INNER JOIN `users` `u2`
ON `u1`.`id_user`=`u2`.`id_user`
HAVING `green`+`blond` = 2;
OR
SELECT `u1`.`id_user`
FROM `users` `u1`
INNER JOIN `users` `u2`
ON `u1`.`id_user`=`u2`.`id_user`
WHERE IF(`u1`.`attr_name` RLIKE 'hair' AND `u1`.`attr_value` = 'blond',1,0) + IF(`u2`.`attr_name` RLIKE 'eyes' AND `u2`.`attr_value` = 'green',1,0) = 2;
The result is
id_user blond green
2 1 1
To re-create the example use:
create table `users` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`id_user` INT,
`attr_name` VARCHAR(255) NOT NULL,
`attr_value` VARCHAR(255) NOT NULL
) ENGINE=INNODB;
INSERT INTO `users` (`id`,`id_user`,`attr_name`,`attr_value`) VALUES (1,1,'hair','blond'),(2,1,'eyes','blue'),(3,2,'hair','blond'),(4,2,'eyes','green'),(5,2,'sex','male'),(6,2,'age','42'),(7,1,'sex','female'),(8,1,'age','39');
Try it on SQL Fiddle
I have a table like this:
userid | trackid | path
123 70000 ad
123 NULL abc.com
123 NULL Apply
345 70001 Apply
345 70001 Apply
345 NULL Direct
345 NULL abc.com
345 NULL cdf.com
And I want a query like this. When path='abc.com', num_website +1; when path='Apply', num_apply +1
userid | num_website | num_Apply | num_website/num_Apply
123 1 1 1
345 1 2 0.5
My syntax looks like this:
select * from
(select userid,count(path) as is_CWS
from TABLE
where path='abc.com'
group by userid
having count(path)>1) a1
JOIN
(select userid,count(userid) as Apply_num from
where trackid is not NULL
group by userid) a2
on a1.userid=a2.userid
My question is
1. how to have the field num_website/num_apply in term of my syntax above?
2. is there any other easier way to get the result I want?
Any spots shared will appreciate.
The simplest way to do it would be to change the select line:
SELECT a1.userid, a1.is_CWS, a2.Apply_num, a1.is_CWS/a2.Apply_num FROM
(select userid,count(path) as is_CWS
from TABLE
where path='abc.com'
group by userid
having count(path)>1) a1
JOIN
(select userid,count(userid) as Apply_num
from TABLE
where trackid is not NULL
group by userid) a2
on a1.userid=a2.userid
and then continue with the rest of your query as you have it. The star means "select everything." If you wanted to select only a few things, you would just list those things in place of the star, and if you wanted to select some other values based on those things, you would put those in the stars as well. In this case a1.is_CWS/a2.Apply_num is an expression, and MySql knows how to evaluate it based on the values of a1.is_CWS and a2.Apply_num.
In the same vein, you can do a lot of what those subqueries are doing in a single expression instead of a subquery. objectNotFound has the right idea. Instead of doing a subquery to retrieve the number of rows with a certain attribute, you can select SUM(path="abc.com") as Apply_num and you don't have to join anymore. Making that change gives us:
SELECT a1.userid,
SUM(path="abc.com") as is_CWS,
a2.Apply_num,
is_CWS/a2.Apply_num FROM
TABLE
JOIN
(select userid,count(userid) as Apply_num
FROM TABLE
where trackid is not NULL
group by userid) a2
on a1.userid=a2.userid
GROUP BY userid
Notice I moved the GROUP BY to the end of the query. Also notice instead of referencing a1.is_CWS I now reference just is_CWS (it's no longer inside the a1 subtable so we can just reference it)
You can do the same thing to the other subquery then they can share the GROUP BY clause and you won't need the join anymore.
to get you started ... you can build on top of this :
select
userid,
SUM(CASE WHEN path='abc.com'then 1 else 0 end ) as num_website,
SUM(CASE WHEN path='Apply' and trackid is not NULL then 1 else 0 end ) as Apply_Num
from TABLE
WHERE path='abc.com' or path='Apply' -- may not need this ... play with it
group by userid
I have a table category which has fields and values as shown below in MYSQL database.
id name parent sort_order
1 Men null 0
2 Women null 1
3 shirt 1 0
4 salwar 2 1
Here parent is a foreign key points to the same table. In my category list page i want to print the parent hierarchy(if any) along with the category name. can i do this in a single query.
I have tried with group_concat in mysql but not able to generate the required result.
Expected Result:
1 men
2. women
3. men>shirt
4. women> salwar
You can use a self-join:
SELECT *
FROM
(
SELECT name, 1 AS rank, sort_order
FROM category
WHERE parent IS NULL
UNION ALL
SELECT CONCAT(c1.name,
CASE WHEN c2.parent IS NOT NULL THEN CONCAT('>', c2.name) ELSE '' END), 0 AS rank, c1.sort_order
FROM category c1 LEFT JOIN category c2
ON c1.id = c2.parent
WHERE c1.parent IS NULL
) t
ORDER BY t.rank DESC, t.sort_order
SQLFiddle
I am having trouble with a mysql query. I want to exclude values of 2. So I thought I would do following:
table products
id | name | backorder
-------------------
1 | product1 | NULL
2 | product2 | NULL
3 | product3 | 2
SELECT name from `products` p
WHERE backorder <> '2'
However, This is not giving the desired result of product1, product 2 It is giving an empty results table.
On the other hand if I use
SELECT name from `products` p
WHERE backorder = '2'
Then it produces: product3. But I want to get those records where it is not equal to 2.
Something is not working with the <> '2'. Could it be that the NULL values are throwing it off? Can anyone suggest a fix.
Thanks in advance!
use IS NULL or IS NOT NULL to compare NULL values because they are simply unknown.
SELECT name
from products p
WHERE backorder IS NULL OR backorder <> 2
SQLFiddle Demo
SQLFiddle Demo (added some records)
Working with NULL Values
Use the <=> operator.
You can use:
SELECT `name` FROM `products` `p`
WHERE NOT `backorder` <=> '2'
or
SELECT `name` FROM `products` `p`
WHERE !(`backorder` <=> '2')
See this answer for more information about the <=> operator:
What is this operator <=> in MySQL?
Sorry to open this
We can use this also
SELECT name
from products p
WHERE COALESCE(backorder,1) <> 2
Try this and see.
SELECT name from `products` p
WHERE backorder != '2'