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

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;

Related

MySQL View in place of subquery does not return the same result

The query below is grabbing some information about a category of toys and showing the most recent sale price for three levels of condition (e.g., Brand New, Used, Refurbished). The price for each sale is almost always different. One other thing - the sales table row id's are not necessarily in chronological order, e.g., a toy with a sale id of 5 could have happened later than a toy with a sale id of 10).
This query works but is not performant. It runs in a manageable amount of time, usually about 1s. However, I need to add yet another left join to include some more data, which causes the query time to balloon up to about 9s, no bueno.
Here is the working but nonperformant query:
SELECT b.brand_name, t.toy_id, t.toy_name, t.toy_number, tt.toy_type_name, cp.catalog_product_id, s.date_sold, s.condition_id, s.sold_price FROM brands AS b
LEFT JOIN toys AS t ON t.brand_id = b.brand_id
JOIN toy_types AS tt ON t.toy_type_id = tt.toy_type_id
LEFT JOIN catalog_products AS cp ON cp.toy_id = t.toy_id
LEFT JOIN toy_category AS tc ON tc.toy_category_id = t.toy_category_id
LEFT JOIN (
SELECT date_sold, sold_price, catalog_product_id, condition_id
FROM sales
WHERE invalid = 0 AND condition_id <= 3
ORDER BY date_sold DESC
) AS s ON s.catalog_product_id = cp.catalog_product_id
WHERE tc.toy_category_id = 1
GROUP BY t.toy_id, s.condition_id
ORDER BY t.toy_id ASC, s.condition_id ASC
But like I said it's slow. The sales table has about 200k rows.
What I tried to do was create the subquery as a view, e.g.,
CREATE VIEW sales_view AS
SELECT date_sold, sold_price, catalog_product_id, condition_id
FROM sales
WHERE invalid = 0 AND condition_id <= 3
ORDER BY date_sold DESC
Then replace the subquery with the view, like
SELECT b.brand_name, t.toy_id, t.toy_name, t.toy_number, tt.toy_type_name, cp.catalog_product_id, s.date_sold, s.condition_id, s.sold_price FROM brands AS b
LEFT JOIN toys AS t ON t.brand_id = b.brand_id
JOIN toy_types AS tt ON t.toy_type_id = tt.toy_type_id
LEFT JOIN catalog_products AS cp ON cp.toy_id = t.toy_id
LEFT JOIN toy_category AS tc ON tc.toy_category_id = t.toy_category_id
LEFT JOIN sales_view AS s ON s.catalog_product_id = cp.catalog_product_id
WHERE tc.toy_category_id = 1
GROUP BY t.toy_id, s.condition_id
ORDER BY t.toy_id ASC, s.condition_id ASC
Unfortunately, this change causes the query to no longer grab the most recent sale, and the sales price it returns is no longer the most recent.
Why is it that the table view doesn't return the same result as the same select as a subquery?
After reading just about every top-n-per-group stackoverflow question and blog article I could find, getting a query that actually worked was fantastic. But now that I need to extend the query one more step I'm running into performance issues. If anybody wants to sidestep the above question and offer some ways to optimize the original query, I'm all ears!
Thanks for any and all help.
The solution to the subquery performance issue was to use the answer provided here: Groupwise maximum
I thought that this approach could only be used when querying a single table, but indeed it works even when you've joined many other tables. You just have to left join the same table twice using the s.date_sold < s2.date_sold join condition and make sure the where clause looks for the null value in the second table's id column.

SQL average and Join

I'm trying to merge these two statements into one query to get the a list of product names(or ids) against the average of their TTFF data, and I'm stuck.
select AVG(TTFF) from TTFFdata group by product_id
select product.product_name, count(*) from product join TTFFdata on product.product_id = TTFFdata.product_id
I've looked into using a temporary table (CREATE TEMPORARY TABLE IF NOT EXISTS averages AS (select AVG(TTFF) from TTFFdata group by product_id)) but couldn't get that to work with a join.
Anyone able to help me please?
You need to understand the components. Your second query is missing a group by. This would seem to be what you want:
select p.product_name, count(t.product_id), avg(t.TTFF)
from product p left join
TTFFdata t
on p.product_id = t.product_id
group by p.product_name
It is better to do group by on product_id, product_name for two reasons. One is, you can select product id along with product name. Second reason is, If the product name is not unique then it may give wrong results(this may be a rare scenario like product name is same but it differs based on other columns like version or model). The below is the final query.
select Product.product_id,
product_name,
AVG(TTFF) as Avg_TTFF
from Product
inner join
TTFFdata
on Product.product_id = TTFFdata.product_id
group by Product.product_id,Product.product_name
TTFFdata:
product:
Output:

SQL query to sort

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!

MySQL - Trying to sort by two different columns from different tables

Hey I'm having a bit of trouble coming up with the SQL for the action I would like to perform.
I have a table listings which has a start_price field and an active field. I only want to pull up listings that are active(1).
I want to order these listings by the current price. I do not have a current price field in either my listings or bids table. So if a listing_id is in the bids table I want to assign the largest value in the amount field that corresponds to the matching listing_id from the bids table, to the current price, otherwise the start_price from listings should be assigned to the current price.
This is what I have come up with so far. I have been mucking around with different things and cannot come up with the correct syntax.
SELECT DISTINCT * FROM listings l LEFT JOIN bids b ON l.id = b.listing_id WHERE l.active = 1 ORDER BY l.start_price
There are multiple ways that you can do the calculation. I think the key idea for you is that you can use a column alias in the order by clause.
The following does the calculation using a correlated subquery, then using coalesce() if there is no match:
select l.*,
coalesce( (select max(b.price)
from bids b
where b.listing_id = l.listing_id
), l.start_price
) as current_price
where l.active = 1
from listings l
order by current_price;
Note: I am a little concerned about using the maximum price from the bids, rather than the most recent price. Is it possible that bids could be withdrawn, but the row remains in the table?
You'll need a GROUP BY and an aggrate function, in this case MAX():
SELECT
l.*, /* columnlist here */
COALESCE(MAX(b.current_price), l.start_price) price
FROM
listings l
LEFT JOIN bids b
ON l.id = b.listing_id
WHERE l.active = 1
GROUP BY l.listing_id
ORDER BY price
When there are no bids on a listing the MAX functions returns NULL so the second value in de COALESCE will be selected.
I think this is going to solve the problem.
SELECT
listing_id,
COALESCE(current_price, start_price) current_price
FROM
(SELECT
l.id, b.listing_id, l.start_price, MAX(b.Amount) current_price
FROM
listing l
LEFT JOIN bids b ON l.id = b.listing_id
WHERE
l.active = 1
GROUP BY
l.id, b.listing_id, l.start_price) Data
ORDER BY
COALESCE(current_price, start_price)
In the subquery I am getting the start_price and the maximum price of each item (one row per item).
In the main query I am using that information to define if I want to use the maximum value or if it is NULL the start_price.
Sorry if there is any mistake or mistype but I don't have a mysql server in here to test it.
Hope this helps

Select corresponding records from another table, but just the last one

I have 2 tables authors and authors_sales
The table authors_sales is updated each hour so is huge.
What I need is to create a ranking, for that I need to join both tables (authors has all the author data while authors_sales has just sales numbers)
How can I create a final table with the ranking of authors ordering it by sales?
The common key is the: authorId
I tried with LEFT JOIN but I must be doing something wrong because I get all the authors_sales table, not just the last.
Any tip in the right direction much appreciated
If you're looking for aggregate data of the sales, you'd want to join the tables, group by the authorId. Something like...
select authors.author_id, SUM(author_sales.sale_amt) as total_sales
from authors
inner join author_sales on author_sales.author_id = authors.author_id
group by authors.author_id
order by total_sales desc
However (I couldn't distinguish from your question whether the above scenario or next is true), if you're only looking for the max value of the author_sales table (if the data in this table is already aggregated), you can join on a nested query for author_sales, such as...
select author.author_id, t.sales from authors
inner join
(select top 1 author_sales.author_id,
author_sales.sale_amt,
author_sales.some_identifier
from author_sales order by some_identifier desc) t
on t.author_id = author.author_id
order by t.sales desc
The some_identifier would be how you determine which record is the most recent for author_sales, whether it is a timestamp of when it was inserted or an incremental primary key, however it is set up. Depending on if the data in author_sales is aggregated already, one of these two should do it for you...
select a.*, sum(b.sales)
from authors as a
inner join authors_sales as b
using authorId
group by b.authorId
order by sum(b.sales) desc;
/* assuming column sales = total for each row in authors_sales */