I think this works, including the case I asked about in the comments:
declare @t table (data varchar(50), [date] datetime) insert @t values ('Foo','20120814'), ('Bar','20120815'), ('Bar','20120916'), ('Xyz','20121020') ; With OuterSort as ( select *,ROW_NUMBER() OVER (ORDER BY [date] asc) as rn from @t ) --Now we need to find contiguous ranges of the same data value, and the min and max row number for such a range , Islands as ( select data,rn as rnMin,rn as rnMax from OuterSort os where not exists (select * from OuterSort os2 where os2.data = os.data and os2.rn = os.rn - 1) union all select i.data,rnMin,os.rn from Islands i inner join OuterSort os on i.data = os.data and i.rnMax = os.rn-1 ), FullIslands as ( select data,rnMin,MAX(rnMax) as rnMax from Islands group by data,rnMin ) select * from OuterSort os inner join FullIslands fi on os.rn between fi.rnMin and fi.rnMax order by fi.rnMin asc,os.rn desc
It works by first calculating the initial ordering in the OuterSort
CTE. Then, using two CTEs ( Islands
and FullIslands
), we calculate the parts of this ordering in which the same data value appears in adjacent rows. Having done this, we can calculate the final ordering by any value that all adjacent values ββwill have (for example, the smallest number of rows of the "island" to which they belong), and then in the "island" we use the reverse side of the originally calculated sort order.
Note that this may not be very efficient for large datasets. In the data samples, it is displayed as requiring 4 scans of the base table table, as well as a coil.
source share