Grouping Categories - mysql

I am trying to group each category and count the total number of cities in each category.
The results should look like this
+------------------------------+--------------+
| GNPPopRatioCategory | CountRecords |
+------------------------------+--------------+
| 1. Equal or greater than 2% | 145 |
| 2. Equal or greater than 1% | 104 |
| 3. Equal or greater than .5% | 566 |
| 4. Rest of country | 3264 |
This is what I have so far however I cannot figure out how to group them and count the cities in each category. I have been informed that using an Inline view is more efficient however I would like to figure out this way first before I moved on to Inline Views. Thanks for the help.
Select Count(Country.GNP / City.Population) AS CountRecords,
(Select Case When CountRecords>= 2 THEN "1.Equal or greater than 2%"
When CountRecords>= 1 THEN "1.Equal or greater than 1%"
When CountRecords>= .5 THEN "1.Equal or greater than .5%"
ELSE "Rest of country" END) AS GNPPopRatioCategory
From City INNER JOIN Country ON City.Country=Country.Code
Limit 20;
City table described: ID, name, Country, District, Population
Country Table described: Code, Name, Continent, Region, SurfaceArea, IndepYear, Population, LifeExpectancy, GNP, LocalName, GovernmentForm, HeadOfState, Capital

Your query is structured in an unusual and incorrect way. You have a subquery with no from.
I think you want to take the rate of the GNP and Population and place that in categories, and then count the numbers in each category. This following query takes this approach:
Select Count(*) AS CountRecords,
(Case When Country.GNP / City.Population >= 2 THEN "1.Equal or greater than 2%"
When Country.GNP / City.Population >= 1 THEN "1.Equal or greater than 1%"
When Country.GNP / City.Population >= .5 THEN "1.Equal or greater than .5%"
ELSE "Rest of country"
END) AS GNPPopRatioCategory
From City INNER JOIN
Country
ON City.Country = Country.Code
group by (Case When Country.GNP / City.Population>= 2 THEN "1.Equal or greater than 2%"
When Country.GNP / City.Population>= 1 THEN "1.Equal or greater than 1%"
When Country.GNP / City.Population>= .5 THEN "1.Equal or greater than .5%"
ELSE "Rest of country"
END)
Limit 20;
Normally when you do a limit you want to have an order by. In this case, there are only four categories, so the limit is entirely unnecessary.

Related

Calculate the percentage using Over Partition By

I would like to added a calculate column that calculate the unique order numbers for 'apple' in percentage for each store. For example, store A has 4 orders in total, but 2 of them have the same order number. Therefore, the percentage of the 'apple' order for store a is 2/3 = 67%
store order no product
a abc apple
a bde orange
a abc apple
a feg apple
b lmn apple
b mno pear
c tsx apple
The desire output should be like this:
store product
c 100%
a 67%
b 50%
This is the query I tried:
count(distinct(case when product = 'apple' then order no end)
/
count(distinct order no) over(partition by store)
For some reason the query above doesn't return the right percentage for each store.
Any suggestions would be greatly appreciated.
SELECT store,
SUM(product = 'apple') / COUNT(*)
FROM ( SELECT DISTINCT *
FROM table ) t
GROUP BY 1
ORDER BY 2 DESC
Window functions not needed.

Respecting GROUP BY Clause in a MySQL SUB-QUERY

I have a query which returns some figures from a table of sales data. The table contains data for a number of different sales managers. I need to return data for the individual managers and also some calculated figures.
One of the figures I'm trying to get at involves a subquery. Getting the figures for each individual manager is fine and works well. The problem occurs when I am trying to get a figure which involves the use of a subquery. It seems that, though the outer query uses a group by clause to separate out individual salespeople, the subquery operates on the entire set.
Sample Data
name | Amount | Sell_at | Profit
--------------------------------
Fred | 1 | 3.99 | 0.99
Joe | 2 | 10.50 | 5.00
Fred | 5 | 20.00 | 15.00
Joe | 10 | 10.00 | 6.00
Desired result:
name | Total Profit | < 50% | > 50%
------------------------------------
Fred | 75.99 | 0.99 | 75.00
Joe | 71.00 | 60 | 10
SELECT
Account_Manager,
SUM(Profit * Amount) AS 'Total Profit'
(SELECT sum(Profit * Amount) from sales WHERE Profit * Amount / (Sell_at * Amount) < 0.5) AS '< 50%',
(SELECT sum(Profit * Amount) from sales WHERE Profit * Amount / (Sell_at * Amount) > 0.5) AS '> 50%'
FROM sales WHERE Invoice_Date = 'some date' GROUP BY Account_Manager
This gives me a row for each salesperson and their profit for that day, but the sub queries return figures totaled from the entire table. I could add a clause to the subquery WHERE in order to limit the result to the same date as the outer query, but ideally what I need to do really is to get the results for each individual salesperson.
Am I on the right track or is there another way I should be approaching this?
From your sample data and desired results it appears you only need a conditional aggregate:
select Name, sum(amount * profit) as TotalProfit,
Sum(case when Profit * Amount / (Sell_at * Amount) < 0.5 then Profit * Amount end) as '<50%',
Sum(case when Profit * Amount / (Sell_at * Amount) > 0.5 then Profit * Amount end) as '>50%'
from t
group by name
The expression:
Profit * Amount / (Sell_at * Amount)
is equivalent to just:
Profit / Sell_at
Use it in a CASE expression to perform conditional aggregation:
SELECT Account_Manager,
SUM(Amount * Profit) as TotalProfit,
SUM(CASE WHEN Profit / Sell_at < 0.5 THEN Profit * Amount END) `< 50%`,
SUM(CASE WHEN Profit / Sell_at > 0.5 THEN Profit * Amount END) `> 50%`
FROM Sales
WHERE Invoice_Date = 'some date'
GROUP BY Account_Manager;
You should also check for the case that Profit / Sell_at is equal to 0.5.
See the demo.
If you want to classify the rows based on their percentile, then use window functions. Let me assume that you want to know who is above and below average:
SELECT Account_Manager,
SUM(Profit * Amount) AS Total_Profit,
(CASE WHEN SUM(Profit * Amount) > AVG(SUM(Profit * Amount)) OVER ()
THEN 'Above average'
WHEN SUM(Profit * Amount) < AVG(SUM(Profit * Amount)) OVER ()
THEN 'Below average'
ELSE 'Average'
END) as relative_position
FROM sales
WHERE Invoice_Date = 'some date'
GROUP BY Account_Manager;

Refine SQL Query given list of ids

I am trying to improve this query given that it takes a while to run. The difficulty is that the data is coming from one large table and I need to aggregate a few things. First I need to define the ids that I want to get data for. Then I need to aggregate total sales. Then I need to find metrics for some individual sales. This is what the final table should look like:
ID | Product Type | % of Call Sales | % of In Person Sales | Avg Price | Avg Cost | Avg Discount
A | prod 1 | 50 | 25 | 10 | 7 | 1
A | prod 2 | 50 | 75 | 11 | 4 | 2
So % of Call Sales for each product and ID adds up to 100. The column sums to 100, not the row. Likewise for % of In Person Sales. I need to define the IDs separately because I need it to be Region Independent. Someone could make sales in Region A or Region B, but it does not matter. We want aggregate across Regions. By aggregating the subqueries and using a where clause to get the right ids, it should cut down on memory required.
IDs Query
select distinct ids from tableA as t where year>=2021 and team = 'Sales'
This should be a unique list of ids
Aggregate Call Sales and Person Sales
select ids
,sum(case when sale = 'call' then 1 else 0 end) as call_sales
,sum(case when sale = 'person' then 1 else 0 end) as person_sales
from tableA
where
ids in t.ids
group by ids
This will be as follows with the unique ids, but the total sales are from everything in that table, essentially ignoring the where clause from the first query.
ids| call_sales | person_sales
A | 100 | 50
B | 60 | 80
C | 100 | 200
Main Table as shown above
select ids
,prod_type
,cast(sum(case when sale = 'call' then 1 else 0 end)/CAST(call_sales AS DECIMAL(10, 2)) * 100 as DECIMAL(10,2)) as call_sales_percentage
,cast(sum(case when sale = 'person' then 1 else 0 end)/CAST(person_sales AS DECIMAL(10, 2)) * 100 as DECIMAL(10,2)) as person_sales_percentage
,mean(price) as price
,mean(cost) as cost
,mean(discount) as discount
from tableA as A
where
...conditions...
group by
...conditions...
You can combine the first two queries as:
select ids, sum( sale = 'call') as call_sales,
sum(sale = 'person') as person_sales
from tableA
where
ids in t.ids
group by ids
having sum(year >= 2021 and team = 'Sales') > 0;
I'm not exactly sure what the third is doing, but you can use the above as a CTE and just plug it in.

MySQL Where-clause with select

I have a table that has three columns: visitations, country and user_id. I have a query to retrieve amount of all visitors per country and re-visitors per country. Now, I would like to alter my query so, that I get both amounts and a ratio of re-visitors per country (re-visitors / all visitors). I'm just learning MySQL and I feel that I don't know the tools to select all visitors and re-visitors and then use them in the ratio. Is there a way to do this in one query? Could someone help me with this? Thanks!
Here is my query for all visitors (and re-visitors if the # is removed)
SELECT sum(Visitations) AS "Amount", country
FROM E91
#WHERE Visitations > 1
GROUP BY Country
ORDER BY `Amount` DESC
Sample of the data, user is a revisitor if visitations is above 1:
user_id | country | visitations
---------|--------------|---------------
beth123 | Germany | 4
david78 | USA | 2
matt23 | UK | 1
...
The where clause starts to filter records, leaving out records you do want to count for your total. To count the revisitors you can use CASE WHEN... END:
SELECT
sum(Visitations) AS "Amount",
sum(CASE WHEN Visitations > 1 THEN 1 ELSE 0 END) as "Re-Visitors",
country
FROM E91
GROUP BY Country
ORDER BY `Amount` DESC
For further use you could do something like this:
SELECT
sum(Visitations) AS "Amount",
sum(CASE WHEN Visitations > 1 THEN 1 ELSE 0 END) as "Re-Visitors",
sum(CASE WHEN Visitations > 1 THEN 1 ELSE 0 END) / sum(Visitations) as "X",
country
FROM E91
GROUP BY Country
ORDER BY `Amount` DESC
or:
SELECT Amount, Re-visitors, "Re-visitors"/Amount, country
FROM (
SELECT
sum(Visitations) AS "Amount",
sum(CASE WHEN Visitations > 1 THEN 1 ELSE 0 END) as "Re-Visitors",
country
FROM E91
GROUP BY Country
ORDER BY `Amount` DESC
) x
The amount of visitors and re-visitors has nothing to do with the sum of the column visitations.
You can get the number of visitors (all user_ids who visited a country) by counting the number of user_ids for each country and the number of re-visitors by counting the number of user_ids for each country when the column visitations is greater than 1:
SELECT country,
SUM(visitations > 1) AS revisitors,
COUNT(*) AS visitors,
AVG(visitations > 1) AS ratio
FROM E91
GROUP BY Country

Mysql Agregate function to select maximum and then select minimum price within that group

I am trying to get the maximum value out of a aggregate function, and then also get the min value out of a Price column which comes back in results.
id | discount | price
1 | 60 | 656
2 | 60 | 454
3 | 60 | 222
4 | 30 | 335
5 | 30 | 333
6 | 10 | 232
So in above table, I would like to separate Minimum Price vs Highest Discount.
This is the result I should be seeing:
id | discount | price
3 | 60 | 222
5 | 30 | 333
6 | 10 | 232
As you can see, its taken discount=60 group and separated the lowest price - 222, and the same for all other discount groups.
Could someone give me the SQL for this please, something like this -
SELECT MAX(discount) AS Maxdisc
, MIN(price) as MinPrice
,
FROM mytable
GROUP
BY discount
However, this doesnt separate the minimum price for each group. I think i need to join this table to itself to achieve that. Also, the table contains milions of rows, so the sql needs to be fast. One flat table.
This question is asked and answered with tedious regularity in SO. If only the algorithm was better at spotting duplicates. Anyway...
SELECT x.*
FROM my_table x
JOIN
( SELECT discount,MIN(price) min_price FROM my_table GROUP BY discount) y
ON y.discount = x.discount
AND y.min_price = x.price;
In your query, you cannot group by discount and then maximize the discount value.
This should get you the result you are looking for..
SELECT Max(ID) AS ID, discount, MIN(price) as MinPrice, FROM mytable GROUP BY discount
If you do not need the id, yo would do:
select discount, min(price) as minprice
from table t
group by discount;
If you want other columns in the row, you can either join back to the original table or use the substring_index()/group_concat() trick:
select substring_index(group_concat(id order by price), ',', 1) as id,
discount, min(price)
from table t
group by discount;
This will not always work because the intermediate result for group_concat() can overflow if there are too many matches within a column. This is controlled by a system parameter, which could be made bigger if necessary.