SQL query to sort - mysql

I have table products and table seen. Every time a customer views a product an entry is added for that product in seen table. I want to retrieve list of all products from products table and sort the list of products in desc order of how many times its been viewed.Note if a product hasn't been viewed even once then there will be no entry in seen table, and that product should be put at top of the list followed by products which have been viewed once,twice and so on
SELECT products.product_id ,products.product_name , anon_1.seen_count
FROM products
LEFT OUTER JOIN (
SELECT seen.product_id , count(*) AS seen_count
FROM seen
GROUP BY seen.product_id
) AS anon_1 ON anon_1.product_id = products.product_id
ORDER BY anon_1.seen_count ASC;
My above query puts the products that haven't been viewed at the bottom. How do i fix this?
I have tried both descending and ascending. Descending puts the ones that have been least viewed at the bottom and ascending puts the ones that have not been viewed at the bottom

For LEFT OUTER JOIN, you should consider NULL value for products that haven't been viewed. So you should use this:
SELECT products.product_id ,products.product_name,
COALESCE(anon_1.seen_count, 0) seen_count
FROM products
LEFT OUTER JOIN (
SELECT seen.product_id , count(*) AS seen_count
FROM seen
GROUP BY seen.product_id
) AS anon_1 ON anon_1.product_id = products.product_id
ORDER BY seen_count ASC;

ORDER BY anon_1.seen_count DESC;
it sorts them from the greatest value to smallest.
You should use
ORDER BY anon_1.seen_count ASC;
to sort from the smallest to greatest.

Try this
SELECT products.product_id ,products.product_name , CASE WHEN anon_1.seen_count IS NULL THEN 0 ELSE anon_1.seen_count END AS count
FROM products
LEFT OUTER JOIN (
SELECT seen.product_id , count(*) AS seen_count
FROM seen
GROUP BY seen.product_id
) AS anon_1 ON anon_1.product_id = products.product_id
ORDER BY count DESC;

In Mysql and SqlServer, null value is the min value. But for Oracle, null value is the max value.
So you might using MySql or SqlServer, right?
Current result would be as follow, we need to put the last one to the top, which seen_count is null.
In order to do this, I add an additional column to solve it. Does that meet with your need? Hope that could help you!

Related

query to have all itms from one table but order by another table that doesn't have all items

I have one table products with id of product and name.
Second table is products_last_usage where I keep product_id, user_id and last_used_at.
Whenever a user clicks on a product, I have the field last_used_at updated.
Now, I need a query to list all products, and order them first by last_used_at, and then by name of product. It has to be PER USER. i.e. every user will have his own order of the table.
But I need all products, even if there are no records of them in the second table.
How to do that?
You can help me with a rails query or mysql query.
You can use a left join:
select p.*
from products p left join
products_last_usage plu
on plu.product_id = p.id and plu.user_id = $user_id
order by (last_used_at is not null) desc, last_used_at desc;
Ordering by last_used_at desc should also work. However, I think it is clearer to explicitly handle NULL values.
I think you can start from something like this. Pls next time post sample data, expected results etc.
SELECT A.PRODUCT_ID
, A.PRODUCT_NAME
, B.USER_ID
, B.LAST_USED_AT
FROM PRODUCTS A
LEFT JOIN PRODUCTS_LAST_USAGE B ON A.PRODUCT_ID = B.PRODUCT_ID
ORDER BY B.USER_ID, B.LAST_USED_AT DESC, A.PRODUCT_NAME;

mysql left join not returning empty row

I'm trying to get the list of categories with number of child records present in there. If the categories doesn't have records it should return NULL or 0 but my query returning categories with child records looks like its skipping the one without child records. ... will really appreciate the help.
here's my code:
SELECT
t_gal.f_sub_category_id,
t_sub_cat.f_sub_cat_name,
t_gal.f_image_thumb, (
SELECT COUNT(*)
FROM t_gallery
WHERE f_sub_category_id = t_gal.f_sub_category_id)
AS f_image_total
FROM t_gallery t_gal
LEFT JOIN t_sub_category t_sub_cat ON t_sub_cat.r_id = t_gal.f_sub_category_id
GROUP BY t_sub_cat.r_id
ORDER BY t_gal.f_added_on DESC, t_gal.r_id DESC
Here's the two tables:
Problem appears to be your group by clause.
You are grouping by a field that is on the LEFT JOINed table, hence when it does the group by all the rows which do not have a matching row on that table would appear to be aggregated into a single row.
I think what you are trying to get is a list of gallery items, along with the category they are in (if found) and the count of other galleries in the same category. If so try the following (if not let me know!)
SELECT t_gal.f_sub_category_id, t_sub_cat.f_sub_cat_name, t_gal.f_image_thumb, Sub1.GalleryCount
FROM t_gallery t_gal
LEFT JOIN t_sub_category t_sub_cat
ON t_sub_cat.r_id = t_gal.f_sub_category_id
LEFT OUTER JOIN (SELECT f_sub_category_id, COUNT(*) AS GalleryCount FROM t_gallery GROUP BY f_sub_category_id) Sub1
ON Sub1.f_sub_category_id = t_gal.f_sub_category_id
ORDER BY t_gal.f_added_on DESC, t_gal.r_id DESC
It LOOKS like for every sub-category (of a previously selected category), you want to include ALL of that sub-category... And, of those sub-categories, you want a count of how many images for that category wheather or not there even IS an image in the gallery table.
What you may have been running into is the select statement for the FIELD used to count images... first, that could become a performance killer. Instead, you could just do a left-join directly to the gallery table and COUNT the distinct R_IDs from the gallery FOR the corresponding sub-category
SELECT
t_sub_cat.r_id,
t_sub_cat.f_sub_cat_name,
MIN( COALESCE( t_gal.f_image_thumb, '' )) as JustOneThumbImg,
COUNT( DISTINCT( t_gal.r_id )) SubCategoryImageCount
FROM
t_sub_category t_sub_cat
LEFT JOIN t_gallery t_gal
ON t_sub_cat.r_id = t_gal.f_sub_category_id
GROUP BY
t_sub_cat.r_id
ORDER BY
t_sub_cat.f_added_on DESC
Since you are not grabbing all gallery images (since some may not exist FOR a given sub-category), ordering by the t_gal.r_id doesn't make sense
Also, the reason I'm not pre-grabbing aggregates in a sub-query to join against... I don't want to get everything from every category / sub-category without knowing which sub-categories are associated with the category you actually want.
the problem with your query is that you are using t_gallery as your main table and not t_sub_category while using left join.
you could try this: sqlfiddle
select
t_gal.f_sub_category_id,
t_sub_cat.f_sub_cat_name,
(
SELECT COUNT(*)
FROM t_gallery
WHERE f_sub_category_id = t_gal.f_sub_category_id)
AS f_image_total
from t_sub_category as t_sub_cat
left join t_gallery t_gal on t_gal.f_sub_category_id = t_sub_cat.r_id
GROUP BY t_sub_cat.r_id
ORDER BY t_gal.r_id DESC;

Attempting to Join 3 tables in MySQL

I have three tables that are joined. I almost have the solution but there seems to be one small problem going on here. Here is statement:
SELECT items.item,
COUNT(ratings.item_id) AS total,
COUNT(comments.item_id) AS comments,
AVG(ratings.rating) AS rate
FROM `items`
LEFT JOIN ratings ON (ratings.item_id = items.items_id)
LEFT JOIN comments ON (comments.item_id = items.items_id)
WHERE items.cat_id = '{$cat_id}' AND items.spam < 5
GROUP BY items_id ORDER BY TRIM(LEADING 'The ' FROM items.item) ASC;");
I have a table called items, each item has an id called items_id (notice it's plural). I have a table of individual user comments for each item, and one for ratings for each item. (The last two have a corresponding column called 'item_id').
I simply want to count comments and ratings total (per item) separately. With the way my SQL statement is above, they are a total.
note, total is the total of ratings. It's a bad naming scheme I need to fix!
UPDATE: 'total' seems to count ok, but when I add a comment to 'comments' table, the COUNT function affects both 'comments' and 'total' and seems to equal the combined output.
Problem is you're counting results of all 3 tables joined. Try:
SELECT i.item,
r.ratetotal AS total,
c.commtotal AS comments,
r.rateav AS rate
FROM items AS i
LEFT JOIN
(SELECT item_id,
COUNT(item_id) AS ratetotal,
AVG(rating) AS rateav
FROM ratings GROUP BY item_id) AS r
ON r.item_id = i.items_id
LEFT JOIN
(SELECT item_id,
COUNT(item_id) AS commtotal
FROM comments GROUP BY item_id) AS c
ON c.item_id = i.items_id
WHERE i.cat_id = '{$cat_id}' AND i.spam < 5
ORDER BY TRIM(LEADING 'The ' FROM i.item) ASC;");
In this query, we make the subqueries do the counting properly, then send that value to the main query and filter the results.
I'm guessing this is a cardinality issue. Try COUNT(distinct comments.item_id)

Ordering by multiple columns in MYSQL

I am building a small app that shows the cheapest price for a product by area. I need the result from my database to show only one result per area, and that result must have the lowest price of all rows for that area.
I've come up with this so far, which is nearly there but mixes up the result rows.
SELECT `products`.`id`, `products`.`area_id`, `products`.`date`,
`products`.`duration`, MIN(`products`.`price`) AS `price`, `products`.`rating`,
`products`.`buy_url` FROM `products` WHERE `price` >= '0' GROUP BY `products`.`area_id` ORDER BY
`price` ASC
Although this successfully returns only one result per area, using MIN() here seems to get the lowest price for that area, but the other columns will be from a different row (i.e. the row that would have been selected in its entirety had I not used MIN() above).
So, I obviously have this wrong. I'd be really grateful for some advice on how I can select the lowest 'price', along with the rest of that row, from each distinct area.
Thanks,
Matt
select t1.* from products as t1
inner join (
select area_id,min(price) as price
from products where price > 0
group by area_id) as t2
on t1.area_id = t2.area_id and t1.price = t2.price
alter table products add index i (area_id,price);
SELECT `products`.`id`, `products`.`area_id`, `products`.`date`, `products`.`duration`, MIN(`products`.`price`) AS `price`, `products`.`rating`, `products`.`buy_url` WHERE `price` >= '0' GROUP BY `products`.`id`, `products`.`area_id`, `products`.`date`, `products`.`duration`, `products`.`rating`, `products`.`buy_url` ORDER BY `price` ASC
You'll have to group by all the columns you are selecting.
what about this
SELECT MIN(p1.price) AS minPrice,
p1.id, p1.area_id, p1.date, p1.duration,p1.rating,p1buy_url
FROM products p1
LEFT JOIN products p2
ON (p1.area_id=p2.area.id AND p1.id<p2.id)
WHERE p2.id is NULL
GROUP BY area_id
ORDER BY p1.id ASC
note: you can not order by on a field( here u mention minPrice) which is not exist in table
LEFT JOIN is faster than INNER JOIN as u can check by using EXPLAIN keyword before SELECT
Reference Question

How do I limit the result of a subquery in MySQL?

Is there a way of limiting the result of a subquery? The sort of thing I'm trying to achieve can be explained by the query below:
SELECT *
FROM product p
JOIN (
SELECT price
FROM supplierPrices sp
ORDER BY price ASC
LIMIT 1
) ON (p.product_id = sp.product_id)
The idea would be to get only the lowest price for a particular product from a table that had all the price data in it. LIMIT 1 is limiting the entire result set, whereas excluding it would result in a row being returned for each price, with duplicated product data. I tried GROUP BY price as well to no avail.
Once the limit is working I need to apply IFNULL as well, so that if there is no price found at all for any supplier it can return a supplied string, such as "n/a" rather than NULL. I assume that would just mean modifying the SELECT as below, and changing the JOIN to a LEFT JOIN?
SELECT *, IFNULL(price,'n/a')
Just to expand on Wolfy's answer slightly, and bearing in mind this is untested:
SELECT *
FROM product p
LEFT JOIN (
SELECT product_id, MIN(price)
FROM supplierPrices sp
GROUP BY product_id
) x ON (p.product_id = x.product_id)
And, as you say, it should just be a matter of doing an IFNULL on that column to replace it with something sensible.