This is why you need a Tally table. The query I posted seem to be giving the results that you are asking for, at least as I understood it. I have modified the code below to replace NULL with zeros.
-- First, create a sample table.
-- When you post a question, it would be very helpful if you post DDL such as this
-- that someone can copy and paste to run and write queries against.
-- Otherwise, the code posted would most likely be untested code.
CREATE TABLE #tmp( SDate DATETIME , TotalCalls INT)
INSERT INTO #tmp VALUES
('2015-07-28 11:11:10.420',11),
('2015-07-28 11:53:10.000',3),
('2015-07-29 12:10:10.420',9),
('2015-07-29 12:23:17.000',1),
('2015-07-29 11:23:17.000',6);
-- Create Tally table
CREATE TABLE #Tally(N INT NOT NULL PRIMARY KEY);
INSERT INTO #Tally
( N )
SELECT TOP 10000
ROW_NUMBER() OVER ( ORDER BY t1.number )-1 AS N
FROM master..spt_values t1
CROSS JOIN master..spt_values t2
-- Query for 28th and 29th
DECLARE
@startDate DATETIME = '20150728',
@endDate DATETIME = '20150729';
SELECT
DATEADD(hh,N,@startDate) [StartHour],
COALESCE(SUM(TotalCalls),0) AS TotalCalls
FROM
#Tally t
LEFT JOIN #tmp d ON
d.SDate >= DATEADD(hh,N,@startDate)
AND d.SDate < DATEADD(hh,N+1,@startDate)
WHERE
DATEADD(hh,N-24,@startDate) <= @endDate
GROUP BY
DATEADD(hh,N,@startDate)
ORDER BY
[StartHour] ;'
When I run this, I get 49 rows of output, most of which have TotalCalls = 0. The three rows with non-zeros is as follows
StartHour TotalCalls
2015-07-28 11:00:00.000 14
2015-07-29 11:00:00.000 6
2015-07-29 12:00:00.000 10
Isn't that what you were asking for, or is it something else?