SQL grouping with 3 data - mysql

I think what I need isn't that complex but I've already spent days playing with SQL queries.
Here my basic table structure
id | status | date
1 | active | 2020-01-02
2 | complete | 2020-01-03
3 | complete | 2020-01-03
4 | active | 2020-01-03
I'm trying to achieve this result on my query, grouping the result by date then counting the following
total - based on total row count by date,
active - based on active status by date,
complete - based on complete status by date
this is my desired format below
[
{
total: 1,
active: 1,
completed: 0,
date: "2020-01-02"
},
{
total: 3,
active: 1,
completed: 2,
date: "2020-01-03"
}
]
This runs on Laravel and I'm trying to play with Eloquent as well as the Query builder
but no success
$leadReport = Lead::select(
DB::raw('count(id) as `total`'),
//DB::raw('count(CASE WHEN `status` = `active ) as `active`'),
//DB::raw('count(CASE WHEN `status` = `complete`) as `completed`'),
DB::raw("DATE_FORMAT(created_at, '%Y-%m-%d') as date")
)->where('iso','au')->groupBy('date')->orderBy('date')->get();
return $leadReport;
Appreciate any help
EDIT
just want to thank you #Yazan for the recommended query by using SUM,
I manage to use it on laravel query builder like below
$leadSummary = Lead::select(
DB::raw("Sum(CASE WHEN status = 'completed' AND json_unquote(json_extract(`lenders`, '$.current_status')) IN ('Settled', 'Funded') THEN 1 ELSE 0 END) AS settled"),
DB::raw("Sum(CASE WHEN status = 'completed' AND json_unquote(json_extract(`lenders`, '$.current_status')) NOT IN ('Settled', 'Funded') THEN 1 ELSE 0 END) AS rejected"),
DB::raw("Sum(CASE WHEN status = 'active'THEN 1 ELSE 0 END) AS active ")
)->where('iso','au')->get();

use sum instead of count like this
SELECT Count(id) AS total,
Sum(CASE
WHEN status = 'active' THEN 1
ELSE 0
END) AS active,
Sum(CASE
WHEN status = 'completed' THEN 1
ELSE 0
END) AS completed,
Date_format(date, '%Y-%m-%d') AS date
FROM MYtable
GROUP BY date
ORDER BY date;

Related

Count and Percentage of records in range of values

I have a table with columns
TicketID - ID of the ticket
AssignedTo - UserID of person to whom ticket is assigned
CreatedTime - Time when Ticket is received
HandleTime - Time when Ticket is picked up for handling
FinishTime - Time when Ticket is finished handling
I need to retrieve the following data grouped to individual AssignedTo ID:
AssignedTo
Picking Rate in the following ranges(both % and count)
<1 minutes
1-2 minutes
2-5 minutes
Closing Rate in the following ranges(both % and count)
same ranges as above
Total Tickets
I have come up with a initial query as
SELECT
User,
sum(case when PickupTime <=1 then 1 else 0 end) as range1,
sum(case when PickupTime <=2 then 1 else 0 end) as range2,
...
FROM
(SELECT
((HandleTime - CreatedTime)/60000) as PickupTime,
((FinishTime - CreatedTime)/60000) as CompletedTime,
AssignedTo as User
FROM
TicketTable
)T
GROUP BY
User
Here I am able to get only the Pickup range counts.I still need Pickup range percentages and also Closing range counts and percentages.How do I get them?
EDIT:
Let us consider a sample dataset and only two ranges <=1 and >1 and also consider time as minutes directly here whereas in original table its stored as timestamp.
TicketID | AssignedTo | CreatedTime | HandleTime | FinishTime
1 001 2 3 3
2 001 4 6 8
3 002 1 2 3
In the above table User 001 is assigned a total of 2 tickets and User 002 is assigned a total of 1 ticket.
The PickupTime and CompletedTime for the tickets are
TicketID | PickupTime | CompletedTime
1 1 1
2 2 4
3 1 2
So for User-001 out of the two tickets assigned to him, he has picked 1 ticket within 1 minute range and 1 greater than 1 minute range.So percentage of tickets within 1 minute range is 50% and over 1 minute range is 50% for him.Same applies with regards to CompletedTime and also to the User-002 too.
So the final result what i want is.
AssignedTo | Pickup_range1_count | Pickup_range2_count | Pickup_range1_percentage |
001 1 1 0.5
002 1 0 1
Pickup_range2_percentage | Complete_range1_count | Complete_range2_count |
0.5 1 1
0 0 1
Complete_range1_percentage | Complete_range2_percentage
0.5 0.5
0 1
According to your example you already almost got it. All you need is the ratio of the individual sums and the total sum (or the count would have done it to). Something like
SELECT AssignedTo,
sum(1) AllCount,
sum(CASE
WHEN HandleTime - CreatedTime <= 1
THEN 1
ELSE 0
END) Range1PickupCount,
sum(CASE
WHEN HandleTime - CreatedTime > 1
THEN 1
ELSE 0
END) Range2PickupCount,
...
sum(CASE
WHEN HandleTime - CreatedTime <= 1
THEN 1
ELSE 0
END) / sum(1) * 100 Range1PickupPercentage,
sum(CASE
WHEN HandleTime - CreatedTime > 1
THEN 1
ELSE 0
END) / sum(1) * 100 Range2PickupPercentage,
...
FROM Tickets
GROUP BY AssignedTo;
should be a valid demonstration and something you can continue upon.
(Disclaimer: Not tested at all, as no DDL and DML was provided.)

Group Totals from Logs by Month

I have a log table that stores media requests by act_datetime, app_id, location_id, media_id and media_type_id. What I want is each resultset row to contain type totals for each month. For example, log records contain:
I tried using temp tables to extract records by app_id and grouping by month, but I get multiple rows for each total. I can use sub-queries, but how do I get a total row by type for each month?
Any help is greatly appreciated.
Thanks,
Brandon
EDIT
The follow code works combining shared ideas:
This query takes about 13 seconds parsing about 8.1 million rows. Is that acceptable? Lastly how do you display date as 2018-1 as one column? I'm getting errors when converting to string since the date is also used in the group and order by clauses.
I also want to try code construct sum( case when media_type_id = 1 then 1 else 0 end )... to see if get same results and speed.
Thanks for everyone's help!
Assuming this is SQL Server, and not MySQL:
SELECT DATEPART(MONTH, act_datetime) AS [Month],
COUNT(CASE WHEN app_id = 14 AND media_type_id = 1 AND location_id = 1 THEN act_datetime END) AS MP3_Messages_MO,
COUNT(CASE WHEN app_id = 14 AND media_type_id = 1 AND location_id = 2 THEN act_datetime END) AS MP3_Messages_FL,
COUNT(CASE WHEN app_id = 14 AND media_type_id = 3 AND location_id = 1 THEN act_datetime END) AS MP3_Messages_MO,
COUNT(CASE WHEN app_id = 14 AND media_type_id = 3 AND location_id = 2 THEN act_datetime END) AS MP3_Messages_FL,
COUNT(CASE WHEN app_id = 55 AND media_type_id = 1 THEN act_datetime END) AS MP3_Music,
COUNT(CASE WHEN app_id = 55 AND media_type_id = 9 THEN act_datetime END) AS ZIP_Music
FROM YourTable
GROUP BY DATEPART(MONTH, act_datetime);
Note you have included no logic for differing years, data for each Month will do a count irrespective of year.
This is also completely untested, due to lack of consumable data.

How to Efficiently Find Number of Specific Day Between Two Dates in MySQL?

Different variations of this question have been asked before, but none for the use case that I'm looking for. I'd like to find the specific number of weekdays between two dates for each row of a MySQL table and then update a column of each row with the result of that operation. This is part of an ETL process, and I'd like to keep this in a stored procedure if at all possible.
Data
Dates are of DATE type and I'd like to find the number of a specific because I have 7 day columns that have a flag if a record occurs on that day of the week. Like this (1 is Monday):
day_1 | day_2 | day_3 | day_4 | day_5 | day_6 | day_7
----- | ----- | ----- | ----- | ----- | ----- | -----
0 | 1 | 0 | 1 | 1 | 0 | 1
Example Use Case
I'm doing this because I'm trying to find the frequency of rows for a timeframe that's not available in the input data (call it input). So for a record that had start and end date values of 2016-01-01 and 2016-03-01, I'd want to know how often that record would have occurred only from 2016-01-01 to 2016-01-31, inclusive. I initially tried to do this by making a table that contained all datevalues for many years into the future like:
datevalue
---------
2016-01-01
2016-01-02
...
and then joining input to that table on start_date and end_date and then aggregating up while counting the number of each day like this:
SUM(CASE WHEN WEEKDAY(B.datevalue) + 1 = 1 THEN 1 ELSE 0 END) * day_1 +
SUM(CASE WHEN WEEKDAY(B.datevalue) + 1 = 2 THEN 1 ELSE 0 END) * day_2 +
SUM(CASE WHEN WEEKDAY(B.datevalue) + 1 = 3 THEN 1 ELSE 0 END) * day_3 +
SUM(CASE WHEN WEEKDAY(B.datevalue) + 1 = 4 THEN 1 ELSE 0 END) * day_4 +
SUM(CASE WHEN WEEKDAY(B.datevalue) + 1 = 5 THEN 1 ELSE 0 END) * day_5 +
SUM(CASE WHEN WEEKDAY(B.datevalue) + 1 = 6 THEN 1 ELSE 0 END) * day_6 +
SUM(CASE WHEN WEEKDAY(B.datevalue) + 1 = 7 THEN 1 ELSE 0 END) * day_7 AS adj_total_frequency
That worked perfectly on a smaller dataset, but input has > 30 million records, and when I tried running on that procedure it ran for 36 hours before I killed it.
Is there a more efficient way of doing this in MySQL?
Too long for a comment but, combining with the pre-calculation of weekday I originally suggested, how much does this (using a single SUM with a complete CASE) work out for you?
SUM(CASE WHEN B.weekdayval = 1 AND day_1 THEN 1
WHEN B.weekdayval = 2 AND day_2 THEN 1
WHEN B.weekdayval = 3 AND day_3 THEN 1
WHEN B.weekdayval = 4 AND day_4 THEN 1
WHEN B.weekdayval = 5 AND day_5 THEN 1
WHEN B.weekdayval = 6 AND day_6 THEN 1
WHEN B.weekdayval = 7 AND day_7 THEN 1
ELSE 0 END) AS adj_total_frequency
actually this could be better; it could theoretically mean B.weekdayval only gets compared once per row (I say theoretically because MySQL does not guarantee irrelevant THEN clauses will not be evaluated, just not "returned" from the CASE).
SUM(CASE WHEN day_1 THEN B.weekdayval = 1
WHEN day_2 THEN B.weekdayval = 2
WHEN day_3 THEN B.weekdayval = 3
WHEN day_4 THEN B.weekdayval = 4
WHEN day_5 THEN B.weekdayval = 5
WHEN day_6 THEN B.weekdayval = 6
WHEN day_7 THEN B.weekdayval = 7
ELSE 0 END) AS adj_total_frequency
Edit: As far as the datesub method goes, I don't have the time to write a full solution, but to start you (or other potential answerers) on that...
I meant DATEDIFF
you can get the number of whole weeks between the start and end with DATEDIFF(end, start) DIV 7
multiply that by the number of days in a week that apply to get an approximation
then (the hardest part), figure out the number of days to add for the fractional week not covered by div.
(Sometimes) MySQL has big troubles optimizing GROUP BY statements with a JOIN. To overcome that you can store the joined result into a temporary table so you can use GROUP BY with one table.
drop temporary table if exists tmp;
create temporary table tmp (id int unsigned not null)
engine=myisam
select i.id
from input i
straight_join dates B
on B.datevalue >= i.`start`
and B.datevalue < i.`end`
where (
(WEEKDAY(B.datevalue ) = 0) AND i.day_7 OR
(WEEKDAY(B.datevalue ) = 1) AND i.day_1 OR
(WEEKDAY(B.datevalue ) = 2) AND i.day_2 OR
(WEEKDAY(B.datevalue ) = 3) AND i.day_3 OR
(WEEKDAY(B.datevalue ) = 4) AND i.day_4 OR
(WEEKDAY(B.datevalue ) = 5) AND i.day_5 OR
(WEEKDAY(B.datevalue ) = 6) AND i.day_6
)
-- and i.id > 000000
-- and i.id <= 100000
;
drop temporary table if exists tmp1;
create temporary table tmp1 (id int unsigned not null, cnt int unsigned not null)
engine=myisam
select id, count(1) as cnt
from tmp
group by id
;
update input i
join tmp1 using(id)
set i.numdays = tmp1.cnt
where 1=1;
My test data contains 1M rows with random day bits (round(rand())) and an average date range of 50 days. So the tmp table contains about 25M rows.
On my system it takes about 500 msec for 10K rows, 5 sec for 100K rows and 2 mins for 1M rows. So if you split the updates in chunks of 100K rows (using the commented id range condition in the first statement) you should be ready in about 30 minutes.

Getting first week of date in mysql

I have a lot of table and i need to get the gross income of a movie, now my problem is i don't know how to get the sum of first week only of a movie.
This is what i need.
+-------------------------------------------+
| title | Week one | Week one |
| | (Wed-Sun) | (Mon-Tue) |
+-------------------------------------------+
| title 1 | 50000 | 10000 |
+-------------------------------------------+
If the starting show of a movie is wed then i should make 3 column, first column is title, second column is the wed-sun and third is mon-tue.
Is this possible to query like select movie, sum(wed-sun), sum(mon-tue)
Thanks in advance
This is my answer based on how I understand your question.
SELECT movie, sum(wed-sun), sum(mon-tue) CONVERT(date, getdate()) as day
FROM thetable
WHERE thedate(BETWEEN first AND last)
GROUP BY day
You can user DAYOFWEEK() if you are using date type for that. See http://dev.mysql.com/doc/refman/5.1/en/date-and-time-functions.html#function_dayofweek
For week days from mon-tue
SUM(CASE WHEN DAYOFWEEK(DATE)=2 THEN 1 WHEN DAYOFWEEK(DATE)=3 THEN 1 ELSE 0 END)
For week days from wed-sun
SUM(CASE WHEN DAYOFWEEK(DATE)=4 THEN 1 WHEN DAYOFWEEK(DATE)=5 THEN 1 WHEN DAYOFWEEK(DATE)=6 THEN 1 WHEN DAYOFWEEK(DATE)=7 THEN 1 WHEN DAYOFWEEK(DATE)=1 THEN 1 ELSE 0 END)
If you use WEEKDAY instead of DAYOFWEEK you can shorten the case statements:
SELECT
movie_id,
title,
SUM(CASE WHEN WEEKDAY(date_field) < 2 THEN field_to_sum ELSE 0 END) `mon-tue`,
SUM(CASE WHEN WEEKDAY(date_field) > 1 THEN field_to_sum ELSE 0 END) `wed-sun`
FROM
movies
/* optional WHERE */
GROUP_BY movie_id
Obviously you want WEEKDAY FUNCTION, it returns the weekday index starting from 0-Monday.
Assume you have table Movies with title and starting_show_date columns, and value_table with action_date and amount columns.
You can sum amount by splitting amounts to two parts like this:
select
movies.title,
sum(case when value_table.action_date
< dateadd(movies.starting_show_date , interval 7 -WEEKDAY(movies.starting_show_date) day)
then value_table.amount else 0 end) as FirstWeek,
sum(case when value_table.action_date
>= dateadd(movies.starting_show_date , interval 7 -WEEKDAY(movies.starting_show_date) day)
then value_table.amount else 0 end) as OtherWeeks
from
movies
inner join
value_table
on
movies.id = value_table.movie_id
group by
movies.title

MySQL - get COUNT depends on the value

How can I get the COUNT() of the specific field depends on the value of the field? For example I have the field typeOfAssistance , in the query below I got the total numbers of the typeOfAssistance but I have different values in it which is financial medical and burial, How can I add custom column that will divide the total value depends on the value?
SELECT date,COUNT(*) AS num
FROM requests
WHERE date BETWEEN DATE_ADD(CURDATE(),INTERVAL -20 DAY) AND CURDATE()
GROUP BY date
desired output:
date | financial | burial | medical | total
2014-04-25 | 1 | 2 | 3 | 6
Thanks. Sorry for the explanation. :)
Typically for something like that I would use SUM rather than COUNT for the item breakdowns.
Something like
SELECT date,
SUM(CASE WHEN typeOfAssistance = 'financial' THEN 1 ELSE 0 END) AS financial,
SUM(CASE WHEN typeOfAssistance = 'burial' THEN 1 ELSE 0 END) AS burial,
SUM(CASE WHEN typeOfAssistance = 'medical' THEN 1 ELSE 0 END) AS medical,
COUNT(1) AS Total
FROM requests
GROUP BY date