Get records between two time values in MS Acess - ms-access

I have a datetime field. I want to get the records between 9AM and 5PM also I need the records between 5PM AND 9AM. If I am using between operator it gives me the same number of records.
SELECT count(*)
FROM DirectLineMainCallQuery AS CallTbl
Where Format(CallTbl.CallDate,"dddd") <> 'Saturday' AND Format(CallTbl.CallDate,"dddd") <> 'Sunday'
AND (Format(CallTbl.StartTime,'hh:mm') Between '09:00' AND '17:00')
UNION ALL
SELECT count(*)
FROM DirectLineMainCallQuery AS CallTbl
Where Format(CallTbl.CallDate,"dddd") <> 'Saturday' AND Format(CallTbl.CallDate,"dddd") <> 'Sunday'
AND (Format(CallTbl.StartTime,'hh:mm') Between '17:00' AND '09:00') ;
Any help would be appreciated.
Thanks

AK47 has the correct comment, of needing two time ranges, the second one being either 1700 to midnight, or 0000 to 0900, as below-- Notice the extra pair of parens that enclose the last two betweens...
SELECT Count(*)
FROM directlinemaincallquery AS CallTbl
WHERE Format(CallTbl.calldate, "dddd") <> 'Saturday'
AND Format(CallTbl.calldate, "dddd") <> 'Sunday'
AND ( Format(CallTbl.starttime, 'hh:mm') BETWEEN '09:00' AND '17:00' )
UNION ALL
SELECT Count(*)
FROM directlinemaincallquery AS CallTbl
WHERE Format(CallTbl.calldate, "dddd") <> 'Saturday'
AND Format(CallTbl.calldate, "dddd") <> 'Sunday'
AND (( Format(CallTbl.starttime, 'hh:mm') BETWEEN '17:00' AND '23:59' )
OR ( Format(CallTbl.starttime, 'hh:mm') BETWEEN '00:00' AND '09:00' ));
Edited 4/6 nite
You said -- If I am using between operator it gives me the same number of records
And MSACCESS agrees with you... Between 9 and 17 is the same as Between 17 and 9
I created this test, and got this result----
SELECT table1.*
FROM table1
Where Frame between '8' and '3'
Frame
3
4
5
6
7
8
MSACCESS does not care that the "larger" is before the "smaller", but rather gives you BETWEEN the smaller and the larger of the two values. While you may think that it should "Do What I Mean", it cannot. The way to ask it is to make two Betweens for 17--thru--2359 and 0000 thru 0900 as shown in my example, or to use Greater/Lessor signs (>= <= ).

Please try following code,
SELECT count(*)
FROM DirectLineMainCallQuery AS CallTbl
Where Format(CallTbl.CallDate,"dddd") <> 'Saturday' AND Format(CallTbl.CallDate,"dddd") <> 'Sunday'
AND (Format(CallTbl.StartTime,'hh:mm') >= '09:00' AND (CallTbl.StartTime,'hh:mm') < '17:00')
Union All
SELECT count(*)
FROM DirectLineMainCallQuery AS CallTbl
Where Format(CallTbl.CallDate,"dddd") <> 'Saturday' AND Format(CallTbl.CallDate,"dddd") <> 'Sunday'
AND (Format(CallTbl.StartTime,'hh:mm') >= '17:00' AND (CallTbl.StartTime,'hh:mm') < '9:00')

Related

MYSQL SUM until last day of Each month for last 12 months

I have a table like this two
Table A
date amount B_id
'2020-1-01' 3000000 1
'2019-8-01' 15012 1
'2019-6-21' 90909 1
'2020-1-15' 84562 1
--------
Table B
id type
1 7
2 5
I have to show sum of amount until the last date of each month for the last 12 month.
The query i have prepared is like this..
SELECT num2.last_dates,
(SELECT SUM(amount) FROM A
INNER JOIN B ON A.B_id = B.id
WHERE B.type = 7 AND A.date<=num2.last_dates
),
(SELECT SUM(amount) FROM A
INNER JOIN B ON A.B_id = B.id
WHERE B.type = 5 AND A.date<=num2.last_dates)
FROM
(SELECT last_dates
FROM (
SELECT LAST_DAY(CURDATE() - INTERVAL CUSTOM_MONTH MONTH) last_dates
FROM(
SELECT 1 CUSTOM_MONTH UNION
SELECT 0 UNION
SELECT 2 UNION
SELECT 3 UNION
SELECT 4 UNION
SELECT 5 UNION
SELECT 6 UNION
SELECT 7 UNION
SELECT 8 UNION
SELECT 9 UNION
SELECT 10 UNION
SELECT 11 UNION
SELECT 12 )num
) num1
)num2
ORDER BY num2.last_dates
This gives me the result like this which is exactly how i need it. I need this query to execute faster. Is there any better way to do what i am trying to do?
2019-05-31 33488.69 109.127800
2019-06-30 263.690 1248932.227800
2019-07-31 274.690 131.827800
2019-08-31 627.690 13.687800
2019-09-30 1533.370000 08.347800
2019-10-31 1444.370000 01.327800
2019-11-30 5448.370000 247.227800
2019-12-31 61971.370000 016.990450
2020-01-31 19550.370000 2535.185450
2020-02-29 986.370000 405.123300
2020-03-31 1152.370000 26.793300
2020-04-30 9404.370000 11894.683300
2020-05-31 3404.370000 17894.683300
I'd use conditional aggregation, and pre-aggregate the monthly totals in one pass, instead of doing twenty-six individual passes repeatedly through the same data.
I'd start with something like this:
SELECT CASE WHEN A.date < DATE(NOW()) + INTERVAL -14 MONTH
THEN LAST_DAY( DATE(NOW()) + INTERVAL -14 MONTH )
ELSE LAST_DAY( A.date )
END AS _month_end
, SUM(IF( B.type = 5 , B.amount , NULL)) AS tot_type_5
, SUM(IF( B.type = 7 , B.amount , NULL)) AS tot_type_7
FROM A
JOIN B
ON B.id = A.B_id
WHERE B.type IN (5,7)
GROUP
BY _month_end
(column amount isn't qualified in original query, so just guessing here which table that is from. adjust as necessary. best practice is to qualify all column references.
That gets us the subtotals for each month, in a single pass through A and B.
We can get that query tested and tuned.
Then we can incorporate that as an inline view in an outer query which adds up those monthly totals. (I'd do an outer join, just in case rows are missing, sow we don't wind up omitting rows.)
Something like this:
SELECT d.dt + INTERVAL -i.n MONTH + INTERVAL -1 DAY AS last_date
, SUM(IFNULL(t.tot_type_5,0)) AS rt_type_5
, SUM(IFNULL(t.tot_type_7,0)) AS rt_type_7
FROM ( -- first day of next month
SELECT DATE(NOW()) + INTERVAL -DAY(DATE(NOW()))+1 DAY + INTERVAL 1 MONTH AS dt
) d
CROSS
JOIN ( -- thirteen integers, integers 0 thru 12
SELECT 0 AS n
UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8
UNION ALL SELECT 9 UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12
) i
LEFT
JOIN ( -- totals by month
SELECT CASE WHEN A.date < DATE(NOW()) + INTERVAL -14 MONTH
THEN LAST_DAY( DATE(NOW()) + INTERVAL -14 MONTH )
ELSE LAST_DAY( A.date )
END AS _month_end
, SUM(IF( B.type = 5 , B.amount , NULL)) AS tot_type_5
, SUM(IF( B.type = 7 , B.amount , NULL)) AS tot_type_7
FROM A
JOIN B
ON B.id = A.B_id
WHERE B.type IN (5,7)
GROUP
BY _month_end
) t
ON t._month_end < d.dt
GROUP BY d.dt + INTERVAL -i.n MONTH + INTERVAL -1 DAY
ORDER BY d.dt + INTERVAL -i.n MONTH + INTERVAL -1 DAY DESC
The design is meant to do one swoop through the A JOIN B set. We're expecting to get about 14 rows back. And we're doing a semi-join, duplicating the oldest months multiple times, so approx . 14 x 13 / 2 = 91 rows, that get collapsed into 13 rows.
The big rock in terms of performance is going to be materializing that inline view query.
This is how I'd probably approach this in MySQL 8 with SUM OVER:
Get the last 12 months.
Use these months to add empty month rows to the original data, as MySQL doesn't support full outer joins.
Get the running totals for all months.
Show only the last twelve months.
The query:
with months (date) as
(
select last_day(current_date - interval 1 month) union all
select last_day(current_date - interval 2 month) union all
select last_day(current_date - interval 3 month) union all
select last_day(current_date - interval 4 month) union all
select last_day(current_date - interval 5 month) union all
select last_day(current_date - interval 6 month) union all
select last_day(current_date - interval 7 month) union all
select last_day(current_date - interval 8 month) union all
select last_day(current_date - interval 9 month) union all
select last_day(current_date - interval 10 month) union all
select last_day(current_date - interval 11 month) union all
select last_day(current_date - interval 12 month)
)
, data (date, amount, type) as
(
select last_day(a.date), a.amount, b.type
from a
join b on b.id = a.b_id
where b.type in (5, 7)
union all
select date, null, null from months
)
select
date,
sum(sum(case when type = 5 then amount end)) over (order by date) as t5,
sum(sum(case when type = 7 then amount end)) over (order by date) as t7
from data
group by date
order by date
limit 12;
Demo: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=ddeb3ab3e086bfc182f0503615fba74b
I don't know whether this is faster than your own query or not. Just give it a try. (You'd get my query much faster by adding a generated column for last_day(date) to your table and use this. If you need this often, this may be an option.)
You are getting some complicated answers. I think it is easier. Start with knowing we can easily sum for each month:
SELECT SUM(amount) as monthtotal,
type,
MONTH(date) as month,
YEAR(date) as year
FROM A LEFT JOIN B on A.B_id=B.id
GROUP BY type,month,year
From that data, we can use a variable to get running total. Best to do by initializing the variable, but not necessary. We can get the data necessary like this
SET #running := 0;
SELECT (#running := #running + monthtotal) as running, type, LAST_DAY(CONCAT(year,'-',month,'-',1))
FROM
(SELECT SUM(amount) as monthtotal,type,MONTH(date) as month,YEAR(date) as year FROM A LEFT JOIN B on A.B_id=B.id GROUP BY type,month,year) AS totals
ORDER BY year,month
You really need to have a connector that supports multiple statements, or make multiple calls to initialize the variable. Although you can null check the variable and default to 0, you still have an issue if you run the query a second time.
Last thing, if you really want the types to be summed separately:
SET #running5 := 0;
SET #running7 := 0;
SELECT
LAST_DAY(CONCAT(year,'-',month,'-',1)),
(#running5 := #running5 + (CASE WHEN type=5 THEN monthtotal ELSE 0 END)) as running5,
(#running7 := #running7 + (CASE WHEN type=7 THEN monthtotal ELSE 0 END)) as running7
FROM
(SELECT SUM(amount) as monthtotal,type,MONTH(date) as month,YEAR(date) as year FROM A LEFT JOIN B on A.B_id=B.id GROUP BY type,month,year) AS totals
ORDER BY year,month
We still don't show months where there is no data. I'm not sure that is a requirement. But this should only need one pass of table A.
Also, make sure the id on table B is indexed.

Prioritizing case in mysql

I want to prioritize the status part. Everything which is not repported should appear first. Regardless date. But now, It orders eveything after date. How can I prioritize the first case 'not repported part'? The rest of the list should appear after date.
SELECT * FROM list
ORDER BY date DESC,
CASE
WHEN `bookings`.`status` = 'Not repported' THEN 1
WHEN day= 'Monday' THEN 2
WHEN day= 'Tuesday' THEN 3
WHEN day= 'Wednesday' THEN 4
WHEN day= 'Thursdau' THEN 5
WHEN day= 'Friday' THEN 6
WHEN day= 'Saturday' THEN 7 WHEN
day= 'Sunday' THEN 8 END ASC
limit 1,20 ";
I want something like this.
------------------------------
DATE----DAY-------STATUS------
2011---Monday-----Not reported
2015---Sunday-----Not reported
2010---Wednedday--Not reported
2016---Monday---------Reported
2015---Monday---------Reported
2014---Tuesday--------Reported
2013---Sunday---------Reported
------------------------------
You have two order statements. One for the date, and one for your booking status. Mysql will first order according to your first order statement, and then according to your second order statement. So switching the order statements solves your problem. In addition there is the "ELSE" keyword for "CASE"s in mysql.
In total I would write it like this:
SELECT *
FROM list
ORDER BY
CASE
WHEN `bookings`.`status` = 'Not repported' THEN 1
ELSE 2
END ASC,
date DESC
LIMIT 1,20
(Im not so sure where your "bookings" table now comes from, but I left it there. You might miss a JOIN clause)
Change the order by
Something like this
SELECT * FROM list
ORDER BY
CASE
WHEN `bookings`.`status` = 'Not repported' THEN 1 ELSE 2
END,
CASE
WHEN day= 'Monday' THEN 2
WHEN day= 'Tuesday' THEN 3
WHEN day= 'Wednesday' THEN 4
WHEN day= 'Thursdau' THEN 5
WHEN day= 'Friday' THEN 6
WHEN day= 'Saturday' THEN 7 WHEN
day= 'Sunday' THEN 8 END ASC,
date DESC
limit 1,20 ";
This will give you this result
'Not repported' , 1/1/2017
'Not repported' , 3/7/2017
'Not repported' , 15/8/2017
Monday , 6/2/2017
Monday , 11/11/2017

how to group by week start from friday

I have table sql like this:
This my query for count tgl:
SELECT count( tgl ) AS total, absen.id
FROM absen
WHERE absen.status = 'm'
GROUP BY absen.id
So I want group by absen.id and absen.tgl
How to group by week from Friday to Thursday?
2016-01-08 is friday and 2016-01-15 is thursday.
Bellow query can bring the result you want, but i think you defined the wrong end date, because in your example from 2015-01-08 up to 2015-01-15 its 8 day and one week has 7 days.
select
count( tgl ) AS total,
absen.id,
CASE WHEN (weekday(tgl)<=3) THEN date(tgl + INTERVAL (3-weekday(tgl)) DAY)
ELSE date(tgl + INTERVAL (3+7-weekday(tgl)) DAY)
END as week_days
FROM absen
WHERE status = 'm'
GROUP BY id,week_days
here is the fiddle fiddle
Query Description:
mysql weekday array numbers:
$weekArr = array(
'Monday' => 0,
'Tuesday' => 1,
'Wednesday' => 2,
'Thursday' => 3,
'Friday' => 4,
'Saturday' => 5,
'Sunday' => 6);
So now suppose today is Tuesday and date is 2016-01-12, now let's count from today towards the start date in our table which is 2016-01-07 and it match with Thursday of past week, so according to the weekday array number its weekday(2016-01-07) == 3 so it goes to the WHEN part of our query, and query will select something like this CASE WHEN (weekday('2016-01-07') <= 3) THEN date('2016-01-07' + INTERVAL(3-3)) that is equal to SELECT '2016-01-07' and so on for others.
I just found how to get this by trouble shooting on excel by using this WEEK('date' + INTERVAL 3 DAY, 3)

Select data between two dates exclude some days

I need to make query that selects data between two dates but exclude weekdays and some other days that are for example public holidays.
SELECT
`tblproduction`.`OperaterID`, `tblproduction`.`OperationID`,
SUM(`tblproduction`.`TotalProduced`) AS TotalProduced,
SUM(`tblproduction`.`TotalProducedOperator`) AS TotalProducedOp,
'Normal working day' AS DayType
FROM `tblproduction`
WHERE tblproduction.StartDateTime >= '2015-02-01 00:00:00' AND (tblproduction.StartDateTime <= '2015-02-28 23:59:59') AND tblproduction.OperaterID = 10
AND (DAYOFWEEK(tblproduction.StartDateTime) IN (1,7)) AND (DATE(tblproduction.StartDateTime) NOT IN (SELECT HolidayDate FROM tblholidays))
GROUP BY `tblproduction`.`OperaterID`, `tblproduction`.`OperationID`
UNION ALL
SELECT
`tblproduction`.`OperaterID`, `tblproduction`.`OperationID`,
SUM(`tblproduction`.`TotalProduced`) AS TotalProduced,
SUM(`tblproduction`.`TotalProducedOperator`) AS TotalProducedOp,
'Weekend' AS DayType
FROM `tblproduction`
WHERE tblproduction.StartDateTime >= '2015-02-01 00:00:00' AND (tblproduction.StartDateTime <= '2015-02-28 23:59:59') AND tblproduction.OperaterID = 10
AND (DAYOFWEEK(tblproduction.StartDateTime) NOT IN (1,7)) AND (DATE(tblproduction.StartDateTime) NOT IN (SELECT HolidayDate FROM tblholidays))
GROUP BY `tblproduction`.`OperaterID`, `tblproduction`.`OperationID`
For that purpose i have table with predefined dates that represents public holidays named tblHolidays. One of criteria in sql query is avoid counting pieces made during the holiday date. By running this query is steel includes those dates that are holidays. What should I change in query?
Maybe start with this...
SELECT p.OperaterID
, p.OperationID
, SUM(p.TotalProduced) TotalProduced
, SUM(p.TotalProducedOperator) TotalProducedOp
, CASE WHEN DAYOFWEEK(p.startdatetime) IN (1,7) THEN 'Normal working day' ELSE 'Weekend' END DayType
FROM tblproduction p
WHERE p.StartDateTime >= '2015-02-01 00:00:00' AND p.StartDateTime <= '2015-02-28 23:59:59'
AND p.OperaterID = 10
AND DATE(p.StartDateTime) NOT IN (SELECT HolidayDate FROM tblholidays)
GROUP
BY p.OperaterID
, p.OperationID
, CASE WHEN DAYOFWEEK(p.startdatetime) IN (1,7) THEN 'Normal working day' ELSE 'Weekend' END

Retrieve records using JOIN query

I want to retrieve data of last 1 week from emp_info table on per day basis.
So I have used :
SELECT DAYNAME(timestamp), COUNT(*)
FROM `emp_info`
WHERE DATE(timestamp ) > DATE_SUB(CURDATE( ) , INTERVAL 1 WEEK )
GROUP BY DAYNAME(timestamp);
According to the query I am getting result like:
Monday 5
Thursday 7
But I also want the result of weekday as 0 on which no record has been entered.
From suggestions I come to know about JOIN query. So I have tried to fix it but not getting any solution.
The result you are getting is right because there are no records on a specific dayname. Since you want to get all daynames, you need to project complete set of day (using UNION inside a SUBQUERY) and join it with your existing query.
SELECT a.day_name,
COALESCE(b.totalCount, 0) totalCount
FROM
(
SELECT 'Sunday' day_name, 1 ordby UNION ALL
SELECT 'Monday' day_name, 2 ordby UNION ALL
SELECT 'Tuesday' day_name, 3 ordby UNION ALL
SELECT 'Wednesday' day_name, 4 ordby UNION ALL
SELECT 'Thursday' day_name, 5 ordby UNION ALL
SELECT 'Friday' day_name, 6 ordby UNION ALL
SELECT 'Saturday' day_name, 7 ordby
) a
LEFT JOIN
(
SELECT DAYNAME(timestamp) day_name,
COUNT(*) totalCount
FROM `emp_info`
WHERE DATE(timestamp ) > DATE_SUB(CURDATE( ) , INTERVAL 1 WEEK )
GROUP BY DAYNAME(timestamp)
) b ON a.day_name = b.day_name
ORDER BY a.ordby
SQLFiddle Demo (simple example)