I'd use a numbers table to categorize the values in dbo.ProductTT.
I've created a simple MCVE1 to show how this works. FYI, in future, it would be great if you'd provide code like this when asking a question. It helps everyone.
USE tempdb;
IF OBJECT_ID(N'dbo.ProductTT', N'U') IS NOT NULL
BEGIN
DROP TABLE dbo.ProductTT;
END
CREATE TABLE dbo.ProductTT
(
ID int NOT NULL PRIMARY KEY CLUSTERED
, Product varchar(50) NOT NULL
, CreateTime int NOT NULL
, FormattedCreateTime AS RIGHT('0000' + CONVERT(varchar(4), CreateTime), 4)
);
INSERT INTO dbo.ProductTT
VALUES (1, 'XX', 0030)
, (2, 'UY', 0354)
, (3, 'YY', 0517)
, (4, 'ZZ', 0712)
, (5, 'WW', 0415)
, (6, 'GG', 1112)
, (7, 'MM', 1030)
, (8, 'HH', 0913)
, (9, 'H1', 1230)
, (10, 'H2', 1359)
, (11, 'H3', 2359);
IF OBJECT_ID(N'dbo.TimeGroups', N'U') IS NOT NULL
BEGIN
DROP TABLE dbo.TimeGroups;
END
CREATE TABLE dbo.TimeGroups
(
TimeGroupStart int NOT NULL
, TimeGroupEnd int NOT NULL
, TimeGroupName varchar(9) NOT NULL
, PRIMARY KEY CLUSTERED (TimeGroupStart, TimeGroupEnd)
);
INSERT INTO dbo.TimeGroups (TimeGroupStart, TimeGroupEnd, TimeGroupName)
VALUES (0, 3, '00 to 03')
, (3, 6, '03 to 06')
, (6, 9, '06 to 09')
, (9, 12, '09 to 12')
, (12, 15, '12 to 15')
, (15, 18, '15 to 18')
, (18, 21, '18 to 21')
, (21, 24, '21 to 24');
The "numbers table" in the code above is called "TimeGroups".
To get the desired output, you simply join the two tables together, as in:
SELECT tg.TimeGroupName
, TimeGroupCount = COUNT(1)
FROM dbo.ProductTT tt
INNER JOIN dbo.TimeGroups tg ON (tt.CreateTime / 100) >= tg.TimeGroupStart
AND (tt.CreateTime / 100) < tg.TimeGroupEnd
GROUP BY tg.TimeGroupName
ORDER BY tg.TimeGroupName;
The output looks like:
╔═══════════════╦════════════════╗
║ TimeGroupName ║ TimeGroupCount ║
╠═══════════════╬════════════════╣
║ 00 to 03 ║ 1 ║
║ 03 to 06 ║ 3 ║
║ 06 to 09 ║ 1 ║
║ 09 to 12 ║ 3 ║
║ 12 to 15 ║ 2 ║
║ 21 to 24 ║ 1 ║
╚═══════════════╩════════════════╝
Note that the JOIN clause in the above query specifies the range as greater-than-or-equal to the start of the category, and less-than the end of the category. If you used less-than-or-equal-to for the end of the range, you'd have ProductTT rows showing up in multiple categories, which is clearly incorrect.
You can see how the join works with this simple query:
SELECT tt.*
, Category = tt.CreateTime / 100
FROM dbo.ProductTT tt
The output looks like:
╔════╦═════════╦════════════╦═════════════════════╦══════════╗
║ ID ║ Product ║ CreateTime ║ FormattedCreateTime ║ Category ║
╠════╬═════════╬════════════╬═════════════════════╬══════════╣
║ 1 ║ XX ║ 30 ║ 0030 ║ 0 ║
║ 2 ║ UY ║ 354 ║ 0354 ║ 3 ║
║ 3 ║ YY ║ 517 ║ 0517 ║ 5 ║
║ 4 ║ ZZ ║ 712 ║ 0712 ║ 7 ║
║ 5 ║ WW ║ 415 ║ 0415 ║ 4 ║
║ 6 ║ GG ║ 1112 ║ 1112 ║ 11 ║
║ 7 ║ MM ║ 1030 ║ 1030 ║ 10 ║
║ 8 ║ HH ║ 913 ║ 0913 ║ 9 ║
║ 9 ║ H1 ║ 1230 ║ 1230 ║ 12 ║
║ 10 ║ H2 ║ 1359 ║ 1359 ║ 13 ║
║ 11 ║ H3 ║ 2359 ║ 2359 ║ 23 ║
╚════╩═════════╩════════════╩═════════════════════╩══════════╝
1 - I own the website pointed to in that link