SQL column calculation mininimum value - mysql

I have a table named calcu
id date name s1 s2 s3 s4 min_value
1 10/10/2017 dicky 7 4 8 9 [4]
2 10/10/2017 acton 12 15 17 19 [15]
3 10/10/2017 adney 28 13 19 14 [13]
------------------------------------------------------------------
when total by date 47 32 44 42
Here as minimum column value is s2[32], that's why s2 value = min_value column.
check sqlfiddle here
Now there is no problem. But when any of the fields within s1, s2, s3, s4 value equals [see below example] with any field then min_value field doubles and all column doubles.
Example:
id date name s1 s2 s3 s4 min_value
1 10/10/2017 dicky 7 24 8 11 [8]/[11]
2 10/10/2017 acton 12 15 17 19 [17]/[19]
3 10/10/2017 adney 28 13 19 14 [19]/[14]
------------------------------------------------------------------
when total by date 47 52 44 44
Here minimum value columns are s3 ans s4,
I require any column within s3 or s4 it means either s3 or s4 column will be filled in the min_value column.
see the problem here with sqlfiddle
I am using MySQL.

Based on your sqlfiddle, you need to add a GROUP BY outside of the nested queries in order to achieve what you want.
select c.id, c.date, c.name, c.s1, c.s2, c.s3, c.s4,
case v.s
when 1 then c.s1
when 2 then c.s2
when 3 then c.s3
when 4 then c.s4
end as min_value
from calcu c
join (
select date, s, sum(val) val_sum
from ( #unpivot your data
select date, s1 as val, 1 as s
from calcu
union all
select date, s2 as val, 2 as s
from calcu
union all
select date, s3 as val, 3 as s
from calcu
union all
select date, s4 as val, 4 as s
from calcu
) x
group by date, s
) v on c.date = v.date
where not exists ( #we are only interested in the minimum val_sum above
select 1
from ( #note this is the same derived table as above
select date, s, sum(val) val_sum
from (
select date, s1 as val, 1 as s
from calcu
union all
select date, s2 as val, 2 as s
from calcu
union all
select date, s3 as val, 3 as s
from calcu
union all
select date, s4 as val, 4 as s
from calcu
) x
group by date, s
) v2
where v2.date = v.date
and v2.val_sum < v.val_sum
) GROUP BY c.id # This is the addition you need
See a running solution here

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.

MYSQL dynamic query from rows to columns

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

substitute duplicate value with null SQL

I have a table with the following data :
orderid
item_amount
total_bill_amount
123
2
8
123
6
8
455
4
11
455
6
11
455
1
11
I want to substitute duplicate value for total_bill_amount with null ad keep the first record always with a value and anything after with null. Example of how i want to see the data :
orderid
item_amount
total_bill_amount
123
2
8
123
6
null
455
4
11
455
6
null
455
1
null
Note that my MySQL version is 5.7, so I can't use any window functions in MySQL 8.
You can use row_number() with a subquery:
with to_r(rnum, id, a, t) as (
select row_number() over (order by o.orderid), o.* from orders o
)
select r.id, r.a, case when
(select sum(r1.t = r.t and r1.id = r.id and r.rnum >= r1.rnum) from to_r r1) > 1
then null else r.t end
from to_r r

MySQL group by with max value

Hi I have this table.
id lat lng userId
1 12 23 1
2 45 34 2
3 42 34 3
4 33 34 1
5 36 79 2
6 53 98 2
7 23 90 3
8 23 67 1
Here we have three users. (user ids 1,2,3). I want to get lateset record (id column max value) of each user.
My excepted output is this
userId lat lng
1 23 67
2 53 98
3 23 90
This query will give me group by option
SELECT
*
FROM
covid.locations
GROUP BY userId;
But how do I combine this with MAX(id) function.
One way is to use the following:
SELECT
cl.*
FROM covid.locations cl
INNER JOIN (
SELECT
userid
, MAX( id ) mid
FROM covid.locations
GROUP BY
userId
) g ON cl.userid = g.userid
AND cl.id = cl.mid
Another is to use row_number() over()
SELECT
userId
, lat
, lng
FROM (
SELECT
*
, ROW_NUMBER() OVER (PARTITION BY userid ORDER BY id DESC) rn
FROM covid.locations
GROUP BY
userId
) d
WHERE rn = 1
Both will identify the "most recent" row in the source table based in the id column of that table. Note that the second query requires MySQL version 8+ as this is when row_number() became supported in that database. The first query should run in dbms supporting SQL.
This will do
SELECT
*
FROM
covid.locations
where id in (select max(t.id) from covid.locations t group by t.userId)
order by id desc;
An example of the above query can be found in this SQLFiddle

SQL Row wise total value

I have a table named calcu
id date name s1 s2 s3 s4 min_value
1 02/10/2017 dicky 7 4 8 9 4
2 02/10/2017 acton 12 15 17 19 15
3 02/10/2017 adney 28 13 19 10 13
This is my table in SQL Fiddle
I need row wise total value. I means in a new column total, it will be (s1 + s2 + s3 + s4) i.e. (7+4+8+9) = 28 where id=1, (12+15+17+19)=63 where id=2, (28+13+19+10)=70 where id=3 respectively.
Result will be like below:
id date name s1 s2 s3 s4 min_value Total
1 02/10/2017 dicky 7 4 8 9 4 28
2 02/10/2017 acton 12 15 17 19 15 63
3 02/10/2017 adney 28 13 19 10 13 70
see my problem here
It results all total 161 and 3 rows become 1 row.
How to write SQL query?
The SUM() function is an aggregate function. As with other aggregates, use it only to compute values across multiple rows.
You want to add up values in one row, so just use the + operator (brackets are optional).
As for finding the minimum value in the row, use CASE WHEN with 3 tests, comparing S1, S2, S3 and S4.
This should work:
select
c.id, c.date, c.name, c.s1, c.s2, c.s3, c.s4,
(c.s1 + c.s2 + c.s3 + c.s4) as total,
case
when c.s1 <= c.s2 and c.s1 <= c.s3 and c.s1 <= c.s4 then c.s1
when c.s2 <= c.s1 and c.s2 <= c.s3 and c.s2 <= c.s4 then c.s2
when c.s3 <= c.s2 and c.s3 <= c.s1 and c.s3 <= c.s4 then c.s3
when c.s4 <= c.s2 and c.s4 <= c.s3 and c.s4 <= c.s1 then c.s4
end as min_value
from calcu c
;
See SQLFiddle
select c.id,
c.date, c.name, c.s1, c.s2, c.s3, c.s4,
least(s1,s2,s3,s4) Minvalue,
(s1+s2+s3+s4) Total
from calcu c
I tried simplifying the query. So you are looking for the minimum value among s1,s2,s3 and s4. You can achieve with least function. And you need a total of all four 's' columns. Just add them
SELECT *,s1+s2+s3+s4 as Total FROM calcu