Creating duplicate rankings with window function in mysql - mysql

I have a table like below:
product
country
group
value
p1
c1
g1
5
p1
c1
g2
6
p1
c2
g1
3
p1
c2
g2
22
p1
c3
g1
1
p1
c3
g2
6
I want to rank them considering the sum in value column for every product-country combination. So in this case, the updated table should be like this:
product
country
group
value
rank
p1
c1
g1
5
2
p1
c1
g2
6
2
p1
c2
g1
3
1
p1
c2
g2
22
1
p1
c3
g1
1
3
p1
c3
g2
6
3
p1-c1 combination will have the 2nd seed because 5+6 in the value column is higher than 7 (1+6) and lower than 25 (22+3). I used dense_rank() over (partition by product, country order by value) but it didn't work. How can I create the above ranking using mysql?
Thanks,

First use SUM() window function in a subquery to get the total value for each product, country combination and then DENSE_RANK() to rank the totals:
select product, country, `group`, value,
dense_rank() over (order by total desc) rnk
from (
select *, sum(value) over (partition by product, country) total
from tablename
) t
See the demo.

Related

Query to display top product for each category in MySQL

I have the following table:
category product sale
C1 P1 10
C2 P2 12
C3 P1 14
C1 P2 15
C1 P1 16
C2 P2 10
C2 P3 22
C3 P1 01
C3 P2 27
C3 P3 02
I want to group by category and get sum of sales and moreover the top product in each category. (Top product is actually the maximum occurrence of product in products, not via sales)
Expected Output:
category Top product Total sales
C1 P1 38
C2 P2 44
C3 P1 44
I have written the following MySQL query, but I get error (Invalid column name for products):
SELECT SUM(product) AS pro FROM
(SELECT category, count(sale)
FROM `table`
GROUP BY `table`.`category`
) as t2
ORDER BY pro DESC LIMIT 1
I am struggling to write the correct MySQL query, can anyone please point out the issue?
To get total sales per category along with top product you can use correlated/dependent sub query as
select t.category,
sum(t.sale) sales,
(select product
from demo
where category = t.category
group by product
order by count(*) desc
limit 1) top_product
from demo t
group by t.category
Demo
MySQL only solution, so don't use this when you need database portability in your code.
Using GROUP_CONCAT in combination with nested SUBSTRING_INDEX functions to get the first element/item can also be used to get a extra column which keeping the correct grouped information..
Note: SET SESSION group_concat_max_len = ##max_allowed_packet; is very important to use otherwise GROUP_CONCAT will only display 1024 bytes..
Query
SET SESSION group_concat_max_len = ##max_allowed_packet;
SELECT
Table1.category
, SUM(Table1.sale) sum_per_category
, SUBSTRING_INDEX(
SUBSTRING_INDEX(
GROUP_CONCAT(product ORDER BY sale DESC)
, ','
, 1
)
, ','
, -1
) AS most_sold_product_in_category
FROM
Table1
GROUP BY
Table1.category
Result
| category | sum_per_category | most_sold_product_in_category |
|----------|------------------|-------------------------------|
| C1 | 26 | P3 |
| C2 | 12 | P2 |
| C3 | 14 | P1 |
| C4 | 15 | P2 |
see demo http://sqlfiddle.com/#!9/0eb073/13
You can use Correlated Subquery to get the Top product (having highest sale) in a category. Try the following:
SELECT
t.category,
SUM(t.sale) AS total_sales,
(SELECT t2.product
FROM `table` AS t2
WHERE t2.category = t.category
ORDER BY t2.sale DESC LIMIT 1) AS top_product
FROM
`table` AS t
GROUP BY t.category
DB Fiddle DEMO
This may work:
SELECT t2.category, t2.sales,(select product
from `table`
where sale=(select max(b.sale)
from `table` b
where b.category=t2.category) limit 1)
FROM
(SELECT category, sum(sale) as sales
FROM `table`
GROUP BY `table`.`category`
) as t2
Select q.*,SUM(sale) from
(SELECT *
FROM `categories` order by sale desc limit 50
) as q group by q.category

How to optimize a my sql query taking 2 mins and 25 secs to run

I have a table with 3 GB data (It will keep on increasing) and I need to display total sales, top category and top product (Maximum occurrence in the column).
Following is the query that's giving me the above mentioned result:
select t.category,
sum(t.sale) sales,
(select product
from demo
where category = t.category
group by product
order by count(*) desc
limit 1) top_product
from demo t
group by t.category
The above query takes approximately 2 mins and 25 seconds. I couldn't find any way to optimize it. Is there any other way that someone could recommend?
Example table:
category product sale
C1 P1 10
C2 P2 12
C3 P1 14
C1 P2 15
C1 P1 02
C2 P2 10
C2 P3 22
C3 P1 01
C3 P2 27
C3 P3 02
Output:
category Top product Total sales
C1 P1 27
C2 P2 44
C3 P1 44
Your query could be written like this:
SELECT g1.category, g1.sum_sale, g2.product
FROM (
SELECT category, SUM(sale) AS sum_sale
FROM demo
GROUP BY category
) AS g1
INNER JOIN (
SELECT category, product, COUNT(*) AS product_count
FROM demo
GROUP BY category, product
) AS g2 ON g1.category = g2.category
INNER JOIN (
SELECT category, MAX(product_count) AS product_count_max
FROM (
SELECT category, product, COUNT(*) AS product_count
FROM demo
GROUP BY category, product
) AS x
GROUP BY category
) AS g3 ON g2.category = g3.category AND g2.product_count = g3.product_count_max
Basically it tries to find the maximum count(*) per category and from that it calculates the product. It could benefit from appropriate indexes.
A MySQL only hack solution is using GROUP_CONCAT in combination with nested SUBSTRING_INDEX functions to get the first element in an Ordered comma separated string.
It is not an ideal approach; but it will reduce the number of subqueries required, and may be efficient for your peculiar case.
You will also need to use SET SESSION group_concat_max_len = ##max_allowed_packet;.
We basically determine sales and count of occurrence, for a product and category combination. This result-set is then used as a Derived Table, and we use the Group_concat() hack to determine the product with maximum count in a category.
SET SESSION group_concat_max_len = ##max_allowed_packet;
SELECT
dt.category,
SUM(dt.sale_per_category_product) AS total_sales,
SUBSTRING_INDEX(
SUBSTRING_INDEX(
GROUP_CONCAT(dt.product ORDER BY dt.product_count_per_category DESC)
, ','
, 1
)
, ','
, -1
) AS top_product
FROM
(
SELECT
category,
product,
SUM(sale) AS sale_per_category_product,
COUNT(*) AS product_count_per_category
FROM demo
GROUP BY category, product
) AS dt
GROUP BY dt.category
Schema (MySQL v5.7)
| category | total_sales | top_product |
| -------- | ----------- | ------------|
| C1 | 27 | P1 |
| C2 | 44 | P2 |
| C3 | 44 | P1 |
View on DB Fiddle

How to produce "n" rows output from a query when a column value is "n"

Consider an Order Table:
OrderId Item Qty
O1 A1 5
O2 A2 1
O3 A3 3
Can this data be "exploded" into single-unit records like:
OrderId itemId Qty
O1 A1 1
O1 A1 1
O1 A1 1
O1 A1 1
O1 A1 1
O2 A2 1
O3 A3 1
O3 A3 1
O3 A3 1
The best solution here is to use GROUP_CONCAT and CONCAT_WS (concat with separator):
SELECT CONCAT('Order Id Item Qty ',
GROUP_CONCAT(
CONCAT_WS(' ',OrderId,Item,Qty)
SEPARATOR ' '
)
)
FROM Order;
Let me know if that works or if it needs some refinement!

SQL, add column with summed values of the same type

My current table looks like this:
ID TYPE QUANTITY
1 A1 3
2 B1 2
3 A1 2
4 B1 8
And after doing the query I want to get that:
ID TYPE QUANTITY SUM
1 A1 3 5
2 B1 2 10
3 A1 2 5
4 B1 8 10
The SUM column consist of summed quantities of items with the same type.
My approach is to use a derived table which aggregates the quantity by type first and then join this result with the original data:
select
t.id,
t.type,
t.quantity,
tmp.overall
from
table t join (
select
table.type,
sum(table.quantity) as overall
from
table
group by
table.type
) tmp on t.type = tmp.type
SELECT t.ID,t.TYPE,t.QUANTITY,x.SUM FROM TABLE t LEFT JOIN
(SELECT ID,TYPE,QUANTITY,SUM(QUANTITY) AS SUM FROM TABLE GROUP BY TYPE)x
ON t.type=x.type
SQL Fiddle
I haven't tried the query but see what happens if you do this:
SELECT
ID,
myTable.TYPE,
QUANTITY,
aaa.summy
FROM myTable
JOIN
(
SELECT
TYPE,
SUM(QUANTITY) summy
FROM myTable
GROUP BY TYPE
) aaa
ON aaa.TYPE = myTable.TYPE

Get Data from Table with Column as Rows in MYSQL

Can any one help me with a query that converts column into rows and then find data accordingly.
The question is below. I have tried a query that gets the column names into rows but i was unable to get values or had any idea to figure it out for column3 and column 4. I am a new bibe in SQL. Can anyone help me out here. I heard from a friend suggesting pivot and when i browse i found PIVOT is not supported in MYSQL
Input Table:
E1 E2 E3 E4 E5 E6
Null 1234 234 12 A B
123 Null Null Null 12 B
Null Null Null Null Null Null
123 2 1 A 1 2
Output Table:
C1 C2 Count TotalC percent
E1 123 2 2 1
E2 1234 1 2 0.5
E2 2 1 2 0.5
E3 234 1 2 0.5
E3 1 1 2 0.5
E4 12 1 2 0.5
E4 A 1 2 0.5
E5 A 1 3 0.3
E5 12 1 3 0.3
E5 1 1 3 0.3
E6 B 2 3 0.6
E6 2 1 3 0.3
Explanation of the Question:
C1 is the Column names of input table.
C2 is the data in those columns.
Count is the number of similar items and singles in each columns.
TotalC is the number of total not null values.
percent is the count/totalc.
Query i worked : SELECT (COLUMN_NAME)AS Column1 FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = 'inputtable';
Note: query in MYSQL.
As mentioned in my comment above, you should probably handle this problem differently. See What is the XY problem?
However, if you must, you can solve this problem using UNION:
SELECT *, Count/TotalC AS percent
FROM (
SELECT 'E1' AS C1, E1 AS C2, COUNT(*) AS Count
FROM Input
WHERE E1 IS NOT NULL
GROUP BY C2
UNION ALL
SELECT 'E2' AS C1, E2 AS C2, COUNT(*) AS Count
FROM Input
WHERE E2 IS NOT NULL
GROUP BY C2
UNION ALL
SELECT 'E3' AS C1, E3 AS C2, COUNT(*) AS Count
FROM Input
WHERE E3 IS NOT NULL
GROUP BY C2
UNION ALL
SELECT 'E4' AS C1, E4 AS C2, COUNT(*) AS Count
FROM Input
WHERE E4 IS NOT NULL
GROUP BY C2
UNION ALL
SELECT 'E5' AS C1, E5 AS C2, COUNT(*) AS Count
FROM Input
WHERE E5 IS NOT NULL
GROUP BY C2
UNION ALL
SELECT 'E6' AS C1, E6 AS C2, COUNT(*) AS Count
FROM Input
WHERE E6 IS NOT NULL
GROUP BY C2
) t1 NATURAL JOIN (
SELECT 'E1' AS C1, COUNT(E1) AS TotalC FROM Input
UNION ALL
SELECT 'E2' AS C1, COUNT(E2) AS TotalC FROM Input
UNION ALL
SELECT 'E3' AS C1, COUNT(E3) AS TotalC FROM Input
UNION ALL
SELECT 'E4' AS C1, COUNT(E4) AS TotalC FROM Input
UNION ALL
SELECT 'E5' AS C1, COUNT(E5) AS TotalC FROM Input
UNION ALL
SELECT 'E6' AS C1, COUNT(E6) AS TotalC FROM Input
) t2
See it on sqlfiddle.