I have two tables 1.category 2. product tables.I want total products under a category,total sub-products under a category with author who last created the product or sub-product under product along with create date.
My Query is working like below:
SELECT c.category_id,c.title,c.type,c.alias,
(SELECT COUNT(product_id)
FROM products
WHERE parent_type='main'
AND category_id=c.category_id
AND is_active=1
) as total_main_products,
(SELECT count(product_id)
FROM products
WHERE parent_type IN('subtypeL1','subtypeL2','subtypeL3')
AND category_id=c.category_id
AND is_active=1
) as total_sub_products,
DATE_FORMAT(
(SELECT FROM_UNIXTIME(created) as update_time
FROM products
WHERE parent_type='main'
AND category_id=c.category_id
ORDER BY update_time desc limit 1
),'%b %d,%Y at %r'
)as last_updated,
(SELECT user_id
FROM products
WHERE parent_type='main'
AND category_id= c.category_id
ORDER BY created desc limit 1
) as updated_by
FROM category c
WHERE c.type=1
AND c.is_active=1
ORDER BY c.created DESC
Looking for a discussion , Is this the right & efficient way to write query or we have any other way to get the same result.I tried JOIN options but not returning the correct result.
Any help or suggestion would be greatly appriciated.
Thanks
Something like this (without last updated_by field) -
SELECT c.category_id, c.title, c.type, c.alias,
COUNT(IF(p.parent_type='main' AND p.is_active = 1, p.product_id, 0)) total_main_products,
COUNT(IF((p.parent_type='subtypeL1' OR p.parent_type='subtypeL2' OR p.parent_type='subtypeL3') AND p.is_active = 1, p.product_id, 0)) total_sub_products,
FROM_UNIXTIME(MAX(IF(p.parent_type='main', update_time, 0))) last_updated -- add formatting here
FROM category c
LEFT JOIN products p
ON p.category_id = c.category_id
WHERE
c.type = 1 AND c.is_active = 1
GROUP BY
p.category_id
ORDER BY
c.created DESC
Related
I have a problem here, I wonder if it is possible to do this in just one query instead of having the server do several queries.
So here's the thing, what I want is, in each category, to search all stores belonging to that category within a limit of 24 stores. That is, imagining that I have 5 categories registered, I will search 24 stores belonging to that category, which in the conclusion I will have a total of 120 stores in the query result.
The following code is an example, but this will only fetch the first 24 stores of any category without iterating through each category.
SELECT *
FROM categories c
LEFT JOIN (SELECT * FROM stores_categories LIMIT 24 OFFSET 0) sc ON sc.id_category = c.id
WHERE type = 'STORE' OR type = 'ALL';
Someone sent me this and it is very similar to my problem, How to SELECT the newest four items per category?, It is kind of like that, but I would like to be able to limit and with offset to make pages and not just the first 24 most recent in each category.
The example code from the link I was given:
SELECT sc1.*
FROM stores_categories sc1
LEFT OUTER JOIN stores_categories sc2 ON (sc1.id_category = sc2.id_category AND sc1.id_store < sc2.id_store)
GROUP BY sc1.id_store
HAVING COUNT(*) < 24
ORDER BY sc1.id_category;
And the database server which one I'm is the version 5.5.64-MariaDB. An fiddle example where is possible to make tests with same values from my database. https://www.db-fiddle.com/f/jcowJL9S4FQXqKg2yMa8kf/0
It could make use of a calculated row_number per category.
--
-- Emulated row_number via variables
--
SELECT sc.id, id_store
, cat.name AS cat_name
, cat.type AS cat_type
, rn
FROM
(
SELECT sc.*
, #rn := CASE
WHEN #cat_id = sc.id_category
THEN #rn + 1 ELSE 1
END AS rn
, #cat_id := sc.id_category as cat_id
FROM stores_categories sc
CROSS JOIN (SELECT #cat_id:=0, #rn:=0) vars
ORDER BY sc.id_category, sc.id_store DESC, sc.id DESC
) sc
LEFT JOIN categories cat
ON cat.id = sc.id_category
WHERE rn BETWEEN 1 AND 24
ORDER BY sc.id_category, sc.id_store DESC, sc.id DESC
Or
--
-- Grouped self-join
--
SELECT sc.id, sc.id_store
, cat.name AS cat_name
, cat.type AS cat_type
, COUNT(sc2.id_store) AS cnt
FROM stores_categories sc
LEFT JOIN stores_categories sc2
ON sc2.id_category = sc.id_category
AND sc2.id_store >= sc.id_store
LEFT JOIN categories cat
ON cat.id = sc.id_category
GROUP BY sc.id
HAVING cnt BETWEEN 1 AND 24
ORDER BY sc.id_category, sc.id_store DESC;
Or
--
-- Correlated sub-query
--
SELECT sc.id, id_store
, cat.name AS cat_name
, cat.type AS cat_type
, ( select count(*)
from stores_categories sc2
where sc2.id_category = sc.id_category
and sc2.id_store >= sc.id_store
) as rn
FROM stores_categories AS sc
LEFT JOIN categories cat
ON cat.id = sc.id_category
HAVING rn BETWEEN 1 AND 24
ORDER BY sc.id_category, sc.id_store DESC;
Demo on db<>fiddle here
My apologies but I cannot get my head around this one (even not after searching and trying out a few things). All I want to do is join two tables and then sort the join descending on the created_at in the article_translations table. However, I need unique entries.
I have two tables:
articles
--------
id
user_id
article_translations
--------
id
article_id (brings this table together with the other one)
locale
title
...
created_at
updated_at
Performing mysql query:
SELECT * from articles
JOIN article_translations as t on t.article_id = articles.id
ORDER BY t.created_at desc
I get the joined tables with the corresponding related entries.
articles.id t.article_id created_at
1 1 ''
1 1 ''
2 2 ''
When I try no to get rid of the duplicates, in this case of the article with id = 1, I get a nasty error:
Expression #3 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'blog.t.id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
The Desired result would be:
articles.id t.article_id created_at
1 1 ''
2 2 ''
Any help please... Thank You!
The only way to get unique rows is if you want the latest (or the earliest?) date for each id, which you can do if you group by a.id, t.article_id and aggregate:
SELECT a.id, t.article_id, MAX(t.created_at) AS created_at
FROM articles AS a INNER JOIN article_translations AS t
ON t.article_id = a.id
GROUP BY a.id, t.article_id
ORDER BY MAX(t.created_at) DESC
If you want all the columns of the 2 tables, first get unique rows from article_translations with NOT EXISTS and then join to articles:
SELECT *
FROM articles AS a INNER JOIN (
SELECT t.*
FROM article_translations t
WHERE NOT EXISTS (
SELECT 1 FROM article_translations
WHERE article_id = t.article_id AND created_at > t.created_at
)
) AS t
ON t.article_id = a.id
ORDER BY t.created_at DESC
This will work if there are not more than 1 rows in article_translations with the same maximum created_at for an article_id.
For MySql 8.0+ you could use ROW_NUMBER():
SELECT t.* FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY a.id ORDER BY t.created_at DESC) rn
FROM articles AS a INNER JOIN article_translations AS t
ON t.article_id = a.id
) AS t
WHERE t.rn = 1
You are almost there with the query in the question. You just need to add the distinct keyword:
SELECT distinct * from articles
JOIN article_translations as t on t.article_id = articles.id
ORDER BY t.created_at desc`
Thanks forpas for providing the correct answer. I needed this query basically for a Laravel Eloquent model in combination with eager loading. In case anybody cares, that's how the final solution now looks like:
$articles = Article::join('article_translations as at', function ($join) {
$join->on('articles.id', '=', 'at.article_id');
})
->select('articles.id',
'articles.user_id',DB::raw('MAX(at.created_at) as created_at'))
->groupBy('at.article_id')
->orderBy('created_at', 'desc')
->with('translations')
->get();
Pure SQL
SELECT at.article_id, MAX(at.created_at) as created_at FROM articles as a
INNER JOIN article_translations as at
ON a.id = at.article_id
GROUP BY at.article_id
ORDER BY MAX(created_at) desc
Since you do want all the columns:
If you are trying to keep just the article translation with the latest creation date, then assuming the creation dates are unique for a give article's translations, one way would be to create a subquery that computes for each article_translation.article_id the maximum article_translation.created_at column value:
SELECT articles.*, t.* from articles
JOIN article_translations as t on t.article_id = articles.id
JOIN (
SELECT article_id, max(created_at) as created_at from article_translations
GROUP BY article_id
) a_t on t.article_id = a_t.article_id and t.created_at = a_t.created_at
ORDER BY t.created_at desc
If the creation dates are not unique, or even if they are, then this should also work:
SELECT articles.*, t.* from articles
JOIN article_translations as t on t.article_id = articles.id
AND t.article_id = (
SELECT t2.article_id from article_translations t2
WHERE t2.article_id = t.article_id
ORDER BY created_date DESC
LIMIT 1
)
ORDER BY t.created_at DESC
I have a problem with SQL to select same information when I hve lot of clause
the first column is unknow in other clause.
This is my sql expression:
SELECT * ,
(select sum(sell) from product_details where product_details.product_id = products.id) as total ,
(select sell from product_details where product_details.product_id = products.id order by product_details.id desc limit 1) as ysell ,
(select sum(sell) as wsell FROM (select sell from product_details where product_details.product_id = products.id order by product_details.id desc limit 2 ) as weeksell) as wsell
FROM `products`
I try to get information from product and second table is product_details
based on product ID;
ysell = last sell
total = total sell
wsell = limit 7 sell by using sum() and select from the last 7
but whene i run my expression i get error
#1054 - Unknown column 'products.id' in 'where clause'
isn't knowing in this line
(select sum(sell) as wsell FROM (select sell from product_details where product_details.product_id = products.id order by product_details.id desc limit 2 ) as weeksell) as wsell
can you suggest any ideas.
Your problem is that correlation clauses cannot be nested more than one level deep -- hence the reason by products is not seen. One solution is to transform this to conditional aggregation. That is a bit tricky, but here is one method:
SELECT p.* ,
sum(pd.sell) as total,
max(case when pd.id = pdd.max_id then pd.sell end) as ysell,
sum(case when pd.id >= pdd.id7 then pd.sell end) as wsell
FROM products p JOIN
product_details pd
ON pd.product_id = p.id JOIN
(SELECT pd.product_id, MAX(pd.id) as max_id,
SUBSTRING_INDEX(SUBSTRING_INDEX(GROUP_CONCAT(pd.id ORDER BY pd.id DESC), ',', 7), ',', -1) as id7
FROM product_details pd
GROUP BY pd.product_id
) pdd
ON pdd.product_id = p.id
GROUP BY p.id; -- reasonable assuming `id` is unique/primary key
In MySQL I have 4 tables:
- product(id)
- order(id)
- order_detail_1(id, product_id, order_id, qty)
- order_detail_2(id, product_id, order_id, qty)
I want to get the sum of the quantity of products sold from the 2 tables (order_detail_1, order_detail_2) grouping them by product
produt can existe in order_detail_1 and not in order_detail_2 and vice versa
i tested this query and it worked but I want a simpler query without the union and the subquery.
select tmp.product_id ,sum(tmp.qty) from
(
(
select order_detail_1.product_id ,sum(order_detail_1.qty)
from order_detail_1
inner join order on order_detail_1.id_order = order.id
where order_detail_1.product_id is not null
group by order_detail_1.product_id
)
union all
(
select order_detail_2.product_id ,sum(order_detail_2.qty)
from order_detail_2
inner join order on order_detail_2.id_order = order.id
where order_detail_2.product_id is not null
group by order_detail_2.product_id
)
) tmp
group by tmp.product_id
It looks like you're not using order table other then checking if it exists, so you can use EXISTS()
SELECT p.product_id,sum(p.qty) as qty
FROM (SELECT product_id,qty,id_order FROM order_detail_1
WHERE product_id IS NOT NULL
UNION ALL
SELECT product_id,qty,id_order FROM order_detail_2
WHERE product_id IS NOT NULL) p
WHERE EXISTS(SELECT 1 FROM order o
WHERE o.id = p.id_order)
GROUP BY p.product_id
If a product is in only one table, you can use left join:
select p.id, (coalesce(sum(od1.qty), 0) + coalesce(sum(od2.qty, 0))) as qty
from product p left join
order_detail_1 od1
on od1.product_id = p.id left join
order_detail_2 od2
on od2.product_id = p.id
group by p.id;
This formulation depends on the fact that the two tables are exclusion -- a product is in only one table.
EDIT:
If products can exist in both tables, then you need to aggregate them first:
select p.id, (coalesce(od1.qty, 0) + coalesce(od2.qty, 0)) as qty
from product p left join
(select product_id, sum(qty) as qty
from order_detail_1 od1
group by product_id
) od1
on od1.product_id = p.id left join
(select product_id, sum(qty) as qty
from order_detail_2 od2
group by product_id
) od2
on od2.product_id = p.id;
I try to get a list of products with each newest and lowest offer price
Table product:
id | name
Table offer:
id | product_id | price | created | dealer_id
Table invalids:
id | offer_id | status
I have tried:
SELECT * FROM product INNER JOIN
(
SELECT offer.product_id , offer.price
FROM offer
LEFT JOIN invalids
ON offer.id = invalids.offer_id
WHERE invalids.id IS NULL
GROUP BY offer.dealer_id
ORDER BY offer.created DESC
) o
ON o.product_id = product.id
ORDER BY product.name
I have tried an sqlfiddle http://sqlfiddle.com/#!9/32658/3 with this offer values:
(`id`, `price`, `dealer_id`, `product_id`, `created`)
(1,12.60,1,1,'2015-05-17 08:44:45'),
(2,13.00,1,1,'2015-08-17 08:44:45'),
(3,20.00,1,1,'2015-08-17 08:45:30'),
(4,10.00,1,1,'2015-08-17 08:45:46'),
(5,4.00,2,1,'2015-05-17 08:44:11'),
(6,11.00,2,1,'2015-08-17 08:44:46'),
(7,5.00,2,1,'2015-08-17 08:45:31'),
(9,110.00,2,2,'2015-08-17 08:46:58'),
(10,11.00,2,2,'2015-08-17 08:47:12');
Expected value for product ID 1 is offer ID 7 with price 5.
These steps I think I must realize:
Order offers by created and group by dealer_id to get newest entries
Take result from step 1 and order it by price to get smallest price.
Make this for all products
Maybe I must use a second SELECT FROM offer with GROUP BY and ORDER BY but how do I get I the product_id from the first (outer) select?
Well I would start by getting the latest date for each product offer like this:
SELECT product_id, MAX(created) AS latestOffer
FROM offer
GROUP BY product_id;
Once you have that, you can join it to the original table to get that offer:
SELECT o.*
FROM offer o
JOIN(
SELECT product_id, MAX(created) AS latestOffer
FROM offer
GROUP BY product_id) tmp ON tmp.product_id = o.product_id AND tmp.latestOffer = o.created;
Here is an SQL Fiddle example.
This query should help you:
SELECT *
FROM product
JOIN (
SELECT product_id, min(price) as minPrice, max(created) as newestOffer
FROM offer
WHERE id NOT IN (SELECT offer_id FROM invalids)
GROUP BY 1
) as b
ON product.id = b.product_id
A shot in the dark based on what I understand you to be after...
lots of nested subqueries.. keep thinking there's got to be a better way...
SELECT OO.ID, OO.Price, OO.Dealer_Id, OO.Product_ID, OO.created, P.name
FROM Offer OO
INNER JOIN (
SELECT Min(Price) as MinP
FROM offer O
INNER JOIN (
SELECT max(OI.created) as LatestOffer, OI.Dealer_ID, OI.Product_ID
FROM Offer OI
LEFT JOIN invalids I
on OI.Id = I.offer_Id
WHERE I.ID is null
GROUP BY OI.Dealer_Id, OI.Product_Id
) B
on O.Dealer_Id = B.Dealer_Id
and O.Product_Id = B.Product_Id
and O.Created = B.LatestOffer
) Z
on OO.Price = Z.MinP
INNER JOIN product P
on P.ID = OO.Product_ID
SQL FIDDLE