Select based on the next business day in a calendar table - sql

I have a calendar table (Calendar_Date, Is_Business_Day) already filled in.
I have already managed to do a SELECT on this basis :
If today is before the 3rd day of the current month, select all the days before this day until the last day of the penultimate month
For example : Today is 2018-05-02, this is my output :
Calendar_Date | Is_Business_Day
2000-01-01 | 0
... |
2018-03-29 | 1
2018-03-30 | 1
2018-03-31 | 0
If today is after the 3rd day of the current month, select all the days before this day until the last day of the last month.
For example : Tomorrow, 2018-05-03 this will be my output :
Calendar_Date | Is_Business_Day
2000-01-01 | 0
... |
2018-04-28 | 0
2018-04-29 | 0
2018-04-30 | 1
This is my query :
SELECT Calendar_Date, Is_Business_Day
FROM Calendar_Table
WHERE (Calendar_Date <= (CASE WHEN DATEPART(day, GETDATE()) >= 3 THEN EOMONTH(DATEADD(MONTH, - 1, GETDATE())) ELSE EOMONTH(DATEADD(MONTH, - 2, GETDATE())) END))
This is working perfectly, but what i would like it to do now is to switch after the first business day after the 3rd day of the month, instead of switching after the 3rd day of the month.
How can I use the information about business days in my calendar table to do this?

I think following query should work.
;WITH CTE AS
(
SELECT Calendar_Date, Is_Business_Day
FROM Calendar_Table
WHERE (Calendar_Date <= (CASE WHEN DATEPART(day, GETDATE()) >= 3
THEN EOMONTH(DATEADD(MONTH, - 1, GETDATE()))
ELSE EOMONTH(DATEADD(MONTH, - 2, GETDATE())) END))
)
SELECT * FROM CTE
WHERE Calendar_Date >= (SELECT MIN(Calendar_Date) FROM CTE WHERE Is_Business_Day=1)

Related

count grouped records by ID and show as weekly with day/week outputted

I have a table in which I have to count total records assigned to each USER by weekly (monday to sunday).
Table BooksIssued
BOOKID USER DATE
1 A 20211001
2 A 20211002
3 A 20211003
4 A 20211004
5 B 20211009
6 C 20211008
7 C 20211008
20211001 is friday.
output of sql query is as follows, the WEEKDATE column shows the week end date (i.e sunday)
WEEKCOUNT USER WEEKDATE
3 A 10/03
1 A 10/10
1 B 10/10
2 C 10/10
I am unable to get the date in output containing day, as grouping is done based on user and week part of date. Please suggest on getting above output.
I am using vertica DB.
Below is sample query i tried (though i could not get the day part of date)
SELECT USER, date_part('WEEK', date)) as WEEKDATE
       SUM(CASE WHEN DATE >= timestampadd(WEEK, DATEDIFF(WEEK, date('1900-01-01 00:00:00.000'), date(sysdate)), date('1900-01-01 00:00:00.000'))
                AND  DATE <  timestampadd(WEEK, DATEDIFF(WEEK, date('1900-01-01 00:00:00.000'), date(sysdate)) + 1, date('1900-01-01 00:00:00.000'))
                THEN 1 ELSE 0 END) AS WEEKCOUNT,
FROM   BOOKSISSUED
GROUP BY USER, date_part('WEEK', date)
when i add date_part('DAY', date) in select clause, i get error as its not in group by.
Please help.
Do you mean this?
WITH
-- your input ...
indata(BOOKID,USR,DT) AS (
SELECT 1,'A',DATE '20211001'
UNION ALL SELECT 2,'A',DATE '20211002'
UNION ALL SELECT 3,'A',DATE '20211003'
UNION ALL SELECT 4,'A',DATE '20211004'
UNION ALL SELECT 5,'B',DATE '20211009'
UNION ALL SELECT 6,'C',DATE '20211008'
UNION ALL SELECT 7,'C',DATE '20211008'
)
SELECT
COUNT(*) AS week_count
, usr
, TO_CHAR(
DATE_TRUNC('WEEK',dt) + INTERVAL '6 DAYS'
, 'MM/DD'
) AS trcweek
FROM indata
GROUP BY 2,3
ORDER BY 2,3
;
week_count | usr | trcweek
------------+-----+---------
3 | A | 10/03
1 | A | 10/10
1 | B | 10/10
2 | C | 10/10
Can you please check the sql query syntax.
In the SELECT clause second column and group by clause second column
SELECT USER, date_part('WEEK', date) as WEEKDATE,
SUM(CASE WHEN DATE >= timestampadd(WEEK, DATEDIFF(WEEK, date('1900-01-01 00:00:00.000'), date(sysdate)), date('1900-01-01 00:00:00.000'))
AND DATE < timestampadd(WEEK, DATEDIFF(WEEK, date('1900-01-01 00:00:00.000'), date(sysdate)) + 1, date('1900-01-01 00:00:00.000'))
THEN 1 ELSE 0 END) AS WEEKCOUNT
FROM BOOKSISSUED
GROUP BY USER, date_part('WEEK', date)

Get SUM of Current Week data and Current Year data from SQLite

I have a SQLite database and sales table is like the following,
| Id | quantity | dateTime |
------------------------------------
| 1 | 10 | 2019-12-25 12:55 |
| 2 | 05 | 2019-12-30 12:55 |
| 3 | 25 | 2020-08-23 12:55 |
| 4 | 25 | 2020-08-24 12:55 |
| 5 | 56 | 2020-08-25 12:55 |
| 6 | 25 | 2020-08-26 12:55 |
| 7 | 12 | 2020-08-27 12:55 |
| 8 | 30 | 2020-08-28 12:55 |
| 9 | 40 | 2020-08-29 12:55 |
I need to get the Current Week data (Mon to Sun) and the Current Year data from (Jan to Dec). So if I pass today date I need to get only the Current Week sales data group by days like the following,
If I pass today date and time (2020-08-28 13:55) the query should give me Current Week data like this,
Day Sold Items (SUM(quantity))
Monday 20
Tuesday 25
Wednesday 10
Thursday 50
Friday 60
Saturday 0 (If the date hasn't come yet I need to get 0)
Sunday 0
And same as the Current Year data when I pass the Current Date,
Month Sold Items (SUM(quantity))
JAN 20
FEB 25
MAR 10
APR 50
MAY 60
JUN 0 (If the month hasn't come yet I need to get 0)
JUL 0
... ...
I tried with multiple queries in SQLite but couldn't get what I need. Here are the queries I tried,
Weekly Data (This one gave me past week data also)
SELECT SUM(quantity) as quantity, strftime('%w', dateTime) as Day
From sales
Group by strftime('%w', dateTime)
Monthly Data
SELECT SUM(quantity) as quantity, strftime('%m', dateTime) as Month
From sales
Group by strftime('%m', dateTime)
So anybody can help me to achieve this? Thanks in advance.
For the totals of the current week you need a CTE that returns the names of the days and the another one that returns the Monday of the current week.
You must cross join these CTEs and left join your table to aggregate:
with
days as (
select 1 nr, 'Monday' day union all
select 2, 'Tuesday' union all
select 3, 'Wednesday' union all
select 4, 'Thursday' union all
select 5, 'Friday' union all
select 6, 'Saturday' union all
select 7, 'Sunday'
),
weekMonday as (
select date(
'now',
case when strftime('%w', 'now') <> '1' then '-7 day' else '0 day' end,
'weekday 1'
) monday
)
select d.day,
coalesce(sum(t.quantity), 0) [Sold Items]
from days d cross join weekMonday wm
left join tablename t
on strftime('%w', t.dateTime) + 0 = d.nr % 7
and date(t.dateTime) between wm.monday and date(wm.monday, '6 day')
group by d.nr, d.day
order by d.nr
For the totals of the current year you need a CTE that returns the month names and then left join the table to aggregate:
with
months as (
select 1 nr, 'JAN' month union all
select 2 nr, 'FEB' union all
select 3 nr, 'MAR' union all
select 4 nr, 'APR' union all
select 5 nr, 'MAY' union all
select 6 nr, 'JUN' union all
select 7 nr, 'JUL' union all
select 8 nr, 'AUG' union all
select 9 nr, 'SEP' union all
select 10 nr, 'OCT' union all
select 11 nr, 'NOV' union all
select 12 nr, 'DEC'
)
select m.month,
coalesce(sum(t.quantity), 0) [Sold Items]
from months m
left join tablename t
on strftime('%m', t.dateTime) + 0 = m.nr
and date(t.dateTime) between date('now','start of year') and date('now','start of year', '1 year', '-1 day')
group by m.nr, m.month
order by m.nr
You can use the below query to get the weekly date, I am assuming that everydate has single entry and hence not grouping otherwise you can add group by.
First we will get the weekly calendar based on the input date (I have taken current date)
and then left join with calendar to get the required sold items info.
WITH seq(n) AS
(
SELECT 0 UNION ALL SELECT n + 1 FROM seq
WHERE n < DATEDIFF(DAY, (SELECT DATEADD(DAY, 2 - DATEPART(WEEKDAY, GETDATE()), CAST(GETDATE() AS DATE)) [Week_Start_Date]), (Select DATEADD(DAY, 8 - DATEPART(WEEKDAY, GETDATE()), CAST(GETDATE() AS DATE)) [Week_End_Date]))
),
CALENDAR(d) AS
(
SELECT DATEADD(DAY, n, (SELECT DATEADD(DAY, 2 - DATEPART(WEEKDAY, GETDATE()), CAST(GETDATE() AS DATE)) [Week_Start_Date])) FROM seq
)
SELECT coalesce(QUANTITY, 0) sold_items ,DATENAME(WEEKDAY, d) week_day FROM CALENDAR a left outer join Table_WEEKDAY b
on (a.d = convert(date, b.dateTime))
ORDER BY d
OPTION (MAXRECURSION 0);
You can try the below - DEMO
select day,coalesce(sum(quantity),0) as quantity
from
(select 0 as day union all select 1 union all select 2 union all select 3 union all select 4
union all select 5 union all select 6) as d
left join sales on cast(strftime('%w', dateTime) as int)=day
group by strftime('%w', dateTime),day
order by day

SQL how to count census points occurring between date records

I’m using MS-SQL-2008 R2 trying to write a script that calculates the Number of Hospital Beds occupied on any given day, at 2 census points: midnight, and 09:00.
I’m working from a data set of patient Ward Stays. Basically, each row in the table is a record of an individual patient's stay on a single ward, and records the date/time the patient is admitted onto the ward, and the date/time the patient leaves the ward.
A sample of this table is below:
Ward_Stay_Primary_Key | Ward_Start_Date_Time | Ward_End_Date_Time
1 | 2017-09-03 15:04:00.000 | 2017-09-27 16:55:00.000
2 | 2017-09-04 18:08:00.000 | 2017-09-06 18:00:00.000
3 | 2017-09-04 13:00:00.000 | 2017-09-04 22:00:00.000
4 | 2017-09-04 20:54:00.000 | 2017-09-08 14:30:00.000
5 | 2017-09-04 20:52:00.000 | 2017-09-13 11:50:00.000
6 | 2017-09-05 13:32:00.000 | 2017-09-11 14:49:00.000
7 | 2017-09-05 13:17:00.000 | 2017-09-12 21:00:00.000
8 | 2017-09-05 23:11:00.000 | 2017-09-06 17:38:00.000
9 | 2017-09-05 11:35:00.000 | 2017-09-14 16:12:00.000
10 | 2017-09-05 14:05:00.000 | 2017-09-11 16:30:00.000
The key thing to note here is that a patient’s Ward Stay can span any length of time, from a few hours to many days.
The following code enables me to calculate the number of beds at both census points for any given day, by specifying the date in the case statement:
SELECT
'05/09/2017' [Date]
,SUM(case when Ward_Start_Date_Time <= '05/09/2017 00:00:00.000' AND (Ward_End_Date_Time >= '05/09/2017 00:00:00.000' OR Ward_End_Date_Time IS NULL)then 1 else 0 end)[No. Beds Occupied at 00:00]
,SUM(case when Ward_Start_Date_Time <= '05/09/2017 09:00:00.000' AND (Ward_End_Date_Time >= '05/09/2017 09:00:00.000' OR Ward_End_Date_Time IS NULL)then 1 else 0 end)[No. Beds Occupied at 09:00]
FROM
WardStaysTable
And, based on the sample 10 records above, generates this output:
Date | No. Beds Occupied at 00:00 | No. Beds Occupied at 09:00
05/09/2017 | 4 | 4
To perform this for any number of days is obviously onerous, so what I’m looking to create is a query where I can specify a start/end date parameter (e.g. 1st-5th Sept), and for the query to then evaluate the Ward_Start_Date_Time and Ward_End_Date_Time variables for each record, and – grouping by the dates defined in the date parameter – count each time the 00:00:00.000 and 09:00:00.000 census points fall between these 2 variables, to give an output something along these lines (based on the above 10 records):
Date | No. Beds Occupied at 00:00 | No. Beds Occupied at 09:00
01/09/2017 | 0 | 0
02/09/2017 | 0 | 0
03/09/2017 | 0 | 0
04/09/2017 | 1 | 1
05/09/2017 | 4 | 4
I’ve approached this (perhaps naively) thinking that if I use a cte to create a table of dates (defined by the input parameters), along with associated midnight and 9am census date/time points, then I could use these variables to group and evaluate the dataset.
So, this code generates the grouping dates and census date/time points:
DECLARE
#StartDate DATE = '01/09/2017'
,#EndDate DATE = '05/09/2017'
,#0900 INT = 540
SELECT
DATEADD(DAY, nbr - 1, #StartDate) [Date]
,CONVERT(DATETIME,(DATEADD(DAY, nbr - 1, #StartDate))) [MidnightDate]
,DATEADD(mi, #0900,(CONVERT(DATETIME,(DATEADD(DAY, nbr - 1, #StartDate))))) [0900Date]
FROM
(
SELECT
ROW_NUMBER() OVER ( ORDER BY c.object_id ) AS nbr
FROM sys.columns c
) nbrs
WHERE nbr - 1 <= DATEDIFF(DAY, #StartDate, #EndDate)
The stumbling block I’ve hit is how to join the cte to the WardStays dataset, because there’s no appropriate key… I’ve tried a few iterations of using a subquery to make this work, but either I’m taking the wrong approach or I’m getting my syntax in a mess.
In simple terms, the logic I’m trying to create to get the output is something like:
SELECT
[Date]
,SUM (case when WST.Ward_Start_Date_Time <= [MidnightDate] AND (WST.Ward_End_Date_Time >= [MidnightDate] OR WST.Ward_End_Date_Time IS NULL then 1 else 0 end) [No. Beds Occupied at 00:00]
,SUM (case when WST.Ward_Start_Date_Time <= [0900Date] AND (WST.Ward_End_Date_Time >= [0900Date] OR WST.Ward_End_Date_Time IS NULL then 1 else 0 end) [No. Beds Occupied at 09:00]
FROM WardStaysTable WST
GROUP BY [Date]
Is the above somehow possible, or am I barking up the wrong tree and need to take a different approach altogether? Appreciate any advice.
I would expect something like this:
WITH dates as (
SELECT CAST(#StartDate as DATETIME) as dte
UNION ALL
SELECT DATEADD(DAY, 1, dte)
FROM dates
WHERE dte < #EndDate
)
SELECT dates.dte [Date],
SUM(CASE WHEN Ward_Start_Date_Time <= dte AND
Ward_END_Date_Time >= dte
THEN 1 ELSE 0
END) as num_beds_0000,
SUM(CASE WHEN Ward_Start_Date_Time <= dte + CAST('09:00' as DATETIME) AND
Ward_END_Date_Time >= dte + CAST('09:00' as DATETIME)
THEN 1 ELSE 0
END) as num_beds_0900
FROM dates LEFT JOIN
WardStaysTable wt
ON wt.Ward_Start_Date_Time <= DATEADD(day, 1, dates.dte) AND
wt.Ward_END_Date_Time >= dates.dte
GROUP BY dates.dte
ORDER BY dates.dte;
The cte is just creating the list of dates.
What a cool exercise. Here is what I came up with:
CREATE TABLE #tmp (ID int, StartDte datetime, EndDte datetime)
INSERT INTO #tmp values(1,'2017-09-03 15:04:00.000','2017-09-27 06:55:00.000')
INSERT INTO #tmp values(2,'2017-09-04 08:08:00.000','2017-09-06 18:00:00.000')
INSERT INTO #tmp values(3,'2017-09-04 13:00:00.000','2017-09-04 22:00:00.000')
INSERT INTO #tmp values(4,'2017-09-04 20:54:00.000','2017-09-08 14:30:00.000')
INSERT INTO #tmp values(5,'2017-09-04 20:52:00.000','2017-09-13 11:50:00.000')
INSERT INTO #tmp values(6,'2017-09-05 13:32:00.000','2017-09-11 14:49:00.000')
INSERT INTO #tmp values(7,'2017-09-05 13:17:00.000','2017-09-12 21:00:00.000')
INSERT INTO #tmp values(8,'2017-09-05 23:11:00.000','2017-09-06 07:38:00.000')
INSERT INTO #tmp values(9,'2017-09-05 11:35:00.000','2017-09-14 16:12:00.000')
INSERT INTO #tmp values(10,'2017-09-05 14:05:00.000','2017-09-11 16:30:00.000')
DECLARE
#StartDate DATE = '09/01/2017'
,#EndDate DATE = '10/01/2017'
, #nHours INT = 9
;WITH d(OrderDate) AS
(
SELECT DATEADD(DAY, n-1, #StartDate)
FROM (SELECT TOP (DATEDIFF(DAY, #StartDate, #EndDate) + 1)
ROW_NUMBER() OVER (ORDER BY [object_id]) FROM sys.all_objects) AS x(n)
)
, CTE AS(
select OrderDate, t2.*
from #tmp t2
cross apply(select orderdate from d ) d
where StartDte >= #StartDate and EndDte <= #EndDate)
select OrderDate,
SUM(CASE WHEN OrderDate >= StartDte and OrderDate <= EndDte THEN 1 ELSE 0 END) [No. Beds Occupied at 00:00],
SUM(CASE WHEN StartDTE <= DateAdd(hour,#nHours,CAST(OrderDate as datetime)) and DateAdd(hour,#nHours,CAST(OrderDate as datetime)) <= EndDte THEN 1 ELSE 0 END) [No. Beds Occupied at 09:00]
from CTE
GROUP BY OrderDate
This should allow you to check for any hour of the day using the #nHours parameter if you so choose. If you only want to see records that actually fall within your date range then you can filter the cross apply on start and end dates.

Netezza Grouping by Week Start (Sunday) AND Month Start

I have a little bit of an unusual question. I'm using Python to write some data to a text file that I then use Tableau to read from and build visualizations. I'm grouping the query results by week in order to reduce the size of the output file. I think the SQL is pretty standard for that type of operation.
SELECT [Date] - EXTRACT(DOW FROM [Date]) + 1
[this gives me the Sunday of the week for any date]
However, I occasionally want to group by months rather than weeks, which is impossible with the current output. What I want is a modification to the query which will group by week EXCEPT when a week overlaps two months. If the week overlaps two months, it will split the results into the first part of the week which is in the first month, and then the second part of the week which is in the second month. That way, we could use the output to show weekly result OR monthly/quarterly/yearly results simply by grouping the dates within Tableau.
Has anyone tackled a problem like this before?
As an illustration, consider the following values.
2016-08-21 1
2016-08-22 1
2016-08-23 1
2016-08-24 1
2016-08-25 1
2016-08-26 1
2016-08-27 1
2016-08-28 1
2016-08-29 1
2016-08-30 1
2016-08-31 1
2016-09-01 1
2016-09-02 1
2016-09-03 1
2016-09-04 1
... ...
I would like the code to output the following values:
2016-08-21 7
2016-08-28 4
2016-09-01 3
2016-09-04 1...
Would really appreciate any help!
Based on googled Netzetta syntax, this could work:
select
min([Date]) as MinDate, count(*) as TotalDays
from YourTable
group by
extract(year from [Date]),
extract(month from [Date]),
(case
when extract(dow from [Date]) = 1 -- dow 1 is sunday
then extract(week from [Date]) + 1 -- week starts on monday
else extract(week from [Date])
end);
Or as suggested in the comments, group on the sunday:
select
min([Date]) as MinDate, count(*) as TotalDays
from YourTable
group by
([Date] - (extract(dow from [Date]) - 1));
Here's the final code that I used.
CASE
WHEN EXTRACT(MONTH FROM [Date]) <> EXTRACT(MONTH FROM [Date] - EXTRACT(DOW FROM [Date]) + 1)
THEN DATE_TRUNC('month', [Date])
ELSE [Date] - EXTRACT(DOW FROM [Date]) + 1 END
Then I grouped on that field.
The way it works is that it checks if the month of the date is equal to the month of the week start. If it isn't, it returns the first day of the month. If it is, it returns the week start. This code returns the values in the example from the original post.

SQL Server : Gap / Island, datetime, contiguous block 365 day block

I have a table that looks like this:-
tblMeterReadings
id meter period_start period_end amount
1 1 2014-01-01 00:00 2014-01-01 00:29:59 100.3
2 1 2014-01-01 00:30 2014-01-01 00:59:59 50.5
3 1 2014-01-01 01:00 2014-01-01 01:29:59 70.7
4 1 2014-01-01 01:30 2014-01-01 01:59:59 900.1
5 1 2014-01-01 02:00 2014-01-01 02:29:59 400.0
6 1 2014-01-01 02:30 2014-01-01 02:59:59 200.3
7 1 2014-01-01 03:00 2014-01-01 03:29:59 100.8
8 1 2014-01-01 03:30 2014-01-01 03:59:59 140.3
This is a tiny "contiguous block" from '2014-01-01 00:00' to '2014-01-01 3:59:59'.
In the real table there are "contiguous blocks" of years in length.
I need to find the the period_start and period_end of the most recent CONTINUOUS 365 COMPLETE DAYs (fileterd by meter column).
When I say COMPLETE DAYs I mean a day that has entries spanning 00:00 to 23:59.
When I say CONTINUOUS I mean there must be no days missing.
I would like to select all the rows that make up this block of CONTINUOUS COMPLETE DAYs.
I also need an output like:
block_start block_end total_amount_for_block
2013-02-26 00:00 2014-02-26 23:59:59 1034234.5
This is beyond me, so if someone can solve... I will be very impressed.
Since your granularity is 1 second, you need to expand your periods into all the date/times between the start and end at 1 second intervals. To do this you need to cross join with a numbers table (The numbers table is generated on the fly by ranking object ids from an arbitrary system view, I have limited it to TOP 86400 since this is the number of seconds in a day, and you have stated your time periods never span more than one day):
WITH Numbers AS
( SELECT TOP (86400)
Number = ROW_NUMBER() OVER(ORDER BY a.object_id) - 1
FROM sys.all_objects a
CROSS JOIN sys.all_objects b
ORDER BY a.object_id
)
SELECT r.ID, r.meter, dt.[DateTime]
FROM tblMeterReadings r
CROSS JOIN Numbers n
OUTER APPLY
( SELECT [DateTime] = DATEADD(SECOND, n.Number, r.period_start)
) dt
WHERE dt.[DateTime] <= r.Period_End;
You then have your continuous range in which to perform the normal gaps and islands grouping:
WITH Numbers AS
( SELECT TOP (86400)
Number = ROW_NUMBER() OVER(ORDER BY a.object_id) - 1
FROM sys.all_objects a
CROSS JOIN sys.all_objects b
ORDER BY a.object_id
), Grouped AS
( SELECT r.meter,
Amount = CASE WHEN Number = 1 THEN r.Amount ELSE 0 END,
dt.[DateTime],
GroupingSet = DATEADD(SECOND,
-DENSE_RANK() OVER(PARTITION BY r.Meter
ORDER BY dt.[DateTime]),
dt.[DateTime])
FROM tblMeterReadings r
CROSS JOIN Numbers n
OUTER APPLY
( SELECT [DateTime] = DATEADD(SECOND, n.Number, r.period_start)
) dt
WHERE dt.[DateTime] <= r.Period_End
)
SELECT meter,
PeriodStart = MIN([DateTime]),
PeriodEnd = MAX([DateTime]),
Amount = SUM(Amount)
FROM Grouped
GROUP BY meter, GroupingSet
HAVING DATEADD(YEAR, 1, MIN([DateTime])) < MAX([DateTime]);
N.B. Since the join to Number causes amounts to be duplicated, it is necessary to set all duplicates to 0 using CASE WHEN Number = 1 THEN r.Amount ELSE 0 END, i.e only include the amount for the first row for each ID
Removing the Having clause for your sample data will give:
meter | PeriodStart | PeriodEnd | Amount
------+---------------------+---------------------+----------
1 | 2014-01-01 00:00:00 | 2014-01-01 03:59:59 | 1963
Example on SQL Fiddle
You could try this:
Select MIN(period_start) as "block start"
, MAX(period_end) as "block end"
, SUM(amount) as "total amount"
FROM YourTable
GROUP BY datepart(year, period_start)
, datepart(month, period_start)
, datepart(day, period_start)
, datepart(year, period_end)
, datepart(month, period_end)
, datepart(day, period_end)
Having datepart(year, period_start) = datepart(year, period_end)
AND datepart(month, period_start) = datepart(month, period_end)
AND datepart(day, period_start) = datepart(day, period_end)
AND datepart(hour, MIN(period_start)) = 0
AND datepart(minute,MIN(period_start)) = 0
AND datepart(hour, MAX(period_end)) = 23
AND datepart(minute,MIN(period_end)) = 59