I am trying to calculate a cumulative sum of values that can occur more than once on a given day. Therefore i only want to keep the max value for each given day.
currently i have:
SELECT
DATE_FORMAT(created_at, '%d/%m') AS day1,
SUM(principal) over (order by created_at) AS tot
FROM loan;
OUTPUT:
[
{
"day1" : "21/08",
"tot" : 200
},
{
"day1" : "21/08",
"tot" : 1200
},
{
"day1" : "21/08",
"tot" : 2200
},
{
"day1" : "21/08",
"tot" : 2500
},
{
"day1" : "25/08",
"tot" : 4500
},
{
"day1" : "25/08",
"tot" : 6500
},
{
"day1" : "27/08",
"tot" : 7000
},
{
"day1" : "27/08",
"tot" : 7600
}
]
I know i need to group it by the day, but it needs to take the max value for each group of days. any ideas?
When i group by like so:
SELECT
DATE_FORMAT(created_at, '%d/%m') AS day1,
SUM(principal) over (order by created_at) AS tot
FROM loan
GROUP BY day1;
I Get:
[
{
"day1" : "21/08",
"tot" : 1000
},
{
"day1" : "25/08",
"tot" : 3000
},
{
"day1" : "27/08",
"tot" : 3500
},
{
"day1" : "30/08",
"tot" : 4200
}
]
Which is obviously wrong. Im not sure why it gives that output. But how could i get it to be correct?
You need to nest the sums:
SELECT DATE_FORMAT(created_at, '%d/%m') AS day1,
SUM(SUM(principal)) OVER (ORDER BY MIN(created_at)) AS tot
FROM loan
GROUP BY day1;
I am surprised that your version works at all. It would fail in most databases, because neither principal nor created_at are aggregation keys.
Presumably, you want:
select
date_format(created_at, '%d/%m') as day1,
max(sum(principal) over (order by created_at)) as tot
from loan
group by day1
Actually, you could also express this with distinct, and a slightly different order by clause:
select distinct
date_format(created_at, '%d/%m') as day1,
sum(principal) over (order by date(created_at)) as tot
from loan
I am not a big fan of the way you represent dates; if your data spreads over more than one year, the result will become ambiguous (in the second option), or even wrong (in the first query). I would recommend adding the year to the date representation, or simply date(created_at), which basically truncates the time portion.
Related
Name
Date
Score
A
01-01-2023
100
A
01-01-2023
200
A
03-01-2023
300
B
02-01-2023
400
B
03-01-2023
100
B
03-01-2023
100
i have this table and i want to seperate it into multiple column of date and SUM the score on that date using Query Builder laravel or Raw SQL so it become like :
Name
Day 1
Day 2
Day 3
A
300
0
300
B
0
400
200
all of this is upto the current month so january until 31 and so on
You aren't providing anything like your attempted query, how you are passing the date ( it is a range, month only etc ), and your desired json ouput.
its hard to even assume how you are going to do things specially you are passing a column value as column name in your desired result (which doesn't make much sense with raw sql query unless those columns
aren't dynamic).
but to give you a starting point, you can simply group them by name, then date, then do another grouping by date in the collection
e.i;
$result = DB::table('table_name')->select([
'name',
'date',
])
->selectRaw('sum(score) AS score')
->groupBy(['name', 'date'])->get();
return $result->groupBy('date');
then you should be able to get result in a format like below;
{
"01-01-2023" : [
{
"name": "A",
"date": "01-01-2023",
"score": "300"
}
],
"02-01-2023" : [
{
"name": "A",
"date": "02-01-2023",
"score": "300"
}
{
"name": "B",
"date": "02-01-2023",
"score": "200"
}
],
"03-01-2023" : [
.
.
.
]
}
For you desired table result, thats better be changed to a dynamic rows instead of dynamic column
EDIT
In reference with Karl answer, you can loop through a date range and inject additional select statement.
e.i. current month dates
$dateRange = \Carbon\CarbonPeriod::create(now()->startOfMonth(), now()->endOfMonth() )->toArray();
$result = DB::table('table_name')->select(['name']);
foreach ($dateRange as $date) {
$dateFormat = $date->format('d-m-Y');
$day = $date->format('j');
$result->selectRaw("SUM(CASE WHEN Date = '$dateFormat' THEN Score ELSE 0 END) AS 'Day $day'");
}
return $result->groupBy('name')->get();
just to keep date in group by
->groupBy('date');
I have a list ob objects. Each object contains several properties. Now I want to make a SELECT statement that gives me a list of a single property values. The simplified list look like this:
[
[
{
"day": "2021-10-01",
"entries": [
{
"name": "Start of competition",
"startTimeDelta": "08:30:00"
}
]
},
{
"day": "2021-10-02",
"entries": [
{
"name": "Start of competition",
"startTimeDelta": "03:30:00"
}
]
},
{
"day": "2021-10-03",
"entries": [
{
"name": "Start of competition"
}
]
}
]
]
The working SELECT is now
SELECT
JSON_EXTRACT(column, '$.days[*].entries[0].startTimeDelta') AS list
FROM table
The returned result is
[
"08:30:00",
"03:30:00"
]
But what I want to get (and also have expected) is
[
"08:30:00",
"03:30:00",
null
]
What can I do or how can I change the SELECT statement so that I also get NULL values in the list?
SELECT startTimeDelta
FROM test
CROSS JOIN JSON_TABLE(val,
'$[*][*].entries[*]' COLUMNS (startTimeDelta TIME PATH '$.startTimeDelta')) jsontable
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=491f0f978d200a8a8522e3200509460e
Do you also have a working idea for MySQL< 8? – Lars
What is max amount of objects in the array on the 2nd level? – Akina
Well it's usually less than 10 – Lars
SELECT JSON_EXTRACT(val, CONCAT('$[0][', num, '].entries[0].startTimeDelta')) startTimeDelta
FROM test
-- up to 4 - increase if needed
CROSS JOIN (SELECT 0 num UNION SELECT 1 UNION SELECT 2 UNION SELECT 3) nums
WHERE JSON_EXTRACT(val, CONCAT('$[0][', num, '].entries[0]')) IS NOT NULL;
https://www.db-fiddle.com/f/xnCCSTGQXevcpfPH1GAbUo/0
I am working on queries which are based on time and I would like to get the optimal way to calculate opened cases during the day. I do have table task_interval which has 2 columns start and end.
JSON example:
[
{
"start" : "2019-10-15 20:41:38",
"end" : "2019-10-16 01:44:03"
},
{
"start" : "2019-10-15 20:43:52",
"end" : "2019-10-15 22:18:54"
},
{
"start" : "2019-10-16 20:21:38",
"end" : null,
},
{
"start" : "2019-10-17 01:42:35",
"end" : null
},
{
"create_time" : "2019-10-17 03:15:57",
"end_time" : "2019-10-17 04:14:17"
},
{
"start" : "2019-10-17 03:16:44",
"end" : "2019-10-17 04:14:31"
},
{
"start" : "2019-10-17 04:15:23",
"end" : "2019-10-17 04:53:28"
},
{
"start" : "2019-10-17 04:15:23",
"end" : null,
},
]
The result of query should return:
[
{ time: '2019-10-15', value: 1 },
{ time: '2019-10-16', value: 1 }, // Not 2! One task from 15th has ended
{ time: '2019-10-17', value: 3 }, // We take 1 continues task from 16th and add 2 from 17th which has no end in same day
]
I have written the query which will return cumulative sum of started tasks which end date is not same as started date:
SELECT
time,
#running_total:=#running_total + tickets_number AS cumulative_sum
FROM
(SELECT
CAST(ti.start AS DATE) start,
COUNT(*) AS tickets_number
FROM
ticket_interval ti
WHERE
DATEDIFF(ti.start, ti.end) != 0
OR ti.end IS NULL
GROUP BY CAST(ti.start AS DATE)) X
JOIN
(SELECT #running_total:=0) total;
If you are running MySQL 8.0, one option is to unpivot, then aggregate and perform a window sum to compute the running count:
select
date(dt) dt_day,
sum(sum(no_tasks)) over(order by date(dt)) no_tasks
from (
select start_dt dt, 1 no_tasks from mytable
union all select end_dt, -1 from mytable where end_dt is not null
) t
group by date(dt)
order by dt_day
Side note: start and end are reserved words, hence not good choices for column names. I renamed those to start_dt and end_dt.
In earlier versions, we can emulate the window sum with a user variable, like so:
select
dt_day,
#no_tasks := #no_tasks + no_tasks no_tasks
from (
select date(dt) dt_day, sum(no_tasks) no_tasks
from (
select start_dt dt, 1 no_tasks from mytable
union all select end_dt, -1 from mytable where end_dt is not null
) t
group by dt_day
order by dt_day
) t
cross join (select #no_tasks := 0) x
order by dt_day
Demo on DB Fiddle - both queries yield:
dt_day | no_tasks
:--------- | -------:
2019-10-15 | 1
2019-10-16 | 1
2019-10-17 | 3
I would like to convert below mysql query to mongodb query.
SELECT substring(o.schedule_datetime,1,4) 'Year',
SUM(IF(o.order_status in ('SUCCESS','#SUCCESS'),1,0)) 'SUCCESS'
FROM (
select group_concat(distinct ifnull(os.order_status,'') order by os.order_status
separator '#') 'order_status',schedule_datetime
from order_summary os group by order_number
)o group by 1 desc;
For Example: I have sample table
id order_number product_number order_status schedule_datetime
1 001 001.1 SUCCESS 20180103
2 001 001.2 SUCCESS 20180102
3 111 111.1 SUCCESS 20171225
4 111 111.2 SUCCESS 20171224
5 222 222.1 INPROGRESS 20171122
6 222 222.2 SUCCESS 20171121
I get the output using above mysql query for order status SUCCESS
Year SUCCESS
2018 1
2017 1
I have used separator(#) to combine multiple statues as string and get the desired result by status, to get INPROGRESS i will be just changing SUM funtion as shown below :
SUM(IF(o.order_status in ('INPROGRESS','INPROGRESS#SUCCESS', '#INPROGRESS','#INPROGRESS#SUCCESS'),1,0)) 'INPROGRESS'
I have tried to write the mongodb query, but got stuck how to combine sum and if condition as well group_concat with seperator as i used in mysql query.
db.order_summary.aggregate([
{ "$project" :
{ "orderDate" : 1 , "subOrderDate" : { "$substr" : [ "$order_date" , 0 , 4]},
"order_number":"$order_number"
},
} ,
{ "$group":{
"_id": { "order_number" : "$order_number", "Year": "$subOrderDate", "order_status":{"$addToSet":{"$ifNull":["$order_status",'']}}}
}
},
{ "$group": {
"_id": "$_id.Year", "count": { "$sum": 1 }
}
},
{ "$sort" : { "_id" : -1}}
])
Anyone help will be much appreciated, thanks
There is no Group_Concat kind of functionality in mongodb.
You can compare arrays for matching values in last group with $in operator in 3.4 version.
First $group to get all the distinct order status for a combination for order number and order status.
$sort to sort the order statuses.
Second $group to push all the sorted status values by order number.
Final $group to compare the statuses for each year against the input list of status and output total count for all matches.
db.order_summary.aggregate([{"$project":{
"schedule_datetime":1,
"order_number":1,
"order_status":{"$ifNull":["$order_status",""]}
}},
{"$group":{
"_id":{
"order_number":"$order_number",
"order_status":"$order_status"
},
"schedule_datetime":{"$first": "$schedule_datetime"}
}},
{"$sort":{"_id.order_status": 1}},
{"$group":{
"_id":{
"order_number":"$_id.order_number"
},
"schedule_datetime":{"$first": "$schedule_datetime"},
"order_status":{"$push": "$_id.order_status"}
}},
{"$group":{
"_id":{"$substr":["$schedule_datetime",0,4]},
"count":{
"$sum":{
"$cond": [
{"$in": ["$order_status",[["SUCCESS"], ["","SUCCESS"]]]},
1,
0]
}
}
}},
{"$sort":{"_id":-1}}])
I am trying to create nested json array using 2 tables.
I have 2 tables journal and journaldetail.
Schema is -
journal : journalid, totalamount
journaldetail : journaldetailid, journalidfk, account, amount
Relation between journal and journaldetail is one-to-many.
I want the output in following format :
{ journalid : 1,
totalamount : 1000,
journaldetails : [
{
journaldetailid : j1,
account : "abc",
amount : 500
},
{
journaldetailid : j2,
account : "def",
amount : 500
}
]}
However, by writing this query as per this post the query is:
select j.*, row_to_json(jd) as journal from journal j
inner join (
select * from journaldetail
) jd on jd.sjournalidfk = j.sjournalid
and the output is like this :
{ journalid : 1,
totalamount : 1000,
journaldetails :
{
journaldetailid : j1,
account : "abc",
amount : 500
}
}
{ journalid : 1,
totalamount : 1000,
journaldetails :
{
journaldetailid : j2,
account : "def",
amount : 500
}
}
I want the child table data as nested array in the parent.
I found the answer from here:
Here is the query :
select row_to_json(t)
from (
select sjournalid,
(
select array_to_json(array_agg(row_to_json(jd)))
from (
select sjournaldetailid, saccountidfk
from btjournaldetail
where j.sjournalid = sjournalidfk
) jd
) as journaldetail
from btjournal j
) as t
This gives output in array format.