Weekly report behalf of specific date field - mysql

I am using this query for weekly reporting but can not found a way like this
week_number | week_startdate | organization_1 | organization_2
---------------------------------------------------------------
1 | 2013-01--05 |count(date) like 4,24,etc_ | count(date) like 4,24,etc_
SQL:
SELECT WEEK(signed_date) AS week_name, signed_date AS Week_Starting,
YEAR(signed_date), WEEK(signed_date), COUNT(*)
FROM business
WHERE YEAR(signed_date) = YEAR(CURDATE())
GROUP BY CONCAT(YEAR(signed_date), '/', WEEK(signed_date))
ORDER BY YEAR(signed_date), WEEK(signed_date
SAMPLE DATA:
signed_date | organization_id
01-01-2013 | 1
02-01-2013 | 1
03-01-2013 | 2
In 1 week organization_1 have 2 signed & organization_2 has 1 signed.

You should use case within count or sum:
SELECT WEEK(signed_date) AS week_name, signed_date AS Week_Starting,
YEAR(signed_date), WEEK(signed_date),
SUM(CASE WHEN organization_id=1 THEN 1 ELSE 0 END) as organization_1,
SUM(CASE WHEN organization_id=2 THEN 1 ELSE 0 END) as organization_2
FROM business
WHERE YEAR(signed_date) = YEAR(CURDATE())
GROUP BY CONCAT(YEAR(signed_date), '/', WEEK(signed_date))
ORDER BY YEAR(signed_date), WEEK(signed_date);
http://sqlfiddle.com/#!2/587ad/3

Related

How do I aggregate from multiple rows

I have the below table:
Person_ID | Timestamp | Action
1 | 01-01-2020 00:01:00 | LOGGED ON
1 | 01-01-2020 00:02:00 | ON BREAK
1 | 01-01-2020 00:02:30 | OFF BREAK
1 | 01-01-2020 00:04:00 | LOGGED OFF
1 | 01-01-2020 00:04:30 | LOGGED ON
1 | 01-01-2020 00:05:30 | ON BREAK
1 | 01-01-2020 00:06:00 | OFF BREAK
1 | 01-01-2020 00:08:00 | LOGGED OFF
2 | 01-02-2020 00:04:00 | LOGGED ON
2 | 01-02-2020 00:10:00 | LOGGED OFF
My goal is to write an SQL view to get the below result:
Person_ID | LoggedInTime_hours | OnBreakTime_hours
1 | 6.5 | 1
2 | 6 | 0
For each Person_ID, calculate the difference in hours between each pair of occurrences of LOGGED IN/LOGGED OUT and ON BREAK/OFF BREAK respectively.
Thanks a lot for your expertise and help.
1st of all LoggedInTime_hours for PERSON_ID = 1 should be 5.5 hours not 6.5 hours.
Let us assume the table name is TEST.
Database : MySQL
SELECT
PERSON_ID,
IFNULL(SUM(CASE WHEN PREV_ACTIVE_IND - ACTIVE_IND > 0 THEN TIMESTAMPDIFF(SECOND, PREV_TIMESTAMP, TIMESTAMP)/3600 END),0) LoggedInTime_hours,
IFNULL(SUM(CASE WHEN PREV_ACTIVE_IND - ACTIVE_IND = 0 THEN TIMESTAMPDIFF(SECOND, PREV_TIMESTAMP, TIMESTAMP)/3600 END),0) OnBreakTime_hours
FROM
(SELECT
PERSON_ID,
PREV_ACTION,
ACTION,
CASE WHEN PREV_ACTION = 'LOGGED OFF' THEN 1
WHEN PREV_ACTION LIKE '%BREAK' THEN 2
WHEN PREV_ACTION IS NULL THEN NULL
ELSE 3
END AS PREV_ACTIVE_IND,
CASE WHEN ACTION = 'LOGGED OFF' THEN 1
WHEN ACTION LIKE '%BREAK' THEN 2
WHEN ACTION IS NULL THEN NULL
ELSE 3
END AS ACTIVE_IND,
PREV_TIMESTAMP,
TIMESTAMP
FROM
(SELECT PERSON_ID,
TIMESTAMP,
LAG(TIMESTAMP,1) OVER (PARTITION BY PERSON_ID) PREV_TIMESTAMP,
LAG(ACTION,1) OVER (PARTITION BY PERSON_ID) PREV_ACTION,
ACTION
FROM TEST) T) T2
GROUP BY PERSON_ID
Hope this query will help.
Try the following query using MySQL v8.0:
With CTE As
(
Select Person_ID, Action_,
TIME_TO_SEC(
TIMEDIFF(
Lead(Timestamp_) Over (Partition By Person_ID
Order By Case When Action_ In 'LOGGED ON','LOGGED OFF')
Then 1 Else 2 End,Timestamp_), Timestamp_
))/3600 As DIFF
From MyData
)
Select Person_ID,
Sum(Case When Action_='LOGGED ON' Then DIFF Else 0 End) As LoggedInTime_hours,
Sum(Case When Action_='ON BREAK' Then DIFF Else 0 End) As OnBreakTime_hours
From CTE
Group By Person_ID
See a demo from db-fiddle.
The Lead() function will find the next action date related to the current action i.e. (Logged In -> Logged Off), the actions are ordered according to their types; ('LOGGED ON','LOGGED OFF') ordered with 1, and ('ON BREAK','OFF BREAK') ordered with 2, then ordered by Timestamp of the action. Doing so, will ensure that each 'LOGGED ON' is followed by it's 'LOGGED OFF' action, and each 'ON BREAK' is followed by it's 'OFF BREAK' action.
Using TIMEDIFF we can find the difference between the actions times for each pair of actions ('LOGGED ON','LOGGED OFF'), ('ON BREAK','OFF BREAK'). Then the sum of these differences will be LoggedInTime_hours for logged in actions, and OnBreakTime_hours for on break actions.
The TIME_TO_SEC()/3600 is used to convert time differences to hours.

number of visitors gained and lost comparing with previous day - sql

CREATE TABLE visitors (
Date DATE,
visitor VARCHAR(20));
INSERT INTO visitors VALUES
("2019-10-01", "v1"),
("2019-10-01", "v2"),
("2019-10-01", "v3"),
("2019-10-02", "v2"),
("2019-10-03", "v2"),
("2019-10-03", "v4"),
("2019-10-03", "v5");
please find Find number_of_visitors_per_date, number_of_visitors gained comparing with previous day, and number_of_visitors lost comparing with previous day. The expected results for the above table should be:
Date | number_of_visitors | number_of_visitors_gained | number_of_visitors_lost
2019-10-01 | 3 | 3 | 0
2019-10-02 | 1 | 0 | 2
2019-10-03 | 3 | 2 | 0
Obviously, the most challenging part is how to get the last two columns. Note, since there is no previous day for the first day, the number_of_visitors_gained is the total number of visitors in the first day, and the number_of_visitors_lost is 0.
If you RDBMS supports window functions, you can aggregate, and then use lag():
select
date,
number_of_visitors,
case
when lag(number_of_visitors) over(order by date) is null
then number_of_visitors
when lag(number_of_visitors) over(order by date) < number_of_visitors
then number_of_visitors - lag(number_of_visitors) over(order by date)
else 0
end number_of_visitors_gained,
case when lag(number_of_visitors) over(order by date) > number_of_visitors
then lag(number_of_visitors) over(order by date) - number_of_visitors
else 0
end number_of_visitors_lost
from (
select date, count(*) number_of_visitors
from visitors
group by date
) t
order by date
Demo on DB Fiddle:
date | number_of_visitors | number_of_visitors_gained | number_of_visitors_lost
:--------- | -----------------: | ------------------------: | ----------------------:
2019-10-01 | 3 | 3 | 0
2019-10-02 | 1 | 0 | 2
2019-10-03 | 3 | 2 | 0
You are looking for aggregation using lead() and lag() window functions:
select date, count(*) as visitors_on_day,
sum(case when prev_vdate is null or prev_vdate <> date - interval 1 day
then 1 else 0
end) as gained_visitors,
sum(case when next_vdate <> date + interval 1 day
then 1 else 0
end) as lost_visitors
from (select v.*,
lag(date) over (partition by visitor) as prev_vdate,
lead(date) over (partition by visitor) as next_vdate
from visitors v
) t
group by date
order by date;

How to Query Maximum Integer Value from Substring of Varchar Column with Condition

I want to get the total maximum number of column CODE which the maximum is defined by the last five digits from mybarcode column.
mybarcode | code | judge | create_date |
-------------+------+--------+-------------+
M71X400001 | 7 | pass |
M71X400002 | 7 | pass |
M71X400005 | 7 | pass |
M71X400010 | 7 | pass |
M81X400001 | 8 | pass |
M81X400002 | 8 | pass |
M81X400007 | 8 | pass |
M91X400001 | 9 | pass |
M91X400003 | 9 | pass |
```
Example:
>The maximum value of 7 from CODE column is 10 ( from M71X4'00010')
>The maximum value of 8 from CODE column is 7 ( from M81X4'00007')
>The maximum value of 9 from CODE column is 3 ( from M91X4'00003')
The result should be 10+7+3=20.
And want display in the result table below.
```
SELECT DAY,
SUM(CASE WHEN judge = 'pass' then 1 else 0 end) pass,
SUM(CASE WHEN judge = 'fail' then 1 else 0 end) fail
**??? as number**
from MYTABLE
where MONTH(create_date) = '04' and YEAR(create_date) = '2019'
GROUP BY DAY
Result Table
day | pass | fail | number |
--------+------+--------+----------+
1 | 9 | 0 | 20 |
2 | 9 | 0 | ?? |
3 | 9 | 0 | ?? |
I think you need to do group by two times. Please try below code -
For MySQL -
SELECT
DAY,
SUM(pass),
SUM(fail),
SUM(max_barcode)
FROM (
SELECT
DAY,
SUM(CASE WHEN judge = 'pass' then 1 else 0 end) pass,
SUM(CASE WHEN judge = 'fail' then 1 else 0 end) fail,
Code,
CAST(MAX(SUBSTRING(mybarcode, 5)) AS SIGNED) AS max_barcode
FROM MYTABLE
WHERE MONTH(create_date) = '%s' and YEAR(create_date) = '%s'
GROUP BY DAY, Code
) AS CTE
GROUP BY DAY;
FOR MS SQL Server -
;WITH CTE AS (
SELECT
DAY,
SUM(CASE WHEN judge = 'pass' then 1 else 0 end) pass,
SUM(CASE WHEN judge = 'fail' then 1 else 0 end) fail,
Code,
max_barcode = cast(max(right(mybarcode, 5)) as int)
FROM MYTABLE
WHERE MONTH(create_date) = '%s' and YEAR(create_date) = '%s'
GROUP BY DAY, Code
)
SELECT
DAY,
SUM(pass),
SUM(fail),
SUM(max_barcode)
FROM CTE
GROUP BY DAY;

mysql table data rows to column

I'm trying to achieve yearly data as pivot, this is my query
SELECT
month(te.topup_date) as month,
sum(CASE WHEN year(te.topup_date) = '2015' THEN te.topup_amount+ tp.topup_amount END) as '2015',
sum(CASE WHEN year(te.topup_date) = '2016' THEN te.topup_amount+ tp.topup_amount END) as '2016'
from te_daily_topup as te
inner join tp_daily_topup as tp on year(te.topup_date) = year(tp.topup_date)
where year(te.topup_date) between '2015' and '2016'
group by year(te.topup_date), month(te.topup_date)
The result from the above query
month | 2015 | 2016
--------------------
1 | 123 |
2 | 2343 |
1 | |234
2 | |7667
The result I'm looking for is below:
month | 2015 | 2016
--------------------
1 | 123 | 234
2 | 2343 | 7667
help me to modify this query..
Just remove year() from the GROUP BY:
select month(te.topup_date) as month,
sum(case when year(te.topup_date) = 2015 then te.topup_amount + tp.topup_amount end) as `2015`,
sum(case when year(te.topup_date) = 2016 then te.topup_amount + tp.topup_amount end) as `2016`
from te_daily_topup te inner join
tp_daily_topup tp
on year(te.topup_date) = year(tp.topup_date)
where year(te.topup_date) between 2015 and 2016
group by month(te.topup_date);
Note: Do not use single quotes for integer constants nor for column aliases. Only use single quotes for string and date constants.

SQL: Grouped average-if / case SELECT statement

I have a database that looks like this SQL Fiddle: http://sqlfiddle.com/#!9/aa02e/1
CREATE TABLE Table1
(`Store` varchar(1), `Date` date, `Product` varchar(2), `Weekday` int, `Month` int, `Revenue` float)
;
INSERT INTO Table1
(`Store`, `Date`, `Product`, `Weekday`, `Month`, `Revenue`)
VALUES
('a', '20160101', 'aa', 5, 1, 1.5),
('a', '20160101', 'bb', 5, 1, 4),
('a', '20160101', 'cc', 5, 1, 3.5),
('a', '20160108', 'dd', 5, 1, 2.5),
('a', '20160108', 'ee', 5, 1, 5),
('b', '20160204', 'aa', 4, 2, 9.5),
('b', '20160204', 'bb', 4, 2, 4),
('b', '20160204', 'cc', 4, 2, 3),
('b', '20160211', 'dd', 4, 2, 1.5),
('b', '20160211', 'ee', 4, 2, 2.5)
;
SELECT * FROM table1;
+-------+------------+---------+---------+-------+---------+
| Store | Date | Product | Weekday | Month | Revenue |
+-------+------------+---------+---------+-------+---------+
| a | 2016-01-01 | aa | 5 | 1 | 1.5 |
| a | 2016-01-01 | bb | 5 | 1 | 4 |
| a | 2016-01-01 | cc | 5 | 1 | 3.5 |
| a | 2016-01-08 | dd | 5 | 1 | 2.5 |
| a | 2016-01-08 | ee | 5 | 1 | 5 |
| b | 2016-02-04 | aa | 4 | 2 | 9.5 |
| b | 2016-02-04 | bb | 4 | 2 | 4 |
| b | 2016-02-04 | cc | 4 | 2 | 3 |
| b | 2016-02-11 | dd | 4 | 2 | 1.5 |
| b | 2016-02-11 | ee | 4 | 2 | 2.5 |
+-------+------------+---------+---------+-------+---------+
It shows revenue data for stores incl. products, date and the respective day/month.
I want to select the following:
Store
Monthly revenue totals (i.e. what is the total revenue for store a in Jan?)
Weekday revenue averages (i.e. what is the avg revenue for store a on Thu?)
The first and second bullet are straightforward, but I'm having problems with the last one.
Currently, it takes the average over all products and all dates (assuming the weekday matches). What I need are the following steps:
Sum up all revenues for a store and a particular date (e.g. for store b: 9.5+4+3=16.5 for Feb 4th, and 1.5+2.5=4 for Feb 11th) if that date has the same weekday (here Thursday)
Take the average of the two values (e.g. avg(16.5,4)=10.25)
How can I accomplish that?
Thank you
Here is the query:
SELECT
Store,
SUM(CASE WHEN Month = 1 THEN Revenue ELSE NULL END) AS REVENUE_JAN,
SUM(CASE WHEN Month = 2 THEN Revenue ELSE NULL END) AS REVENUE_FEB,
AVG(CASE WHEN Weekday = 4 THEN Revenue ELSE NULL END) AS REVENUE_THU,
AVG(CASE WHEN Weekday = 5 THEN Revenue ELSE NULL END) AS REVENUE_FRI
FROM Table1
GROUP BY
Store
;
The weekday average is tricky. Your query is getting the average "order size" per weekday. But you want the total revenue.
One method is to first aggregate by weekday, but that is a bit of a mess. Instead, you can use this trick of calculating the average by dividing the total revenue by the number of days:
SELECT Store,
SUM(CASE WHEN Month = 1 THEN Revenue ELSE NULL END) AS REVENUE_JAN,
SUM(CASE WHEN Month = 2 THEN Revenue ELSE NULL END) AS REVENUE_FEB,
(SUM(CASE WHEN Weekday = 4 THEN Revenue END) /
COUNT(DISTINCT CASE WHEN Weekday = 4 THEN Date END)
) AS REVENUE_THU,
(SUM(CASE WHEN Weekday = 5 THEN Revenue END) /
COUNT(DISTINCT CASE WHEN Weekday = 5 THEN Date END)
) AS REVENUE_FRI
FROM Table1
GROUP BY Store;
SELECT
t1.store,
SUM(CASE WHEN Month = 1 THEN Revenue ELSE NULL END) AS REVENUE_JAN,
SUM(CASE WHEN Month = 2 THEN Revenue ELSE NULL END) AS REVENUE_FEB,
daily.REVENUE_THU,
daily.REVENUE_FRI
FROM Table1 t1
JOIN (
SELECT
Store,
weekday,
avg(CASE WHEN weekday = 4 THEN sum_rev END) as REVENUE_THU,
avg(CASE WHEN weekday = 5 THEN sum_rev END) as REVENUE_FRI
FROM (
SELECT
Store, date, weekday,
SUM(revenue) AS sum_rev
FROM Table1
GROUP BY
Store, date, weekday
) AS foo
GROUP BY Store, weekday
) AS daily ON daily.store = t1.store
GROUP BY
t1.store
How about this solution it return average for chosen day of chosen store
CREATE PROCEDURE sumForDayStore(IN vday INTEGER, IN vStore VARCHAR(50))
BEGIN
DECLARE totalDays INTEGER;
DECLARE totalRevenu INTEGER;
SET totalDays = (SELECT count(*) FROM Table1 WHERE WeekDay = vDay AND store = vStore);
SET totalRevenu = (SELECT sum(Revenue) FROM Table1 WHERE WeekDay = vDay AND store = vStore);
SELECT totalRevenu/totalDays;
END;
CALL sumForDayStore(5,'a');
How about this one:
SELECT mnth.Store, REVENUE_JAN, REVENUE_FEB, avg(rthu) REVENUE_THU, avg(rfri) REVENUE_FRI
FROM
(Select Store, sum(case when Month = 1 then Revenue else NULL END) REVENUE_JAN,
sum(case when Month = 2 then Revenue else NULL END) REVENUE_FEB
From Table1 group by Store) as mnth
join
(Select Store, sum(case when Weekday = 4 then Revenue end) rThu,
sum(case when Weekday = 5 then Revenue end) rFri from Table1 group by Store, Date) as dys
on mnth.Store = dys.Store
group by mnth.Store, REVENUE_JAN, REVENUE_FEB
I compared the performance of this with the query in the first answer and it shows better performance according to SQL server execution plan (1.6 times faster). Maybe this would be helpful on a larger data set.