Order by on a group sum criteria - mysql

Is it possible to order return rows with a criteria on a sum group ?
For example, my data are :
Id Price Product Category
1 12 Book1 Car
2 1 Book2 Art
3 8 Book3 Car
4 7 Book4 Art
5 11 Book5 Car
6 24 Book6 Bridge
As the sum of Car books is 31, the sum of Art books is 8 and the sum of Bridge books is 24, I would like to have the following result (Car first, then Bridge and then Art):
Id Price Product Category
1 12 Book1 Car
3 8 Book3 Car
5 11 Book5 Car
6 24 Book6 Bridge
2 1 Book2 Art
4 7 Book4 Art
On the other hand, I would like to add other Order by criteria (in the example, "Product" criteria).
I have tried many things using ORDER BY and GROUP BY but it always aggregate my results.
Thanks for help !

You could do something like this:
select
l.* from table l
inner join (
select category, sum(price) as total from table group by category
) r
on l.category = r.category
order by r.total, <some_other_column>
This is the procedure I followed:
find the subtotals
join the original table to the subtotals
order the original table by those subtotals

Related

How to count how many having how many in mysql

I have persons i one table [person] and cars [car] registered to each person i another, connected by id.
I want to get a list of how many persons having how many cars.
Something like this:
PERSON
id
name
1
LISA
2
ADAM
3
RAY
CARS
id
id_person
brand
1
3
FORD
2
1
BMW
3
2
VOLVO
4
1
VOLVO
5
1
VW
RESULT
no cars
no persons
1
2
2
0
3
1
Just dont get how to do it?
You need to do it in two stages. The first is to get the number of cars per person -
SELECT p.id, COUNT(*) num_cars
FROM person p
JOIN cars c ON p.id = c.id_person
GROUP BY p.id
This can then be nested to count people per count -
SELECT num_cars, COUNT(*) num_people
FROM (
SELECT p.id, COUNT(*) num_cars
FROM person p
JOIN cars c ON p.id = c.id_person
GROUP BY p.id
) t
GROUP BY num_cars
ORDER BY num_cars ASC

How to select one column with all distinct values based on some clause

I essentially like to have one query which I'll execute one time and like to have the result (no multiple query execution) and definitely, the query should use simple MySQL structure (no complex/advanced structure to be used like BEGIN, loop, cursor).
Say I've two tables.
1st Table = Country (id(PK), name);
2nd Table = Businessman (id(PK), name, city, country_id(FK))
Like to SELECT all countries, whose businessmen are from distinct cities. No two businessmen exist in one country, who are from the same city. If so, that country will not be selected by the SELECT clause.
Country
id name
1 India
2 China
3 Bahrain
4 Finland
5 Germany
6 France
Businessman
id name city country_id
1 BM1 Kolkata 1
2 BM2 Delhi 1
3 BM3 Mumbai 1
4 BM4 Beijing 2
5 BM5 Paris 6
6 BM6 Beijing 2
7 BM7 Forssa 4
8 BM8 Anqing 2
9 BM9 Berlin 5
10 BM10 Riffa 3
11 BM11 Nice 6
12 BM12 Helsinki 4
13 BM13 Bremen 5
14 BM14 Wiesbaden 5
15 BM15 Angers 6
16 BM16 Sitra  3
17 BM17 Adliya 3
18 BM18 Caen 6
19 BM19 Jinjiang 2
20 BM20 Tubli 3
21 BM21 Duisburg 5
22 BM22 Helsinki 4
23 BM23 Kaarina 4
24 BM24 Bonn 5
25 BM25 Kemi 4
In this respect, China and Finland shouldn't be listed.
I've attempted using count and group by, but no luck.
Can you please help me to build up this query.
Here it is, all you need is to join Businessman table and count cities and distinct cities and if they equal that means all businessmen are from different cities:
SELECT
c.`id`,
c.`name`,
COUNT(b.`id`) AS BusinessmanCount,
COUNT(b.`city`) AS CityCount,
COUNT(DISTINCT b.`city`) AS DistinctCityCount
FROM `countries` c
INNER JOIN Businessman b ON c.`id` = b.`country_id`
GROUP BY c.`id`
HAVING CityCount = DistinctCityCount
For minified version what you exactly need:
SELECT
c.`id`,
c.`name`
FROM `countries` c
INNER JOIN Businessman b ON c.`id` = b.`country_id`
GROUP BY c.`id`
HAVING COUNT(b.`city`) = COUNT(DISTINCT b.`city`)
Well, I think we should have waited for you to show your own query, because one learns best from mistakes and their explanations. However, now that you've got answers already:
Yes, you need group by and count. I'd group by cities to see if I got duplicates. Then select countries and exclude those that have duplicate cities.
select *
from country
where id not in
(
select country_id
from businessmen
group by city, country_id
having count(*) > 1
);
You need either nested aggregations:
select *
from Country
where id in
(
select country_id
from
(
select city, country_id,
count(*) as cnt -- get the number of rows per country/city
from Businessman
group by city, country_id
) as dt
group by country_id
having max(cnt) = 1 -- return only those countries where all counts are unique
)
Or compare two counts:
select *
from Country
where id in
(
select country_id
from Businessman
group by country_id
having count(*) = count(distinct city) -- number of cities is equal to umber of rows
)

Count Category products with mysql

I have to count products inside each category like I have a category A that have two childs B and C.
In B category have 10 products and C category have 5 Products.
I want to write a query that count product like A = 15 B = 10 C = 5.
I have wrote a query that count only for child categories.
My query is given below
SELECT
c.`id`,
c.`parent_id`,
c.`name`,
c.`slug`,
c.`route_id`,
c.`description`,
c.`excerpt`,
c.`sequence`,
c.`image`,
c.`seo_title`,
c.`meta`,
c.`enabled`,
(SELECT
COUNT(`product_id`)
FROM
HM_category_products
WHERE
HM_category_products.category_id IN (SELECT
HM_categories.`id`
FROM
HM_categories
WHERE
(id = c.`id` OR id = c.`parent_id`))) AS count
FROM
`HM_categories` AS c
EDITED :- Below is my tables structure. This is just an example.
Category Table
id parent_id name slug enabled
1 0 Mobiles & Tablets mobiles-tablets 1
2 1 Mobile Phones mobile-phones 1
3 1 Tablets tablets 1
4 1 Mobile Accessories mobile-accessories 1
5 0 Electronics & Computers electronics-computers 1
6 5 Cameras & Accessories cameras-accessories 1
7 5 TV - Video - Audio tv-video-audio 1
Category Product Table
product_id category_id
1 2
2 2
3 2
4 2
5 3
6 2
7 3
8 3
9 3
10 2
11 3
12 2
13 2
14 2
15 2
You hav to use the "Group by" with something like:
SELECT
category_id, COUNT(*)
FROM
HM_category_products
GROUP BY category_id
This would get your table HM_category_products Grouped by category_id so will know how mane rows of each category its in the table, whats mean you will know the number of product of each category. Then you can join this resulting table with category one to get the category info. (Sorry for my english)
The GROUP BY statement is used in conjunction with the aggregate
functions to group the result-set by one or more columns. SQL GROUP BY Statement
for more info abaut "Group by" read
12.16.2 GROUP BY Modifiers and
12.16.1 GROUP BY (Aggregate) Functions
EDIT: I see now what you whant. I already give you the direction but here its a step furder: DISCLAIMER: THIS QUERY ITS JUST EXAMPLE, IT DOSNT WORK
CREATE VIEW view_2 AS
SELECT
category_id, COUNT(*) AS product_sum
FROM
HM_category_products
GROUP BY category_id
CREATE VIEW view_1 AS
SELECT
*
FROM
HM_categories
LEFT JOIN
view_2 AS a ON HM_categories.id = a.category_id;
SELECT
id,
name,
(SELECT
SUM(product_sum)
FROM
view_1
WHERE
parent_id = final.id) as product_count
FROM
HM_categories AS final;
that way you will get the product of a parent category, your only missing a CASE on the last select "product_count" to the child categorys, but that sum its easy because you already hav it on the view_1 (again sorry for my english)

Group by only for "limit and offset" in MySQL

Whilst trying to do pagination I've run into this problem.
My table-
ID CarBrand Car Model
---------------------------
1 Alfa Romeo Guilietta
2 Alfa Romeo Mito
3 Audi A3
4 Audi R8
5 Audi TT
6 Fiat Punto
7 Fiat Panda
8 Ford Mondeo
9 Ford Mustang
10 Nissan Almera
11 Nissan Note
12 Nissan Qashqai
13 Toyota Aygo
14 Toyota Prius
15 Volkswagen Beetle
16 Volkswagen Golf
17 Volkswagen Polo
18 Volkswagen Up
I have the data displayed like so, in groups of two:
-Fiat - Punto
Panda
-Ford - Mondeo
Mustang
So there are 2 brands but 4 database results.
Is it possible to have a query limit and offset my results to two brands while showing all the models for the brand?
Sorry if I'm not clear!
It is clear. Try this:
select * from t t1
join (
select distinct carBrand from t
limit 2
) s on t1.carBrand = s.carBrand
Before the limit 2 apply the ordering you want.
To get a limit, without using the limit keyword, you can impose a count.
For example, given the table definition
create table cars (id int,
carBrand char(10),
carModel char(10));
this will give you all the Car Models for the top 2 Car Brands
select cars.carBrand, cars.carModel
from cars
where ((select count(*) from
(select distinct carBrand from cars) as carBrands
where carBrands.carBrand < cars.carBrand) < 2)
order by cars.carBrand, cars.carModel;
This creates an inline table just listing the carBrands and then joins this back to cars to get the list of all cars that are in the top 2 brands. The count(*) .... < 2 enforces the limit. Consider 'Ford', for example, in your above data. In 'Ford''s case, there are 3 brands that are < 'Ford' alphabetically, so the count(*) above = 3. Since 3 is not less than 2, no 'Ford' cars appear in the output.
The output on your test data would be:
CARBRAND CARMODEL
Alfa Romeo Guilietta
Alfa Romeo Mito
Audi A3
Audi R8
Audi TT
Now, you didn't say how you wanted to pick the 2 brands -- you just listed Ford and Fiat in your example -- I don't know how you happened to pick those. If you want something other than alphabetical criteria for ordering, that's doable, but harder.
SQL Fiddle and results for all this: http://sqlfiddle.com/#!2/33a8f/3
It's a matter of database design. Maybe you should split your data into two tables model (model names) and brand (brand names). Then you can write a query like this:
SELECT m.name, b.name
FROM model m
INNER JOIN brand b
WHERE b.id IN (
SELECT id
FROM brand
ORDER BY name ASC
LIMIT 2 OFFSET 0
)
I did not test the code. No need for GROUP BY in my opinion.

Complicated Crosstab Query Question

I have the following 2 tables:
1) Companies
ID CompanyName Abbreviation Notes
1 CompanyA CA ...
2 CompanyB CB ...
3 CompanyC CC ...
2) PlannedDeployments
ID CompanyID TypeID DepDate NumDeployed
1 1 2 09/2010 5
2 1 2 10/2010 5
3 1 3 09/2010 3
4 1 3 10/2010 3
5 1 4 10/2010 4
6 2 2 12/2010 10
7 2 4 10/2010 1
8 3 2 11/2010 6
Note that TypeID is a number between 1 and 5 describing what type of person is being deployed. For the purposes of this query, I'm interested in Type2 employees for each company and then the sum of Types 3 & 4 for each date. What I eventually want to end up with is a crosstab that looks like the following:
Crosstab
Date/Company CompanyA CompanyB CompanyC SumOfTypes3and4
09/2010 5 3
10/2010 5 8
11/2010 6
12/2010 10
The problem is that final column - the sum of Type 3 and Type 4 employees. The current crosstab that I have includes everything except that sum column and looks like the following:
TRANSFORM Sum(PlannedDeployments.NumDeployed) AS ["NumDeployed"]
SELECT PlannedDeployments.DepDate
FROM PlannedDeployments LEFT JOIN Companies ON Companies.ID=PlannedDeployments.CompanyID
WHERE PlannedDeployments.TypeID=2 AND (PlannedDeployments.DepDate Between FormFieldValue("Form", "Control") AND FormFieldValue("Form", "Control"))
GROUP BY PlannedDeployments.DepDate
PIVOT Companies.CompanyName;
The second part of that WHERE clause is just limiting the data by some form controls. Anyway - I'm having a lot of trouble getting that final column. Anyone have any ideas?
Edit: Building on the solution provided by Remou below, here's what the final query ended up looking like:
TRANSFORM Sum(PlannedDeployments.NumDeployed) AS ["NumDeployed"]
SELECT PlannedDeployments.DepDate, q.SumOfNumDeployed
FROM (SELECT PlannedDeployments.DepDate, Sum(PlannedDeployments.NumDeployed) AS SumOfNumDeployed
FROM PlannedDeployments
WHERE (((PlannedDeployments.[TypeID]) In (3,4)))
GROUP BY PlannedDeployments.DepDate) AS q
RIGHT JOIN (PlannedDeployments
INNER JOIN Companies ON PlannedDeployments.CompanyID = Companies.ID)
ON q.DepDate = PlannedDeployments.DepDate
WHERE PlannedDeployments.TypeID=2
AND (PlannedDeployments.DepDate Between FormFieldValue("Form", "Control")
AND FormFieldValue("Form", "Control"))
GROUP BY PlannedDeployments.DepDate, q.SumOfNumDeployed
PIVOT Companies.CompanyName;
You can use a subquery:
TRANSFORM Sum(PlannedDeployments.NumDeployed) AS ["NumDeployed"]
SELECT PlannedDeployments.DepDate, Sum(q.SumOfNumDeployed) AS SumOfSumOfNumDeployed
FROM (SELECT PlannedDeployments.DepDate, Sum(PlannedDeployments.NumDeployed) AS SumOfNumDeployed
FROM PlannedDeployments
WHERE (((PlannedDeployments.[TypeID]) In (3,4)))
GROUP BY PlannedDeployments.DepDate) AS q
RIGHT JOIN (PlannedDeployments
INNER JOIN Companies ON PlannedDeployments.CompanyID = Companies.ID)
ON q.DepDate = PlannedDeployments.DepDate
WHERE PlannedDeployments.TypeID=2
AND (PlannedDeployments.DepDate Between FormFieldValue("Form", "Control")
AND FormFieldValue("Form", "Control"))
GROUP BY PlannedDeployments.DepDate
PIVOT Companies.CompanyName;