It's possible left join only one row without sub query?
I need to get product statistics and some of products have multiple groups.
Therefore, the amount of products is incorrect.
SELECT COUNT(p.id) AS total_product, SUM(p.price) AS total_price
FROM product p
LEFT JOIN attribute_group a ON
a.product_id = p.id
WHERE p.created_at >= "2018-01-01" AND (a.id = 1 OR a.id = 2)
GROUP BY p.id
LIMIT 0, 30;
product
id | price
1 | 100
2 | 150
3 | 250
attribute_group
id | product_id | title
1 | 1 | a1
2 | 1 | a2
3 | 2 | a3
4 | 3 | a4
Should be:
1| 100
But i get:
2 | 200
You appear to want all products or the counts/sum of them that have attributes of both 1 and 2. Here is one method:
SELECT COUNT(*) as num_products, SUM(p.price) as total_price
FROM product p
WHERE p.created_at >= '2018-01-01' AND
EXISTS (SELECT 1
FROM attribute_group ag
WHERE ag.product_id = p.id AND ag.id = 1
) AND
EXISTS (SELECT 1
FROM attribute_group ag
WHERE ag.product_id = p.id AND ag.id = 2
);
Related
So I created a sql fiddle to explain my problem much clearer:
http://sqlfiddle.com/#!9/f35416
As you can see I have 3 tables and 1 of them links the 2 others.
table name: tags
---------------------
id | tag | value
---------------------
1 | color | green
2 | color | yellow
3 | color | red
4 | category | dress
5 | category | car
table name: product_tags_link
---------------------
product_id | tag_id
---------------------
1 | 1
1 | 5
2 | 1
3 | 2
3 | 5
4 | 4
5 | 4
5 | 1
table name: products
---------------------
id | name
---------------------
1 | green car
2 | yellow only
3 | yellow car
4 | dress only
5 | green dress
How can I make it so If I can get whatever product that have a "color" "green" and "category" "car"?
I tried doing:
select `ptl`.`product_id`
from `product_tags_link` as `ptl`
inner join `tags` on `tags`.`id` = `ptl`.`tag_id`
where ((`tags`.`tag` = "green") or (`tags`.`value` = "car"))
but it will return other product that is green OR car. changing it to and will not return anything as well.
I'm hoping to receive is product_id: 1 which have both color:green and category:car
Join all 3 tables, group by product and set the condition in the HAVING clause:
select p.id, p.name
from products p
inner join product_tags_link l on l.product_id = p.id
inner join tags t on t.id = l.tag_id
where (t.tag = 'category' and t.value = 'car')
or (t.tag = 'color' and t.value = 'green')
group by p.id, p.name
having count(distinct t.tag) = 2
Or:
select p.id, p.name
from products p
inner join product_tags_link l on l.product_id = p.id
inner join tags t on t.id = l.tag_id
where (t.tag, t.value) in (('category', 'car'), ('color', 'green'))
group by p.id, p.name
having count(distinct t.tag) = 2
See the demo.
Results:
> id | name
> -: | :---
> 1 | test
I would omit the joining table and do a simple join as follows:
SELECT
p.id AS product_id
FROM
products p
LEFT JOIN
tags t ON p.id = t.id
WHERE
t.value = 'green'
AND p.name LIKE '%car%'
I would like to make an SQL call where i count the rows on the joined call for each row
SELECT *, COUNT(id_feed.id) as price_count, prices.id as id FROM prices
LEFT JOIN id_feed ON id_feed.id_prices = prices.id
WHERE prices.id_user = :id_user
prices table:
id name id_user
1 name1 21
2 name2 21
3 name3 4
id_feed table:
id id_prices price
1 1 30
2 1 30
3 1 30
4 2 30
5 2 30
6 3 30
Result
id id_prices price_count
1 name1 3
2 name2 2
With a join and aggregation:
select p.id, p.name, count(*) price_count
from prices p inner join id_feed i
on i.id_prices = p.id
where p.id_user = :id_user
group by p.id, p.name
See the demo.
Results for id_user = 21:
> id | name | price_count
> -: | :---- | ----------:
> 1 | name1 | 3
> 2 | name2 | 2
You are describing window functions. Something like this:
SELECT p.*, f.*, COUNT(f.id) OVER () as price_count
FROM prices p LEFT JOIN
id_feed f
ON f.id_prices = p.id
WHERE p.id_user = :id_user
You seems to want window function :
SELECT prices.*, prices.id as id ,
count(*) over (partition by id_prices) as price_count
FROM prices LEFT JOIN
id_feed
ON id_feed.id_prices = prices.id
WHERE prices.id_user = :id_user
I'm not sure I phrased the question correctly, so feel free to correct me. Here are the tables with their data:
product category category_product
------- -------- ----------------
id_product id_category active id_category id_product
1 1 1 1 1
2 2 1 2 1
3 3 0 1 2
4 0 2 2
3 2
3 3
4 3
I need to select only those products, which have all categories as inactive.
For example:
Product 1 is good, since it belongs to active categories (1, 2).
Product 2 is good, since it has at least one active category (1, 2; 3 - inactive)
Product 3 must be selected, since all its categories are inactive (3, 4).
I have the following query, which is obviously incorrect, since it selects both products: 2 and 3:
SELECT p.id_product
FROM product p
JOIN category_product cp
ON p.id_product = cp.id_product
JOIN category c
ON c.id_category = cp.id_category
WHERE
c.active = 0;
Here is the SQL Fiddle: http://sqlfiddle.com/#!2/909dd/2/0
How can I solve this?
This way you can select product without active category.
SELECT p.id_product
FROM product p
WHERE NOT EXISTS
(SELECT * FROM
category_product cp
INNER JOIN category c ON c.id_category = cp.id_category
WHERE p.id_product = cp.id_product AND c.active = 1);
SQL Fiddle
Consider the following:
SELECT p.*
, COUNT(*)
, SUM(c.active = 1) active
, SUM(c.active = 0) inactive
FROM product p
JOIN category_product cp
ON cp.id_product = p.id_product
JOIN category c
ON c.id_category = cp.id_category
GROUP
BY p.id_product;
+------------+----------+--------+----------+
| id_product | COUNT(*) | active | inactive |
+------------+----------+--------+----------+
| 1 | 2 | 2 | 0 |
| 2 | 3 | 2 | 1 |
| 3 | 2 | 0 | 2 |
+------------+----------+--------+----------+
http://sqlfiddle.com/#!2/909dd/55
The last part of this problem has been left as an exercise for the reader
This is what i get while trying...apologies if anything is wrong
set #count:=0;
select a.id_product,a.times from
(SELECT count(p.id_product)times, p.id_product, c.active,
if(c.active!=0, #count:=#count+1, #count:=0) x
From category_product cp
join product p
on (p.id_product = cp.id_product)
join category c
on(c.id_category = cp.id_category )
group by id_product )a
where a.x=0;
I got a "Empresas" table
dbo.empresas
id | name | delegacion_id
-------------------------
1 | A | 3
2 | B | 3
3 | C | 3
4 | D | 4
a "pagos" table
dbo.pagos
id | id_empresa | monto | periodo
----------------------------------
1 | 1 | 120 | 2012-11-01
2 | 1 | 125 | 2012-12-01
3 | 2 | 150 | 2012-11-01
4 | 1 | 200 | 2013-01-01
5 | 2 | 151 | 2012-12-01
I have a value X that is a percentage.
I need to show the "empresas" that, comparing the "montos" of their two last "pagos" (ordered by periodo), have changed at least +X% or -X%, from an especific id_delegacion
For example, if we run this query with these example values, considering
X = 10
id_delegacion = 3
the output expected will be:
name | periodo | monto
---------------------------
A | 2012-12-01 | 125
A | 2013-01-01 | 200
empresa A is from delegacion_id = 3, and the comparison between the last two pagos, ordered by periodo desc (200 => 125) is bigger than 10%.
B is not showed because the comparison is smaller than 10%.
C is not showed because has no row in "pagos" table
D is from another delegation.
How can I get this desired output? For the record, using MySQL 5.5.8.
What I've done
I got this
select P.id_empresa, max(periodo) as periodo from
pagos P
where id_empresa in(
select e.id
from empresa E
where E.id_delegacion = 3
)
group by p.id_empresa, p.periodo
having count(*) > 1
with these I got the "empresas" that have more than one "pago" row, and got id_delegation = 3.
Also get the first period (the maximum), but I don't know how to get the second for each empresa, and compare them.
thanks
This is my query:
SELECT
empresas.name,
pagos.periodo,
pagos.monto
FROM
pagos INNER JOIN (
SELECT
lst.id id1,
prc.id id2
FROM (
SELECT
p1.id_empresa,
MAX(p1.periodo) last_p,
MAX(p2.periodo) prec_p
FROM
pagos p1 INNER JOIN pagos p2
ON p1.id_empresa = p2.id_empresa
AND p2.periodo < p1.periodo
GROUP BY
id_empresa) latest
INNER JOIN
pagos lst ON lst.id_empresa = latest.id_empresa AND lst.periodo=latest.last_p
INNER JOIN
pagos prc ON prc.id_empresa = latest.id_empresa AND prc.periodo=latest.prec_p
WHERE
lst.monto > prc.monto * 1.1) ids
ON pagos.id IN (ids.id1, ids.id2)
INNER JOIN
empresas
ON pagos.id_empresa = empresas.id
WHERE
delegacion_id=3
I think it can be simplified if you want to have values on the same row, e.g.
name | ultimo_periodo | ultimo_monto | anterior_periodo | anterior_monto
Please see fiddle here.
I still wondering if it can be simplified a little, but I am not sure if it is. Here's another solution:
SELECT
empresas.name,
pagos.periodo,
pagos.monto
FROM
pagos INNER JOIN empresas
ON pagos.id_empresa = empresas.id
INNER JOIN (
SELECT
id_empresa,
MAX(CASE WHEN row=1 THEN monto END) lst_monto,
MAX(CASE WHEN row=2 THEN monto END) prc_monto,
MAX(id) id1, MIN(id) id2
FROM (
SELECT
p1.*, COUNT(*) row
FROM
pagos p1 INNER JOIN pagos p2
ON p1.id_empresa = p2.id_empresa
AND p1.periodo <= p2.periodo
INNER JOIN empresas
ON p1.id_empresa = empresas.id
WHERE
empresas.delegacion_id = 3
GROUP BY
p1.id, p1.id_empresa, p1.monto, p1.periodo
HAVING
COUNT(*)<=2
ORDER BY
p1.id_empresa, p1.periodo desc
) s
GROUP BY
id_empresa
HAVING
lst_monto>prc_monto*1.1
) l ON pagos.id IN (l.id1, l.id2)
Please see fiddle here.
I need to join tables to do aggregation. I suck at that. Here's my scenario:
CATEGORIES
CatID | CategoryName | Parent
1 | Cars | NULL
2 | Electronics | NULL
3 | DVD | 2
4 | Blu_ray | 2
5 | Shoes | NULL
So basically, topmost elements don't have parents. Then I have
PRODUCTS
ProdID | Prod Name | CatID
1 | DVD Player 1 | 3
2 | Blu-Ray Player | 3
3 | Nike | 5
4 | DVD Player 2 | 3
I want to end up with...
CATEGORIES
CatID | CategoryName | Parent | totalProds
1 | Cars | NULL | 0
2 | Electronics | NULL | 0
3 | DVD | 2 | 2
4 | Blu_ray | 2 | 1
5 | Shoes | NULL | 1
Any ideas?
Aren't you just asking for the total number of products per category?
SELECT CatID, CategoryName, Parent, COUNT(*) totalProds
FROM categories c
INNER JOIN products p ON p.CatID = c.CatID
GROUP BY CatId
See below query
SELECT
c.`CatID`,
c.`CategoryName`,
c.`Parent`,
COALESCE(COUNT(DISTINCT p.`ProdID`),0) AS totalProds
FROM `CATEGORIES` c
LEFT JOIN `PRODUCTS` p
ON p.`CatID` = c.`CatID`
ORDER BY c.`CatID`
LEFT JOIN to return a row for every category. COALESCE to just make sure a 0 is returned if appropriate.
Assuming you have a limited number of category levels, you can union each level at a time. Here's for 4 levels:
SELECT c1.CatID, c1.CategoryName, c1.Parent, COUNT(1) totalProds
FROM products p
INNER JOIN categories c1 ON c1.CatID = p.CatID
GROUP BY c1.CatID, c1.CategoryName, c1.Parent
UNION
SELECT c2.CatID, c2.CategoryName, c2.Parent, COUNT(1)
FROM products p
INNER JOIN categories c1 ON c1.CatID = p.CatID
INNER JOIN categories c2 ON c2.CatID = c1.Parent
GROUP BY c2.CatID, c2.CategoryName, c2.Parent
UNION
SELECT c3.CatID, c3.CategoryName, c3.Parent, COUNT(1)
FROM products p
INNER JOIN categories c1 ON c1.CatID = p.CatID
INNER JOIN categories c2 ON c2.CatID = c1.Parent
INNER JOIN categories c3 ON c3.CatID = c2.Parent
GROUP BY c3.CatID, c3.CategoryName, c3.Parent
UNION
SELECT c4.CatID, c4.CategoryName, c4.Parent, COUNT(1)
FROM products p
INNER JOIN categories c1 ON c1.CatID = p.CatID
INNER JOIN categories c2 ON c2.CatID = c1.Parent
INNER JOIN categories c3 ON c3.CatID = c2.Parent
INNER JOIN categories c4 ON c4.CatID = c3.Parent
GROUP BY c4.CatID, c4.CategoryName, c4.Parent
Hope you get the idea...