Using GROUP BY to get the entry with the highest value - mysql

I need to create a product list with a preview image for each product.
I have a pretty simple data structure for products. One table is for the products, and one table for the images of a product. A product can have any number of images. The structure looks like this:
PRODUCT
id | name
1 Test Product A
2 Test Product B
3 Test Product C
PRODUCTIMAGE
id | productId | file | priority
1 1 foo.jpg 0
2 1 bar.jpg 1
3 2 something.png 1
4 2 yada.png 0
5 1 yougettheidea.gif 2
Pretty straight forward. The only thing worth mentioning about this is that productimages have a "priority", which is a TINYINT to determine the display order of images for a given product. The idea is: The higher the priority, the earlier the image should be displayed in the list of product images on the detail page. But for this product list that we are about to create, I'm only gonna need one preview image per product.
So as stated initially, my goal is to get a list of all products. So let's start pretty simple:
SELECT *
FROM product
Now I also want to display one preview image in the product page, so I need a little join:
SELECT `p`.*,
`pi`.`file` `previewImage`
FROM `product` `p`
LEFT JOIN `productImage` `pi` ON (`pi`.`productId` = `p`.`id`)
GROUP BY `p`.`id`
So far so good, this gives me one preview image per product to display on the product list. Just one more step to go: I want the preview image with the highest priority for each product as the preview image. So I tried to use a subquery to get the product images in the desired priority order:
SELECT `p`.*,
`pi`.`file` `previewImage`
FROM `product` `p`
LEFT JOIN (
SELECT *
FROM `productImage`
ORDER BY `priority` DESC
) `pi` ON (`pi`.`productId` = `p`.`id`)
GROUP BY `p`.`id`
But for some reason this doesn't (reliably) get me the product image with the highest priority for each product. Why is that? I think that GROUP BY is selecting the wrong productImage entry to keep, but why? Shouldn't it pick the first one, which due to the subquery should be the one with the highest priority?

Your group by is a partial group by. You are grouping by product.id so MySQL will group the result per product, but within each group it is free to return any row from productimage table. To get deterministic results, each column from that table needs to be wrapped inside aggregate functions (MIN, MAX, etc) but that will not give you the image with highest priority.
That being said, if you want only one column from the productimage table you can use a subquery inside select:
select product.*, (
select file
from productimage
where productimage.productid = product.id
order by priority desc
limit 1 -- this is the important bit
) as productimage_file
from product

Related

MySQL finding all elements that have the same value in column A but different values in cloumn B

I'm fairly new to SQL and have a question regarding a query.
I have a database with various pictures attached to a product. All these pictures have a prediction. The structure is like this:
product_ id picture_id prediction
1------------pic1.jpg----------type a
1------------pic2.jpg----------type b
1------------pic3.jpg----------type b
2------------pic4.jpg----------type a
2------------pic5.jpg----------type a
2------------pic6.jpg----------type a
3------------pic7.jpg----------type c
...
... so on.
Each pictures is predicted individually and because of that some of the products have contradictory predictions (meaning that on the same products some pictures are predicted type a while others are predcited type b).
I want to filter out all of these products with a query. In other words: I need all product_ids where the predictions for the pictures linked to it are not all the same. In our example I want it only to show me product 1.
I tried some stuff with GROUP BY, but have not yet gotten anywhere near the result that I want.
Thanks for helping,
Cheers
Use COUNT(DISTINCT prediction) to get the number of different predictions. It will be more than 1 for the products with different predictions.
SELECT product_id
FROM yourTable
GROUP BY product_id
HAVING COUNT(DISTINCT prediction) > 1

Where count is exact not more or less mysql

I have one_to_many relationship where i have 3 tables images, items and image_items .. every image may have one or many items, i wanna make a query where i pass the item/items id/ids and get a specific image (exactly one image) based on the count of items inside this image ...
Here is what i have tried so far but it has a problem, it gives me images with that count or more, i'm not sure if this logic or structure is valid in the first place:
SELECT `image_items`.`image_id`
FROM `image_items`
WHERE `image_items`.`item_id` IN(1, 2)
GROUP BY `image_items`.`image_id`
HAVING count(image_items.image_id) = 2
Here is the fiddle
Lets say image (1) has items (1,2,3), image (2) has items (1,3) and image (3) has items (1,2) .. lets say i want the image that has exactly (1,2) .. with my query even with distinct it gives me images (1,3) when i want only image (3).
Remove the WHERE clause and add in the HAVING clause a condition :
SELECT image_id
FROM image_items
GROUP BY image_id
HAVING
COUNT(DISTINCT item_id) = 2
AND
SUM(item_id NOT IN (1, 2)) = 0
See the demo.
Result:
| image_id |
| -------- |
| 4 |
Forpas's solution is a good solution. A slightly simpler having clause is:
SELECT image_id
FROM image_items
GROUP BY image_id
HAVING GROUP_CONCAT(item_id ORDER BY item_id) = '1,2';
If you are constructing this query in an application, this is simpler because you only need to pass in one value, the string '1,2'.
I hope you need to use DISTINCT in the HAVING clause as HAVING COUNT(DISTINCTimage_id) = 2 to get the exact count.
SELECT `image_id`
FROM `image_items`
WHERE `item_id` IN (1, 2)
GROUP BY `image_id`
HAVING COUNT(DISTINCT `image_id`) = 2

What are the ways to model multivalued M : N relationship?

I am working on the project that requires multivalued M : N relationship.
For eg.
There is a list of products in the Products table.
The user can purchase 1 or more products and are maintained in the table Orders. Along with this information, there is one more table that maintains analytical information.
This table should contain the hard coded data like. If the order contains product 1 and product 2 then does product 3 and product 4 also appeared in the user's order. These are basically hard coded rules
actual products | expected products
1,3 | 2,4
5,6,7,8 | 3,4
Now from these tables, I need to find information like if user's order number 1 had products like 1 and 3 then return 2 and 4.
I need suggestions as to how to describe this multivalued M to N relationship. If there are any other options other than RDBMS feel free to suggest. Thanks
You could create three additional tables:
ProductSet(productset_id) - Stores product sets headers(one row from your sample)
ProductSetPart(productset_id,product_id) - Stores required products to make set, in your case column actual product
ProductSetAdditional(productset_id,product_id) - stores expected products.
Having given Order we can detect which additional products should be added.
EDIT: sample added
Example query that return list of Sets that are fulfilling requirements:
SELECT psp.productset_id FROM
ProductSetPart psp LEFT JOIN
OrderLines ol
ON
psp.product_id=ol.product_id
GROUP BY
psp.productset_id
HAVING
-- trick - COUNT(*) will return count of all products required by aggregatet set
-- COUNT(psp.product_id) will count only not null products
-- (which will be null if they aren't in order line)
-- so if all product sets are in order line then we know,
-- that this set requirements are full filled
COUNT(*) = COUNT(psp.product_id)

Self inner join to get single record

I have an SQL table data as follow
I want to display single record for product
example
90792 Amlaan-Hi-Power .............. Show only 1 record when there are 2 record
90793 Amlaan-Neutral .............. show only 1 record when there are 2 record
90794 Amlaan-Phosphate free .........show only 1 record when there are 2 record
90801 Acetone .......................show only 1 record when there are 2 record
90901 Acetanilide ...................show only 1 record when there is 1 record
Can I do this using Inner join
I know
select distinct product from product ORDER BY `product`.`product` DESC
will select distinct (unique) product code and that to only one field i.e. product but confused how to get other information using SQL statement
but results in duplicate records or same table...........................
It looks like your duplicate rows vary by the quantity of product in the package.
You can display just the product and name with
SELECT DISTINCT product, name
FROM product
If you want to deal with the quantity as well, that's a little trickier. This might work: it will put all product codes on one line.
SELECT product,
GROUP_CONCAT(product_code ORDER BY product_code) product_codes,
name
FROM product
GROUP BY product, name
Self join doesn't make a whole lot of sense for this application.
Use group by option for such purposes.
SELECT product,GROUP_CONCAT(product_code SEPERATOR '|') AS product_code,name FROM Table GROUP BY NAME
It will show only one record for duplicate names.
The multiple enteries of product code will seperated by | .

Sql reports with BI Studio (BIDS) , I want one Entry per Page

Imagine I have 10 Categories. For each categories I have many Products.
Category
CategoryID(PK)
CategoryName
CategoryDescription
Product
ProductID(PK)
CategoryID(FK)
ProductName
ProductPrice
ProductDescription
ProductNumber
I would like to have 1 page per category.
On each page, I want the category name, the category Descripton.
And in a Table, I want the Product list.
How Can I perform this ?.
Thanks a lot.
First you need a query that gives you the information you want. This will be saved as your dataset within the report. This could be as simple as
SELECT c.CategoryName,
c.CategoryDescription,
p.ProductName,
p.ProductPrice,
p.ProductDescription,
p.ProductNumber
FROM Category c
JOIN Product p ON --insert join criteria here... like c.CategoryId = p.CategoryId
Name that dataset Products.
Next, drag a table onto your report. Define the dataset for the table to be the dataset you created above.
Create a group within the table, based on your Category primary key (CategoryName? some CategoryId?). Order this group by CategoryName (if you want alphabetical order).
Now in your table you can put the Category Name & Category Description in the group header of the table. All your product information will go in the table detail rows.
This will get you started. You can options at the group level to start a new page at the beginning of each group. You can also put in running totals (sums, averages, etc) that reset at a change in group.