aggregation condition in case when - mysql

I have a dataset with a structure similar to the one bellow
fruit, value
apple, 234
apple, 2341
pear, 3233
grape, 323
pear, 3234
grap 1234
I am trying to find a count of a range of the numbers that are in the bottom 10% of the range by performing a query like the one below. (the ultimate goal of the query is to calculate and see the ranges of the calc go up in increments of 10%) I also have a group by clause so I would like the counts to be grouped by the fruit and aggregated that way. Bellow is the query I have tried
select fruit, count(case when (value <= (((max(value) - min(value)) * .1) + min(value))) then 1 end)
from fruit_juice
group by substring(fruit, 5, 5);

Aggregate the table in the from clause to get the limits you want. Join those results back to your query and use those values for the query:
select substring(fj.fruit, 5, 5),
sum(fj.value <= fmm.minv + (fmm.maxv - fmm.minv) * 0.1)
from fruit_juice fj join
(select substring(fruit, 5, 5) as fruit5,
max(value) as maxv, min(value) as minv
from fruit_juice
group by substring(fruit, 5, 5)
) fmm
on fmm.fruit5 = substring(fj.fruit, 5, 5)
group by substring(fruit, 5, 5);
Note that your group by expressions should match the expressions in the select clause.
EDIT:
I'm not sure where the substring() is coming from in your question, so this version removes it:
select fj.fruit, sum(fj.value <= fmm.minv + (fmm.maxv - fmm.minv) * 0.1)
from fruit_juice fj join
(select fruit,
max(value) as maxv, min(value) as minv
from fruit_juice
group by fruit
) fmm
on fmm.fruit = fj.fruit
group by fruit;

Related

Change result from operation if equal to 0

I have created this query, but sometimes this part (CEILING(TIMESTAMPDIFF(MONTH, od.insstart, NOW()) / od.term) returns 0, So I want to check result from this part and if it's equal to 0, to change it to 1, before compare it with second part = (SELECT COUNT(id) is there a way to do this ?
SELECT id FROM od where pol = '123' AND (CEILING(TIMESTAMPDIFF(MONTH, od.insstart, NOW()) / od.term) = (SELECT COUNT(id) FROM od as tt WHERE tt.policyNumber = '123'))
If result from (CEILING(TIMESTAMPDIFF(MONTH, od.insstart, NOW()) / od.term) is equal to 0 -> change it to 1 and then compare with second part which is (SELECT COUNT(id)
Use GREATEST() function
GREATEST( CEILING(TIMESTAMPDIFF(MONTH, od.insstart, NOW()) / od.term), 1 )

Drop Off Funnel in SQL

I have a table that has user_seq_id and no of days a user was active in the program. I want to understand the drop-off funnel. Like how many users were active on day 0 (100%) and on day 1, 2 and so on.
Input table :
create table test (
user_seq_id int ,
NoOfDaysUserWasActive int
);
insert into test (user_seq_id , NoOfDaysUserWasActive)
values (13451, 2), (76453, 1), (22342, 3), (11654, 0),
(54659, 2), (64420, 1), (48906, 5);
I want Day, ActiveUsers, and % Distribution of these users.
One method doesn't use window functions at all. Just a list of days and aggregation:
select v.day, count(t.user_seq_id),
count(t.user_seq_id) / c.cnt as ratio
from (select 0 as day union all select 1 union all select 2 union all select 3 union all select 4 union all select 5
) v(day) left join
test t
on v.day <= t.NoOfDaysUserWasActive cross join
(select count(*) as cnt from test) c
group by v.day, c.cnt
order by v.day asc;
Here is a db<>fiddle.
The mention of window function suggests that you are thinking:
select NoOfDaysUserWasActive,
sum(count(*)) over (order by NoOfDaysUserWasActive desc) as cnt,
sum(count(*)) over (order by NoOfDaysUserWasActive desc) / sum(count(*)) over () as ratio
from test
group by NoOfDaysUserWasActive
order by NoOfDaysUserWasActive
The problem is that this does not "fill in" the days that are not explicitly in the original data. If that is not an issue, then this should have better performance.

sql order by not working with decimal(18,6)

The SQL query with order by clause is not working properly.
Query:
select cast(actual_qty as decimal(18,2)) from `Bin`
where warehouse = `Warehouse`.name
order by actual_qty desc
Output of above query:
303.00
550.00
0.00
3.00
The type of actual_qty is decimal(18,6). I tried using cast function in order by, didn't work.
Any help is much appreciated!!
Update1:
Here's my full query that is being fired:
select `tabWarehouse`.name, CONCAT_WS(" : ", "Actual Qty", ifnull( ( select round(`tabBin`.actual_qty, 2) as qty
from `tabBin` where `tabBin`.warehouse = `tabWarehouse`.name
and `tabBin`.item_code = '30440'
order by qty desc), 0) ) as actual_qty
from `tabWarehouse` where `tabWarehouse`.`name` like '%%%%' and ifnull(`tabWarehouse`.company, '') in ('', 'TILE TEST') and `tabWarehouse`.is_group = 0.0
limit 0, 20
The output is still the same which is not really ordered by the qty in a descending manner.
Update 2:
This is how my code looks:
query = """select tw.name,
CONCAT_WS(" : ", "Actual Qty", ifnull(round(`tabBin`.actual_qty, 2), 0 ) actual_qty
from `tabWarehouse` tw left join `tabBin` tb
on tb.warehouse = tw.name {bin_conditions}
where
tw.`{key}` like {txt}
{fcond} {mcond}
order by ifnull(round(tb.actual_qty, 2), 0) desc
limit
{start}, {page_len}
""".format(
bin_conditions=get_filters_cond(doctype, filter_dict.get("Bin"),bin_conditions, ignore_permissions=True),
# sub_query=sub_query,
key=searchfield,
fcond=get_filters_cond(doctype, filter_dict.get("Warehouse"), conditions),
mcond=get_match_cond(doctype),
start=start,
page_len=page_len,
txt=frappe.db.escape('%{0}%'.format(txt))
)
The cast in select doesn't change the type of the column -- and the order by is referring to the column in the table, not the expression in the select.
One option is to give it a new name and use that:
select cast(actual_qty as decimal(18,2)) as qty
from `Bin`
where warehouse = `Warehouse`.name
order by qty desc;
Or repeat the expression:
select cast(actual_qty as decimal(18,2)) as qty
from `Bin`
where warehouse = `Warehouse`.name
order by cast(actual_qty as decimal(18,2)) desc
There is no ORDER BY clause in your query.
The ORDER BY inside the correlated subquery is useless because that subquery will work only if it returns only 1 row and does not effect the final results.
Try this:
select tw.name,
CONCAT_WS(" : ", "Actual Qty", ifnull(round(tb.actual_qty, 2), 0)) actual_qty
from tabWarehouse tw left join tabBin tb
on tb.warehouse = tw.name and tb.item_code = '30440'
where tw.name like '%%%%'
and ifnull(tw.company, '') in ('', 'TILE SELECT')
and tw.is_group = 0.0
order by ifnull(round(tb.actual_qty, 2), 0) desc limit 0, 20
Also what is this condition:
`tabWarehouse`.`name` like '%%%%'

SQL subquery to fetch rows by column values

I have a PostgreSQL table like this:
CREATE TABLE foo(man_id, subgroup, power, grp)
AS VALUES
(1, 'Sub_A', 4, 'Group_A'),
(2, 'Sub_B', -1, 'Group_A'),
(3, 'Sub_A', -1, 'Group_B'),
(4, 'Sub_B', 6, 'Group_B'),
(5, 'Sub_A', 5, 'Group_A'),
(6, 'Sub_B', 1, 'Group_A'),
(7, 'Sub_A', -1, 'Group_B'),
(8, 'Sub_B', 2, 'Group_B'),
(9, 'Sub_C', 2, 'Group_B');
The power calculation works like this:
Total Power of Subgroup Sub_A in the grp Group_A is (4 + 5 ) = 9
Total Power of Subgroup Sub_B in the grp Group_A is ((-1) + 1 ) = 0
Total Power of Subgroup Sub_A in the grp Group_B is ((-1) + (-1) ) = -2
Total Power of Subgroup Sub_B in the grp Group_B is (6 + 2 ) = 8
So the power of Sub_A in the Group_A is not equal to power of Sub_A in the Group_B
So the power of Sub_B in the Group_A is not equal to power of Sub_B in the Group_B
I want to query the database and fetch the rows where, for a same subgroup name total power is not equal across all the other grp names.
What would be the recommended way to do this?
I can find the sum of total power:
SELECT sum(power) AS total_power
FROM foo
GROUP BY grp
MySQL solution will be accepted as well.
One way:
SELECT f.*
FROM (
SELECT subgroup
FROM (
SELECT subgroup, grp, sum(power) AS total_power
FROM foo
GROUP BY subgroup, grp
) sub
GROUP BY 1
HAVING min(total_power) <> max(total_power) -- can fail for NULL values;
) sg
JOIN foo f USING (subgroup);
In your example all rows qualify except for the last one with 'Sub_C'.
Closely related to your previous question:
Do all groups have equal total power for given subgroup?
Similar explanation and considerations.
db<>fiddle here
I think a way to phrase your problem is that you want to total the power for subgroup in a group, then find if a subgroup with the same name exists in another group with a different power.
The first step is to total the powers like you want:
SELECT grp, subgroup, sum(power) as power
FROM foo
GROUP BY grp, subgroup
That should give you a result like:
grp subgroup power
------- -------- -----
Group_A Sub_A 9
Group_A Sub_B 0
Group_B Sub_A -2
Group_B Sub_B 8
Group_B Sub_C 2
Once you have that, you can use a CTE to join the results with itself for the comparison to get what you want. You don't specify whether you want Sub_C to appear, if 'not existing' qualifies as having a 'different total power', then you would want to use a left join and check for nulls in alias b. The < in the join makes it so that each difference only appears once with the lower order group as grp1.
WITH totals AS (
SELECT grp, subgroup, sum(power) as power
FROM foo
GROUP BY grp, subgroup
ORDER BY grp, subgroup
)
SELECT a.subgroup,
a.grp as grp1, a.power as Power1,
b.grp as grp2, b.power as Power2
FROM totals a
INNER JOIN totals b ON b.subgroup = a.subgroup
and a.grp < b.grp
WHERE b.power <> a.power
ORDER BY a.subgroup, a.grp, b.grp
with totals as (
select grp, subgroup, sum(power) as total_power
from foo
group by grp, subgroup
)
select * from totals t1
where t1.total_power <> all (
select t2.total_power from totals t2
where t2.subgroup = t.subgroup and t2.grp <> t1.grp
)
or
with totals as (
select grp, subgroup, sum(power) as total_power
from foo
group by grp, subgroup
), matches as (
select grp, subgroup, count(*) over (partition by subgroup, total_power) as matches
)
select * from counts where matches = 1;
I would use window functions:
select f.*
from (select f.*,
min(sum_value)) over (partition by group) as min_sum_value,
max(sum_value)) over (partition by group) as max_sum_value,
from (select f.*,
sum(value) over (partition by subgroup, group) as sum_value
from foo f
) f
) f
where min_sum_value <> max_sum_value;

Problems with an UNION MySQL

I have this query:
SELECT COUNT(*) AS invoice_count, IFNULL(SUM(qa_invoices.invoice_total), 0)
AS invoice_total, IFNULL(SUM(qa_invoices.invoice_discount) ,0) AS invoice_discount
FROM qa_invoices
WHERE (DATE(qa_invoices.invoice_date) BETWEEN '12/06/25' AND '12/06/25')
AND qa_invoices.status_code IN (5, 8)
UNION
SELECT IFNULL(SUM(qa_returns.client_credit), 0)
FROM qa_returns
WHERE (DATE(qa_returns.returnlog_date) BETWEEN '12/06/25' AND '12/06/25');
I get the error:
The used SELECT statements have a different number of columns.
I'm trying to join this 2 selects with an UNION command, if we look returnlog_date and invoice_date have the same data condition, if there is any way to perform both queries into one would be better.
Use a subselect:
SELECT
COUNT(*) AS invoice_count,
IFNULL(SUM(invoice_total), 0) AS invoice_total,
IFNULL(SUM(invoice_discount), 0) AS invoice_discount,
(
SELECT IFNULL(SUM(qa_returns.client_credit), 0)
FROM qa_returns
WHERE qa_returns.returnlog_date >= '2012-06-25'
AND qa_returns.returnlog_date < '2012-06-26'
) AS client_credit
FROM qa_invoices
WHERE invoice_date >= '2012-06-25'
AND invoice_date < '2012-06-26'
AND status_code IN (5, 8)
The error is telling you exactly what the problem is, for a UNION you have to have the same number of columns in each query.
I am not sure which column in your second query corresponds to your first query, but you can insert a zero in your second query.
Something like this:
SELECT COUNT(*) AS invoice_count
, IFNULL(SUM(qa_invoices.invoice_total), 0) AS invoice_total
, IFNULL(SUM(qa_invoices.invoice_discount) ,0) AS invoice_discount
FROM qa_invoices
WHERE (DATE(qa_invoices.invoice_date) BETWEEN '12/06/25' AND '12/06/25')
AND qa_invoices.status_code IN (5, 8)
UNION
SELECT 0
, IFNULL(SUM(qa_returns.client_credit), 0)
, 0
FROM qa_returns
WHERE (DATE(qa_returns.returnlog_date) BETWEEN '12/06/25' AND '12/06/25');
Result set you union together have to have the exact same columns.
Well in order to do a UNION u need to have same number of columns