mysql rename column values after select and average - mysql

In my current table i am trying to get average of columns based on comp_name and below is the output
"Comp_Name" "No_of_Rows" "Column1_Avg" "Column2_Avg" "Column3_Avg"
"Company1 Pty Ltd" "291" "39" "60" "0"
"Company1 Pty." "1699" "23" "76" "0"
"Company2 Ltd" 14335" "6" "82" "10"
"Company2 " "4335" "60" "8" "2"
"Company3 Pty Ltd" "767" "22" "77" "0"
"Company3" "1628" "16" "82" "1"
Is is possible to average "Company1 Pty Ltd" and "Company1 Pty." (and for the other companies) but add the number of rows?
My select query is below and basically it is calculating average based on a certain value and grouping based on the company name available in the table
SELECT Comp_Name,count(*) as No_of_Rows,
CAST( (COUNT(CASE WHEN Column1 < 500 then 1 else NULL end)/COUNT(mytable.ID)) * 100 AS CHAR(2))+'%' as Column1_Avg,
CAST( (COUNT(CASE WHEN (Column1 < 30000 AND Column1 > 500) then 1 else NULL end)/COUNT(mytable.ID)) * 100 AS CHAR(2))+'%' as Column2_Avg,
CAST( (COUNT(CASE WHEN (Column1 > 30000) then 1 else NULL end)/COUNT(mytable.ID)) * 100 AS CHAR(2))+'%' as Column3_Avg
FROM mytable
GROUP BY Comp_Name desc
Expected output:
"Comp_Name" "No_of_Rows" "Column1_Avg" "Column2_Avg" "Column3_Avg"
"Company1" "1990" "31" "68" "0"
"Company2" "18670" ".." ".." "6"
"Company3" "2395" ".." ".." ".."
Can i use some sort of reference table with a list of company_name and it's substitution?

If you want to only take the first word in your GROUP BY clause, you can use:
GROUP BY CASE LOCATE(' ', Comp_Name) WHEN 0 THEN Comp_Name ELSE LEFT(Comp_Name, LOCATE(' ', Comp_Name)) END
Then, if you want to build a reference table, a query like this should be fine:
SELECT DISTINCT Comp_Name, CASE LOCATE(' ', Comp_Name) WHEN 0 THEN Comp_Name ELSE LEFT(Comp_Name, LOCATE(' ', Comp_Name)) END AS Simple_Comp_Name
FROM mytable
ORDER BY Simple_Comp_Name

Related

Select date range into different column

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');

How to split column values by comma and return it as an array

As you can see below I have Name column. I want to split it by / and return the value in array.
MyTable
Id
Name
1
John/Warner/Jacob
2
Kol
If I write a query as
Select Id, Name from MyTable
it will return
{
"id": 1,
"name": "John/Warner/Jacob",
},
{
"id": 2,
"name": "Kol",
},
Which query should I write to get below result ?
{
"id": 1,
"name": ["John", "Warner", "Jacob"],
},
{
"id": 2,
"name": ["Kol"] ,
},
Don't think you can return an array in the query itself, but you could do this...
SELECT id,
SUBSTRING_INDEX(name, '/', 1)
AS name_part_1,
SUBSTRING_INDEX(name, '/', -1)
AS name_part_2
FROM tableName;
Only way to build it as an array would be when processing the result accordingly in whatever language you are using.
You can define a function split, which is based on the fact that substring_index(substring_index(name,'/',x),'/',-1) will return the x-th part of a name when separated by '/'.
CREATE FUNCTION `test`.`SPLIT`(s varchar(200), c char, i integer) RETURNS varchar(200) CHARSET utf8mb4
DETERMINISTIC
BEGIN
DECLARE retval varchar(200);
WITH RECURSIVE split as (
select 1 as x,substring_index(substring_index(s,c,1),c,-1) as y, s
union all
select x+1,substring_index(substring_index(s,c,x+1),c,-1),s from split where x<= (LENGTH(s) - LENGTH(REPLACE(s,c,'')))
)
SELECT y INTO retval FROM split WHERE x=i ;
return retval;
END
and then do:
with mytable as (
select 1 as Id, 'John/Warner/Jacob' as Name
union all
select 2, 'Kol')
select
id, split(Name,'/',x) as name
from mytable
cross join (select 1 as x union all select 2 union all select 3) x
order by id, name;
output:
Id
name
1
Jacob
1
John
1
Warner
2
[NULL]
2
[NULL]
2
Kol
It is, of course, possible to refine this, and leave out the NULL values ...
I will not convert this output to JSON for you ...

Calculate open cases per day

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

Get a record count into root JSON path without it repeating?

Gleaning several articles online, including this one with a CTE, and this one WITHOUT a CTE, I have been successful in getting the data I need, including a count of the results. However, I need this count to be in a specific place in the JSON object... Basically, I know how to get a rowset into a specific JSON structure with FOR JSON PATH, ROOT ('data'), etc.
However, I do not know how to get the "recordsFiltered" into the root of my JSON output. This count is is derived using COUNT(*) OVER () AS recordsFiltered
Basically, I need my structure to look like this (see below)... How do I get "recordsFiltered" into the root $. of the JSON result without it repeating a billion times under the "data":[] section?
The best idea I can come up with is to create a temporary table, and then use that to structure the JSON. But, I want to do it the fancy SQL way, if one exists, using SELECT statements or CTEs where applicable.
{
"draw": 1,
"recordsTotal": 57,
"recordsFiltered": 57, // <<<--- need records filtered HERE
"data": [
{
"DT_RowId": "row_3",
"recordsFiltered": "69,420", // <<<---- NOT HERE!!!
"first_name": "Angelica",
"last_name": "Ramos",
"position": "System Architect",
"office": "London",
"start_date": "9th Oct 09",
"salary": "$2,875"
},
...
]
}
Here is the example SQL code:
SELECT
COUNT(*) OVER () AS recordsFiltered,
id,
a,
b
FROM t1
WHERE
(#Search IS NULL OR
id LIKE '%'+#Search+'%' OR
a LIKE '%'+#Search+'%' OR
b LIKE '%'+#Search+'%')
ORDER BY
CASE
WHEN #SortDir = 'ASC' THEN
CASE #SortCol
WHEN 0 THEN id
WHEN 1 THEN a
WHEN 2 THEN b
END
END desc,
CASE
WHEN #SortDir = 'desc' THEN
CASE #SortCol
WHEN 0 THEN id
WHEN 1 THEN a
WHEN 2 THEN b
END
END DESC
OFFSET #DisplayStart ROWS
FETCH NEXT #DisplayLength ROWS ONLY
for json path, root ('data')
Looks like you need to generate your table results, then use two (or more?) sub-queries
Here's a simplified example:
declare #tbl table (ID int identity, Col1 varchar(50), Col2 int)
insert into #tbl (Col1, Col2) values ('A',1),('B',2),('C',3)
select
(select count(1) from #tbl) as 'total',
(select * from #tbl for json path) as 'data'
for json path
produces:
[
{
"total": 3,
"data": [
{
"ID": 1,
"Col1": "A",
"Col2": 1
},
{
"ID": 2,
"Col1": "B",
"Col2": 2
},
{
"ID": 3,
"Col1": "C",
"Col2": 3
}
]
}
]
Without knowing the rest of your code/schema, here's my guess at your needed query:
select
*
into
#MyTable
from
t1
WHERE
(#Search IS NULL OR
id LIKE '%'+#Search+'%' OR
a LIKE '%'+#Search+'%' OR
b LIKE '%'+#Search+'%')
select
(select count(*) from #MyTable) as recordsFiltered,
(
select
id,
a,
b
from
#MyTable
ORDER BY
CASE
WHEN #SortDir = 'ASC' THEN
CASE #SortCol
WHEN 0 THEN id
WHEN 1 THEN a
WHEN 2 THEN b
END
END desc,
CASE
WHEN #SortDir = 'desc' THEN
CASE #SortCol
WHEN 0 THEN id
WHEN 1 THEN a
WHEN 2 THEN b
END
END DESC
OFFSET #DisplayStart ROWS
FETCH NEXT #DisplayLength ROWS ONLY
for json path
) as [data]
for json path
Using a CTE:
with cte as ()
select
*
from
t1
WHERE
(#Search IS NULL OR
id LIKE '%'+#Search+'%' OR
a LIKE '%'+#Search+'%' OR
b LIKE '%'+#Search+'%')
)
select
(select count(*) from cte) as recordsFiltered,
(
select
id,
a,
b
from
cte
ORDER BY
CASE
WHEN #SortDir = 'ASC' THEN
CASE #SortCol
WHEN 0 THEN id
WHEN 1 THEN a
WHEN 2 THEN b
END
END desc,
CASE
WHEN #SortDir = 'desc' THEN
CASE #SortCol
WHEN 0 THEN id
WHEN 1 THEN a
WHEN 2 THEN b
END
END DESC
OFFSET #DisplayStart ROWS
FETCH NEXT #DisplayLength ROWS ONLY
for json path
) as [data]
for json path

Create sum of Elements that are not grouped by

I'm trying to sum up all values from the values column by the timestamp "ts".
What I got so far:
select ts, item, value
from statistics
where indicator = "something"
actual:
"1", "item1", "100"
"1", "item2", "200"
"1", "item3", "300"
"2", "item1", "101"
"2", "item2", "202"
"2", "item3", "303"
expected:
"1", "item1", "100"
"1", "item2", "200"
"1", "item3", "300"
"1", "sum", "600"
"2", "item1", "101"
"2", "item2", "202"
"2", "item3", "303"
"2", "sum", "606"
Use GROUP BY and WITH ROLLUP.
select ts, item, SUM(value)
from statistics
where indicator = "something"
GROUP BY ts, item WITH ROLLUP
You have to group by both columns to get the original rows in the result, then WITH ROLLUP adds the total lines. This will add a row with item = NULL for the ts totals, and a row at the end with both ts = NULL, item = NULL for the grand total.
I think Barmar's solution should be accepted, but just for the sake of giving an alternative solution, I would describe this one:
select ts, item, value
from
(
(
select ts as ts, item, value
from statistics
where indicator = "something"
)
union
(
select ts as ts, 'zzzz_sum' as item, count(*) as value
from statistics
group by ts
)
) t
order by ts, item;
We basically union two result sets and order them accordingly