table name: transactions
customer
debit
credit
a
70
50
a
100
20
a
20
60
b
100
20
b
40
80
b
10
30
c
100
200
c
100
30
c
80
90
d
100
200
d
90
30
d
80
90
e
100
100
e
100
30
e
80
90
check who have positive or negative bal_type;
if(total of debit-total of credit>0 )
positive
else
negative
I try below:
SELECT CASE WHEN (SUM(debit)-SUM(credit))<0 THEN "negative"
ELSE "positive"
END AS bal_type,
customer
FROM
transactions
GROUP BY customer
MY OUTPUT:
bal_type
customer
positive
a
positive
b
negative
c
negative
d
positive
e
expecting:
bal_type
customer
positive
e,a,b
negative
c,d
NOTE: bal_type is derived type column. positive customer name should be order by DESC using there SUM(debit)-SUM(credit).
and negative customer name should be order by ASC using there SUM(debit)-SUM(credit).
You need 2 levels of aggregation and GROUP_CONCAT() aggregate function to concatenate the customers:
SELECT bal_type,
GROUP_CONCAT(customer ORDER BY customer) AS customer
FROM (
SELECT CASE WHEN SUM(debit)- SUM(credit) > 0 THEN 'positive' ELSE 'negative' END AS bal_type,
customer
FROM transactions
GROUP BY customer
) AS t
GROUP BY bal_type;
See the demo.
Related
I have table A
uid dt val_A
10 04/09/2012 34
10 08/09/2012 35
10 10/09/2012 36
100 04/09/2012 40
100 08/09/2012 41
and table B
uid date val_B
10 04/09/2012 1
10 05/09/2012 1
10 06/09/2012 2
10 07/09/2012 2
10 08/09/2012 1
100 07/09/2012 1
100 07/09/2012 3
I want to join them to get table C. I want to join them on uid. Furthermore I want to have a new column val_C which holds the average of val_B where date in B is greater or equal than the corresponding row-value dt in A AND less than the next higher dt value for this uid in table A. It means I want to aggregate the values in B based on date ranges defined in A. The joined table should look like this:
uid dt val_A val_C
10 04/09/2012 34 1.5
10 08/09/2012 35 1
10 10/09/2012 36 0
100 04/09/2012 40 2
100 08/09/2012 41 0
How can this be achieved?
//EDIT
How could a more generalized solution look like where all dates in B2 which are greater than the greatest date in A are being joined & aggregated to the greatest date in A. B2:
uid date val_B
10 04/09/2012 1
10 05/09/2012 1
10 06/09/2012 2
10 07/09/2012 2
10 08/09/2012 1
100 07/09/2012 1
100 07/09/2012 3
100 10/09/2012 4
100 11/09/2012 2
Desired output C2:
uid dt val_A val_C
10 04/09/2012 34 1.5
10 08/09/2012 35 1
10 10/09/2012 36 0
100 04/09/2012 40 2
100 08/09/2012 41 3
If you're on MySQL v8+ that supports LEAD() function, then you can try this:
WITH cte AS (
SELECT uid, dt, val_A,
IFNULL(LEAD(dt) OVER (PARTITION BY uid ORDER BY uid, dt),dt) dtRg
FROM tableA)
SELECT cte.uid, cte.dt, cte.val_A,
AVG(val_B) AS val_C
FROM cte
LEFT JOIN tableB tb1
ON cte.uid=tb1.uid
AND tb1.dt >= cte.dt
AND tb1.dt < cte.dtRg
GROUP BY cte.uid, cte.dt, cte.val_A
The query in common table expression (cte):
SELECT uid, dt, val_A,
IFNULL(LEAD(dt) OVER (PARTITION BY uid ORDER BY uid, dt),dt) dtRg
FROM tableA
will give you a result like this:
As you can see, the dtRg column is generated using LEAD() function which takes the next row dt value according to the ORDER BY. Read more about LEAD() here.
After that, join the cte with tableB on matching uid and where tableB.dt is the same or bigger than the existing tableA.dt - which is now as cte.dt, but lower than cte.dtRg - which is the next date in tableA that was generated by LEAD(). And finally adding AVG(val_B) AS val_C
Demo fiddle
On older MySQL version, you can try this:
SELECT tA.uid, tA.dt, tA.val_A,
AVG(val_B) AS val_C
FROM
(SELECT uid, dt, val_A,
(SELECT dt FROM tableA ta1
WHERE ta1.uid=ta2.uid
AND ta1.dt > ta2.dt LIMIT 1) AS dtRg
FROM tableA ta2) tA
LEFT JOIN tableB tB
ON tA.uid=tB.uid
AND tB.dt >= tA.dt
AND tB.dt < tA.dtRg
GROUP BY tA.uid, tA.dt, tA.val_A;
The difference are as following:
Instead of using LEAD(), it uses correlated subquery in SELECT to get the next dt value of next row in the same uid.
Instead of common table expression, it uses a derived table.
Fiddle for MySQL v5.7 version
I have a dataset as follows,
Table_Date Description Amount
4/17/2022 A 10
4/17/2022 B 45
4/17/2022 C 34
4/17/2022 D 23
4/17/2022 E 76
4/17/2022 F 45
4/18/2022 A 23
4/18/2022 B 45
4/18/2022 C 67
4/18/2022 D 78
4/18/2022 E 98
4/18/2022 F 54
First I need to get sum of Amount for each day for last 8 days.
So I used following query.
Select Table_Date,sum(Amount) as Total_Amount from usertable where Table_Date>=DATE(NOW()-INTERVAL 8 DAY) group by Table_Date;
Result
Table_Date Total Amount
4/17/2022 233
4/18/2022 365
Now I need to get the maximum and the minimum after creating this. So I tried as follows,
select max(Total_Amount) from
(
select Table_Date,sum(Amount) as Total_Amount from usertable where Table_Date>=DATE(NOW()-INTERVAL 8 DAY) group by Table_Date
) group by Table_Date;
Seems this is not correct. Can someone show me how to get the output as follows ,
Min =233
Max=365
Note : My server do not support window functions
If I understand correctly, you might need to use aggregate function without group by
SELECT MIN(Total_Amount),
MAX(Total_Amount)
FROM (
Select Table_Date,sum(Amount) as Total_Amount
from usertable
where Table_Date>=DATE(NOW()-INTERVAL 8 DAY)
group by Table_Date
) t1
i need help to create a sql query that can find the smallest value in 1 row , and display it in the last column, like this table.
id
out
mid
in
Smallest
1
200
100
50
50
2
100
150
50
50
3
200
100
250
100
4
50
100
150
50
5
50
100
100
50
6
20
200
100
20
7
-
-
100
100
8
150
-
100
100
this is my query :
On MySQL you may use the scalar LEAST() function:
SELECT id, `out`, mid, `in`, LEAST(`out`, mid, `in`) AS Smallest
FROM yourTable;
If your database doesn't have a LEAST function, we can use a CASE expression as an alternative:
SELECT id, `out`, mid, `in`,
CASE WHEN `out` < mid AND `out` < `in` THEN `out`
WHEN mid < `in` THEN mid
ELSE `in` END AS Smallest
FROM yourTable;
Side note: Both IN and OUT are reserved MySQL keywords, and you should avoid naming your columns with them.
I have two columns person_type and time_in. I am trying to find the percentage of a person_type in each corresponding hour. My database looks like:
person_type || time_in
FT N/A
FT 0
FT 4
FT 22
FT 23
NL 1
NL 2
NL 3
NL 4
NL 4
I am trying to create a query to return the following output:
% FT || time_in
100 N/A
100 0
0 1
0 2
0 3
33 4
100 22
100 23
I have tried the following code:
select (
(select count(*) from delivery where person_type = 'FT')
/count(*)) as 'FT %', time_in
from delivery
group by hour
order by hour;
The issue here is my subquery returns 5 for every time_in value so my current output looks like:
% FT || time_in
500 N/A
500 0
500 1
500 2
500 3
166 4
500 22
500 23
If I group by time_in in my subquery then I get an error message saying that the query is returning more than one value.
You can use conditional aggregation. For this purpose, I think that avg() is the simplest logic:
select time_in,
avg( person_type = 'FT' ) * 100 as percent_ft
from delivery
group by time_in
order by time_in;
I have a table with the following entries:
id customer amount kind
1 123 15 g
2 123 30 op
3 234 20 g
4 345 25 g
5 456 12 g
6 456 15 op
What I want to do is to sum all amounts with the kind "g".
Now I want to add a condition:
"Only sum the amount to the sum if there is another entry of the customer with the kind 'op'"
Means my result should be 27 in this case and not 72.
What's a good way to add this condition?
Thanks!
To get the sum for each customer do
select customer, sum(case when kind = 'g' then amount else 0 end) as c_sum
from your_table
group by customer
having sum(kind = 'op') > 0
to get the total sum do
select sum(c_sum)
from
(
select customer, sum(case when kind = 'g' then amount else 0 end) as c_sum
from your_table
group by customer
having sum(kind = 'op') > 0
) tmp