Single table scan, no windowing function, single group by, no problems with duplicate dates, equal performance with windowing functions, or even outperforms them with really large queries. (Update: I don't know how it performs compared to the TOP 1 WITH TIES / CROSS APPLY method. Since it uses a scan, it might be slower in some situations.)
SELECT
LogEntryID = Convert(int, Substring(Packed, 9, 4)),
FileID = Convert(int, Substring(Packed, 13, 4)),
CreatedOn = Convert(datetime, Substring(Packed, 1, 8)),
EventTypeID = Convert(int, Substring(Packed, 17, 4))
FROM
(
SELECT
Packed = Max(
Convert(binary(8), CreatedOn)
+ Convert(binary(4), LogEntryID)
+ Convert(binary(4), FileID)
+ Convert(binary(4), EventTypeID)
)
FROM LogEntries
WHERE EventTypeID IN (2,4)
GROUP BY ClientName
) X
If anyone would like to see this in action, here's some creation script:
USE tempdb
CREATE TABLE LogEntries (
LogEntryID int not null identity(1,1),
FileID int,
CreatedOn datetime,
EventTypeID int,
ClientName varchar(30)
)
INSERT LogEntries VALUES (1, GetDate()-20, 2, 'bob')
INSERT LogEntries VALUES (1, GetDate()-19, 3, 'bob')
INSERT LogEntries VALUES (1, GetDate()-18, 4, 'bob')
INSERT LogEntries VALUES (1, GetDate()-17, 3, 'bob')
INSERT LogEntries VALUES (1, GetDate()-19.5, 2, 'anna')
INSERT LogEntries VALUES (1, GetDate()-18.5, 3, 'anna')
INSERT LogEntries VALUES (1, GetDate()-17.5, 4, 'anna')
INSERT LogEntries VALUES (1, GetDate()-16.5, 3, 'anna')
Please note that this method is taking advantage of the internal byte representation of the given data types having the same ordering as the type's values. Packed data types like float or decimal will NOT work: those would require conversion to something suitable first, such as int, bigint, or character.
Also, the new Date and Time data types in SQL 2008 have different representations that won't pack correctly to use with this method. I haven't examined the Time data type yet, but for the Date data type:
DECLARE @d date
SET @d ='99990101'
SELECT Convert(binary(3), @d) -- 0x6EB837
The actual value is 0x37B86E, so it's storing them in reverse byte order (the "zero" date is 0001-01-01).