selecting the least value in a joined table - mysql

Basically I have product and several models for those products. Each model has a price.
This is what I intended to do:
Mark a product as featured, then have it's title, description, number 1 image's thumbnail and the price for the cheapest model
This is my current query:
SELECT
product.title,
product.url_name,
product.description,
price.price,
image.thumbnail
FROM
mps_contents AS product
LEFT OUTER JOIN
mps_contents AS image
ON
image.page_id = product.content_id AND
image.display_order = '1' AND
image.resource_type = 'image'
LEFT OUTER JOIN
mps_contents AS model
ON
product.content_id = model.page_id
INNER JOIN
mps_product_info AS price
ON
model.content_id = price.content_id
WHERE
product.active = '1' AND
product.resource_type = 'product' AND
product.featured = '1'
ORDER BY RAND( )
LIMIT 3
You may see that my query cannot do the price sorting, I hope somebody could help me with that. An additional problem that I encounter is if I have multiple models for a product. I end up getting a set that has prices for 2 models from a single product when the intent is to have the 1 price for each product.
I am aware of the issue with ORDER BY RAND() but I will ignore it since I don't think this site will have more that 50 products.

I think something like this should work....
SELECT
product.title,
product.url_name,
product.description,
A.price,
image.thumbnail
FROM
mps_contents AS product
LEFT OUTER JOIN
mps_contents AS image
ON
image.page_id = product.content_id AND
image.display_order = '1' AND
image.resource_type = 'image'
LEFT OUTER JOIN (
SELECT price.price
FROM mps_contents AS model
JOIN mps_product_info price ON (model.content_id = price.content_id)
WHERE model.page_id = product.content_id
ORDER BY price.price
LIMIT 1
) AS A
WHERE
product.active = '1' AND
product.resource_type = 'product' AND
product.featured = '1'
ORDER BY RAND( )
LIMIT 3

Related

MySQL GROUBY counts is Inconsistent

I was trying to find out product counts with GROUP_BY on name
However These 2 queries confused me a bit.
How can the count of distinct products (Which is 204 -> Query-A) be higher than actual number of products (Which is single -> Query-B)?
Query-A
SELECT an.name
, COUNT(DISTINCT product.productid)
FROM product
JOIN productkeywords pk
ON product.productid = pk.productid
JOIN categorydisplayattributes cda
ON product.categoryid = cda.categoryid
JOIN attributenames an
ON cda.attributeid = an.attributeid
AND an.name = 'Number of Fixed Shelves'
WHERE pk.keywords LIKE '%mouse%'
GROUP
BY an.name
LIMIT 100
Output-A
name , COUNT(DISTINCT product.productid)
-------------------------+-------
'Number of Fixed Shelves', '204'
Query-B
SELECT an.name, product.productid FROM product
JOIN productkeywords pk ON product.productid = pk.productid
JOIN categorydisplayattributes cda ON product.categoryid = cda.categoryid
JOIN attributenames an ON cda.attributeid = an.attributeid AND an.name = 'Number of Fixed Shelves'
WHERE pk.keywords LIKE '%mouse%'
GROUP BY an.name
LIMIT 100
Output-B
name , productid
-------------------------+--------------
'Number of Fixed Shelves', '1025794284'
By adding the productid to the GROUP BY you ensure one output row per name/product combination.
By leaving it out you were only get one row per name. (And so it was being forced to pick one arbitrary productid from the list of 204 possible values.)
SELECT an.name, product.productid
FROM product
JOIN productkeywords pk ON product.productid = pk.productid
JOIN categorydisplayattributes cda ON product.categoryid = cda.categoryid
JOIN attributenames an ON cda.attributeid = an.attributeid
AND an.name = 'Number of Fixed Shelves'
WHERE pk.keywords LIKE '%mouse%'
GROUP BY an.name, product.productid
(You also want to remove the LIMIT if you want all 204 rows.)

Select by multiple values in left join

Working with following structure, how can I select a subset of requests respecting multiple conditions in this 1:n relation?
# Table 1: Request
uid
name
# Table 2: Additional Information
uid
type
value
request
Note for table 2: type can be anything, i.e. 'product_name' or 'rating'.
If I'd just want to select Requests by a given product_name I can do this:
SELECT * FROM request as r
LEFT JOIN additional_information as i
ON r.uid = i.request
WHERE i.type = 'product' AND i.value = 'Product Name'
I'm stuck at what my statement must look like if I want to select Requests by a given product_name AND rating. I have tried to simply add another join but this gave me all requests that related to a given product_name as well as all requests related to a given rating. I need the statement to respect both conditions.
This, as mentioned, does not work for me.
SELECT * FROM request as r
LEFT JOIN additional_information as i
ON r.uid = i.request
LEFT JOIN additional_information as a
ON r.uid = a.request
WHERE i.type = 'product' AND i.value = 'Product Name'
OR a.type = 'rating' AND a.value = 1
Appreciate the help!
Move those conditions from WHERE to JOIN ON condition like
SELECT * FROM request as r
LEFT JOIN additional_information as i
ON r.uid = i.request
AND i.type = 'product' AND i.value = 'Product Name'
LEFT JOIN additional_information as a
ON r.uid = a.request
AND a.type = 'rating' AND a.value = 1;
And Yes absolutely, considering the valuable comment (which missed) from Strwabery, instead of doing a select * or select r.* you might actually want to specify the column names you want to fetch which is better than * performance wise since you are not getting unnecessary data using projection; unless you really want to fetch everything.
It might be slow from the other offering since it is joining to your additional data TWICE, once for product, again on rating. This should probably be changed to a single left-join otherwise you could get a Cartesian result bloating your answer.
SELECT *
FROM
request as r
LEFT JOIN additional_information as i
ON r.uid = i.request
AND ( ( i.type = 'product' AND i.value = 'Product Name' )
OR ( i.type = 'rating' AND i.value = 1 );

Adding a subquery to a join

I've been using the following join, to pull rows of users whom have volunteered for various project positions.
SELECT p.id, up.position_id, title, max_vol, current_datetime, IF(up.id IS NULL, "0", "1") volunteered
FROM positions AS p
LEFT JOIN users_positions AS up
ON p.id = up.position_id
AND up.user_id = 1
AND up.calendar_date = '2016-10-03'
WHERE
p.project_id = 1
AND p.day = 1
...but in a change of functionality, I have to now account for the date of the latest edit to a project. In another query, I solved it like so
SELECT *
FROM positions
WHERE
current_datetime = (SELECT MAX(current_datetime)
FROM positions
WHERE
project_id = 1 AND day = 1)
Which works fine, but now I have to also incorporate the return of rows which match the latest datetime in the left join query.
I just can't seem to wrap my head around it. Any suggestions? Thanks.
Use a sub query, like this:
SELECT
p.id,
up.position_id,
title,
max_vol,
current_datetime,
IF(up.id IS NULL,
"0",
"1") volunteered
FROM
( SELECT
*
FROM
positions
WHERE
current_datetime = (
SELECT
MAX(current_datetime)
FROM
positions
WHERE
project_id = 1
AND day = 1
)
) AS p
LEFT JOIN
users_positions AS up
ON p.id = up.position_id
AND up.user_id = 1
AND up.calendar_date = '2016-10-03'
WHERE
p.project_id = 1
AND p.day = 1

Mysql UPDATE based on lowest value in SELECT

I've got the following Mysql structure:
jss_products
productID
price1
jss_extrafields_values
exvalID
productID
extraFieldID
jss_extrafields_prices
exvalID
price1
Each product has a few extrafields. I'm interested in extraFieldID = 1
I wish to update all of the price1 in jss_extrafields_prices using the value of jss_products.price1. I have the following query but it only updates the first entry per product in jss_extrafields_price, not all entries.
I'm trying to normalize the prices in jss_extrafields_prices so that for a product which has a price of 20.00, each relevant entry in jss_extrafields_prices becomes CURRENTPRICE - 20.
Does that make sense? Here's what I have so far
UPDATE jss_extrafields_prices AS JEP
INNER JOIN (
SELECT P.productID, P.price1 AS P1, EP.price1, EP.exvalID FROM jss_products AS P
INNER JOIN jss_extrafields_values AS EV
ON P.productID = EV.productID
INNER JOIN jss_extrafields_prices AS EP
ON EV.exvalID = EP.exvalID
WHERE EV.extraFieldID = 1
GROUP BY P.productID
ORDER BY P.productID DESC, EP.price1 DESC
) AS X
ON JEP.exvalID = X.exvalID
SET JEP.price1 = JEP.price1 - X.P1
I would expect the inner query to return something like:
productID = 1090
P1 = 20.8333333
price1 = 20.8333333
exvalID = 3236
Knowing that productID of 1090 has 3 pricing options and its base price is 20.83333 I would then want to update every matching product in jss_extrafields_prices to be the current price minus the base price.
Does that help?
Stripping away some of the information that was previously classes as being too localized, this simplified to an UPDATE with an INNER JOIN:
UPDATE jss_extrafields_prices
INNER JOIN (
SELECT P.productID, P.price1 AS baseprice, JEP.price1 AS optionprice, JEP.exvalID, (JEP.price1 - P.price1) AS adjustedprice FROM jss_products AS P
INNER JOIN jss_extrafields_values AS JEV
ON P.productID = JEV.productID
INNER JOIN jss_extrafields_prices AS JEP
ON JEV.exvalID = JEP.exvalID
WHERE JEV.extraFieldID = 1
) AS X
ON jss_extrafields_prices.exvalID = X.exvalID
SET jss_extrafields_prices.price1 = X.adjustedprice

Complex searching for tags

I have currently have a problem with searching products with tags:
Products
id name
1 lightbulb
Tags
id name
1 energy
2 light
3 lights
Tagships
id taggable_id tag_id
1 1 1
1 1 2
1 1 3
I need to build a query to get the products that are tagged as (energy) and (light or lights)
So far this doesn't work:
SELECT ..<snipped>..
FROM `products`
LEFT OUTER JOIN `tagships`
ON (`products`.`id` = `tagships`.`taggable_id`)
LEFT OUTER JOIN `tags`
ON (`tags`.`id` = `tagships`.`tag_id`)
WHERE ((tags.name = 'energy' OR tags.name = 'energies')
AND (tags.name = 'light' OR tags.name = 'lights'))
GROUP BY products.id
HAVING COUNT(tagships.tag_id) <= 2
ORDER BY products.updated_at DESC
UPDATED the query.
Note: I need the query to search for singular and plural tense of the tag as seen in the above query.
try the following query:
SELECT ..<snipped>..
FROM `products`
LEFT OUTER JOIN `tagships`
ON (`products`.`id` = `tagships`.`taggable_id`
AND `tagships`.`taggable_type` = 'Product')
LEFT OUTER JOIN `tags`
ON (`tags`.`id` = `tagships`.`tag_id`)
WHERE ((tags.name = 'energy' OR tags.name = 'energies')
OR (tags.name = 'light' OR tags.name = 'lights'))
GROUP BY products.id
HAVING COUNT(tagships.tag_id) <= 2
ORDER BY products.updated_at DESC
Apparently, a tag can't be 'energy' or 'energies' and, at the same time, 'light' or 'lights'. So, the condition in your query must consist of ORs only:
WHERE t.name = 'energy'
OR t.name = 'energies'
OR t.name = 'light'
OR t.name = 'lights'
Alternatively, of course, you can use IN:
WHERE t.name IN ('energy', 'energies', 'light', 'lights')
Another thing is, how to check that a product has both at least one of the first two and at least one of the other two. I would probably do it using a HAVING clause like this:
HAVING COUNT(CASE WHEN t.name IN ('energy', 'energies') THEN 1 END) > 0
AND COUNT(CASE WHEN t.name IN ('light', 'lights') THEN 1 END) > 0
This works:
SELECT distinct p.id, p.name
FROM products as p
join tagships as ts on ts.product_id = p.id
join tags as t on t.id = ts.tag_id
where t.name in ('energy', 'energies', 'light', 'lights');
I've used aliases to make the code more readable. Also you don't want outer joins, since you don't want records that don't match in the joined tables.