MYSQL dynamic query from rows to columns - mysql

I have a table
or_id
emp_id
cs
val
100
1
x
3.4
100
1
x
4.5
100
1
y
5
100
1
y
6
200
2
a
12
200
2
b
11
200
2
c
14
I want my output table like:
or_id
emp_id
CS1
CS2
CS3
100
1
x
y
200
2
a
b
c
I tried every possible code but nothing seems to work. I want dynamic code for this.
This query is working, but for larger dataset the execution time is lengthy, so I need an optimized code.
select distinct or_id,emp_id,
(select cs from (
select distinct cost_center from orl where emp_id=m.emp_id) a limit 1 offset 0 ) cs1,
(select cost_center from (
select distinct cost_center from orl where emp_id=m.emp_id) a limit 1 offset 1 ) cs2,
(select cost_center from (
select distinct cost_center from orl where emp_id=m.emp_id) a limit 1 offset 2 ) cs3
from orl m

For three CS columns, in MYSQL8
WITH
sorted AS
(
SELECT
or_id,
emp_id,
cs,
ROW_NUMBER() OVER (PARTITION BY or_id, emp_id ORDER BY cs) AS ordinal
FROM
your_table
GROUP BY
or_id,
emp_id,
cs
)
SELECT
or_id,
emp_id,
MAX(CASE WHEN ordinal = 1 THEN cs END) AS cs1,
MAX(CASE WHEN ordinal = 2 THEN cs END) AS cs2,
MAX(CASE WHEN ordinal = 3 THEN cs END) AS cs3
FROM
sorted
GROUP BY
or_id,
emp_id
Demo: https://dbfiddle.uk/2QNiXt6O

Related

Aggregate information from one table to another with a different “layout” (mysql)

this is my starting table which provides sales information by Id.
Id
Store_Name
Market
Sales
Main_Product
1
StoreA
Rome
10
a
2
StoreB
Rome
15
b
3
StoreC
Rome
9
c
4
Mag1
Paris
10
a
5
Mag2
Paris
23
b
6
Mag3
Paris
12
c
7
Shop1
London
11
a
8
Shop2
London
31
b
9
Shop3
London
45
c
10
Shop4
London
63
d
In order to build a report and create some dynamic sentences, I will need the dataset to be "paginated" as per below table:
Id
Dimension
Dimension_Name
Sales
Main_Product
1
ShoppingCentre
StoreA
10
a
1
Market
Rome
34
a
2
ShoppingCentre
StoreB
15
b
2
Maket
Rome
34
b
3
ShoppingCentre
StoreC
9
c
3
Market
Rome
34
c
Do you have any tip about how to build the last table starting from the first one?
To sum-up:
The new table will be always by Id
Aggregation of market sales happens at row level where every single shopping centre is located
This is the query that I have built so far but wondering if there is a better and more efficient way to accomplish the same:
with store_temp_table as (
select
id
,Store_Name
,Market
, Main_Product
, sum(Sales) as Sales
from Production_Table
where 1=1
group by
1,2,3,4
)
, market_temp_table as (
select
market
, sum(Sales) as Sales
from Production_Table
where 1=1
group by
1
)
, store_temp_table_refined as(
Select
a.id
,a.Main_Product
, 'ShoppingCentre' as Dimension_Name
,SUM(a.Sales) as Sales
FROM store_temp_table a INNER JOIN
market_temp_table b on a.market = b.market
group by
1,2,3
)
, market_temp_table_refined as (
Select
a.id
,a.Main_Product
, 'Market' as DimensionName
,SUM(b.Sales) as Sales
FROM store_temp_table a INNER JOIN
market_temp_table b on a.market = b.market
group by
1,2,3
)
select * from store_temp_table_refined
union all
select * from market_temp_table_refined
Thank you
Use a CTE that returns the dimensions that you want and cross join it to a query that returns the columns of the table and an additional column with the total sales of each market:
WITH Dimensions(id, Dimension) AS (VALUES
ROW(1, 'ShoppingCentre'),
ROW(2, 'Market')
)
SELECT p.Id,
d.Dimension,
CASE d.id WHEN 1 THEN p.Store_Name ELSE p.Market END Dimension_Name,
CASE d.id WHEN 1 THEN p.Sales ELSE p.MarketSales END Sales,
p.Main_Product
FROM Dimensions d
CROSS JOIN (SELECT *, SUM(Sales) OVER (PARTITION BY Market) AS MarketSales FROM Production_Table) p
ORDER BY p.id, d.id;
Or, with UNION ALL:
SELECT Id,
'ShoppingCentre' Dimension,
Store_Name Dimension_Name,
Sales,
Main_Product
FROM Production_Table
UNION ALL
SELECT Id,
'Market',
Market,
SUM(Sales) OVER (PARTITION BY Market),
Main_Product
FROM Production_Table
ORDER BY Id,
CASE Dimension WHEN 'ShoppingCentre' THEN 1 WHEN 'Market' THEN 2 END;
See the demo.

Grouping in mysql with lower value from other column

Need group by on id and group and max of tval and lower of group if tval has duplicate/repeating values. Appreciate your help.
id
timel
group
tval
3
0
a
1.184
3
0.5
a
2.173
3
1
a
1.065
3
3
a
1.143
3
6
a
3.222
3
9
a
1.851
3
12
a
3.222
4
0
a
1.184
4
0.5
a
2.173
4
1
a
1.065
4
3
a
1.143
4
0.7
a
2.322
4
9
a
1.851
4
12
a
1.345
Expected output
id
group
timel
tval
3
a
6
3.222
4
a
0.7
2.322
Here is the create and inserts query for those who are interested to try it out.
CREATE TABLE sample (id INT, timel DOUBLE, group VARCHAR(50), tval DOUBLE);
INSERT INTO sample VALUES
(3,0,'a',1.184),
(3,0.5,'a',2.173),
(3,1,'a',1.065),
(3,3,'a',1.143),
(3,6,'a',3.222),
(3,9,'a',1.851),
(3,12,'a',3.222),
(4,0,'a',1.184),
(4,0.5,'a',2.173),
(4,1,'a',1.065),
(4,3,'a',1.143),
(4,0.7,'a',2.322),
(4,9,'a',1.851),
(4,12,'a',1.345);
Use NOT EXISTS to filter the table, so that you get only the rows with the max tval for each combination of id and group and then agregate to get the min timel:
SELECT s1.id, s1.trt, MIN(timel) timel, s1.tval
FROM sample s1
WHERE NOT EXISTS (
SELECT 1
FROM sample s2
WHERE s2.id = s1.id AND s2.trt = s1.trt AND s2.tval > s1.tval
)
GROUP BY s1.id, s1.trt, s1.tval
If your version of MySql is 8.0+ you can do it with ROW_NUMBER() window function:
SELECT id, trt, timel, tval
FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY id, trt ORDER BY tval DESC, timel) rn
FROM sample
) t
WHERE rn = 1
See the demo.

Mysql query with conditional count and value assignment

I have this sample table with Id, Grades:
Id Grades
1 50
1 60
1 70
1 40
1 80
1 65
2 80
2 67
2 100
2 90
2 60
etc
What I would like to do here is step by step:
Group them like this:
Between Assigned
1-50 D
51-70 C
71-90 B
91-100 A
and if the count of these groups is >=2, then assign the specified value above for each group:
if count(1-50)>=2 then D
if count(51-70)>=2 then C
if count(71-90)>=2 then B
if count(91-100)>=2 then A
so the output will be A,B,C,D or "". grouped by Id.
Thank you.
Try this query:
SELECT a.id, COALESCE(MAX(CASE
WHEN a.rank='D' AND a.cnt>=2 THEN 'D'
WHEN a.rank='C' AND a.cnt>=2 THEN 'C'
WHEN a.rank='B' AND a.cnt>=2 THEN 'B'
WHEN a.rank='A' AND a.cnt>=2 THEN 'A'
ELSE NULL
END),'') AS `rank`
FROM (
SELECT t.id,r.`rank`,COUNT(*) AS cnt
FROM (
SELECT 1 AS low, 50 AS high, 'D' AS `rank`
UNION ALL
SELECT 51,70,'C'
UNION ALL
SELECT 71,90,'B'
UNION ALL
SELECT 91,100,'A') r
INNER JOIN t t
ON t.grades>=r.low
AND t.grades<=r.high
GROUP BY t.id,r.`rank`) a
GROUP BY a.id

MySql : How do I have Horizontal Union of results?

I am looking to generate the results into the horizontal union. I could able to combine together and generate the results. However, I need to show the results in the expected results format.
SQL
(SELECT
kpa_id AS 'KPA', SUM(weightage) AS 'Total'
FROM
pmm_question_details
WHERE
weightage NOT LIKE '%-%'
GROUP BY kpa_id) UNION (SELECT
kpa_id AS 'KPA', SUM(weightage_value) AS 'Acheived'
FROM
pmm_answer_details
WHERE
application_id = 2
AND archive_value = 'No'
GROUP BY kpa_id)
Actual Results
1 14
2 37
3 19
4 40
5 51
6 24
1 12
2 19
3 0
6 2
Expected Results
1 14 1 12
2 37 2 19
3 19 3 0
4 40 6 2
5 51
6 24
If we can assume pmm_question_Details will always have as many or more records than pmm_answer_details... then two subqueries and a left join should do the trick with a join on a uservariable rownum (RN)
SELECT A.KPA, A.Total, B.KPA, B.Acheived
FROM (SELECT kpa_id AS 'KPA'
, SUM(weightage) AS 'Total'
, #RN1 := #RN1 + 1 as RN
FROM pmm_question_details
CROSS JOIN (SELECT #RN1 :=0) r1
WHERE weightage NOT LIKE '%-%'
ORDER BY KPA
GROUP BY kpa_id) A
LEFT JOIN (SELECT kpa_id AS 'KPA'
, SUM(weightage_value) AS 'Acheived'
, #RN1 := #RN2 + 1 as RN
FROM pmm_answer_details
CROSS JOIN (SELECT #RN2 :=0) r2
WHERE application_id = 2
AND archive_value = 'No'
ORDER BY KPA
GROUP BY kpa_id) B
on A.RN = B.RN
ORDER BY A.KPA
Though I must admit I don't see why a rownumber is needed if you could just left join on the KPA_ID in the first place...
if this could be the Expected results... (and again assuming pmm_question has all the IDs which could be in pmm_answer... )
Expected Results
1 14 1 12
2 37 2 19
3 19 3 0
4 40
5 51
6 24 6 2
Then the query would just be...
SELECT A.KPA, A.Total, B.KPA, B.Acheived
FROM (SELECT kpa_id AS 'KPA', SUM(weightage) AS 'Total'
FROM pmm_question_details
WHERE weightage NOT LIKE '%-%'
GROUP BY kpa_id) A
LEFT JOIN (SELECT kpa_id AS 'KPA', SUM(weightage_value) AS 'Acheived'
FROM pmm_answer_details
WHERE application_id = 2
AND archive_value = 'No'
GROUP BY kpa_id) B
on A.KPA = B.KPA

How to fetch specific number of row on basis of score

Have to show on specific count . Means i have to show max score value 4 times then less than that score to 3 times and so on
I have table like:-
ID Score
1 1
2 1
3 1
4 1
5 2
6 2
7 2
8 3
9 3
10 4
11 4
12 4
and I am expecting output like:-
Score
1
1
1
2
2
3
4
Try this:
SELECT Score
FROM
(SELECT a.Score, a.ID, count(*) as RN FROM TableName a
JOIN TableName b ON a.Score = b.Score AND a.ID >= b.ID
GROUP BY a.Score, a.ID) T
WHERE RN>1
Result:
SCORE
1
1
1
2
2
3
4
4
An example in SQL Fiddle.
Explanation:
In the inner query, we are selecting a row number partitioned by ID and order by score. And then we select Score from the inner query whose Row number is greater than 1 (inorder to omit 1 record).
SELECT x.*
FROM my_table x
JOIN my_table y
ON y.marks = x.marks
AND y.id <= x.id
GROUP
BY x.marks
, x.id
HAVING COUNT(0) <= MAX(4-x.marks)
ORDER
BY marks DESC,id;