T-SQL Выберите все комбинации диапазонов, которые соответствуют совокупным критериям - PullRequest
0 голосов
/ 28 августа 2018

Проблема пересмотрена по комментариям

Скажем, у нас есть следующие целочисленные идентификаторы и числа ...

id   count
1    0
2    10
3    0
4    0
5    0
6    1
7    9
8    0

У нас также есть переменная @id_range int.

Учитывая значение для @id_range, как мы можем выбрать все комбинации диапазонов id без использования циклов или курсоров while, которые удовлетворяют следующим критериям?

1) Никакие два диапазона в комбинации не могут перекрываться (минимальный и максимальный значения каждого диапазона включены)
2) sum(count) для комбинации диапазонов должно равняться sum(count) исходного набора данных (в нашем случае 20)
3) Включить только диапазоны, в которых sum(count)> 0

Простейший случай будет, когда @id_range = max(id) - min(id) или 7 с учетом приведенных выше данных. В этом случае есть только одно решение:

minId   maxId   count
---------------------
1       8       20

Но если, например, @id_range = 1, будет 4 возможных решения:

Решение 1:

minId   maxId   count
---------------------
1      2        10
5      6        1
7      8        9

Решение 2:

minId   maxId   count
---------------------
1      2        10
6      7        10

Решение 3:

minId   maxId   count
---------------------
2       3       10
5       6       1
7       8       9

Решение 4:

minId   maxId   count
---------------------
2       3       10
6       7       10

Конечная цель - определить, какие решения имеют наименьшее количество диапазонов (решения № 2 и 4, в приведенном выше примере, где @id_range = 1).

1 Ответ

0 голосов
/ 28 августа 2018

это решение не перечисляет все возможные комбинации, но просто попытайтесь сгруппировать его в наименьшее возможное количество строк.

Надеюсь, он охватит все возможные сценарии

-- create the sample table
declare @sample table
(
    id  int,
    [count] int
)

-- insert some sample data
insert into @sample select 1,     0
insert into @sample select 2,    10
insert into @sample select 3,     0
insert into @sample select 4,     0
insert into @sample select 5,     0
insert into @sample select 6,     1
insert into @sample select 7,     9
insert into @sample select 8,     0

-- the @id_range
declare @id_range   int = 1

-- the query
; with
cte as
(
    -- this cte identified those rows with count > 0 and group them together
    -- sign(0) gives 0, sign(+value) gives 1
    -- basically it is same as case when [count] > 0 then 1 else 0 end
    select  *, 
            grp = row_number() over (order by id) 
                - dense_rank() over(order by sign([count]), id)
    from    @sample
),
cte2 as
(
    -- for each grp in cte, assign a sub group (grp2). each sub group 
    -- contains @id_range number of rows
    select  *, 
            grp2 = (row_number() over (partition by grp order by id) - 1) 
                 / (@id_range + 1)
    from    cte
    where   count   > 0
)
select  MinId   = min(id), 
        MaxId   = min(id) + @id_range, 
        [count] = sum(count)
from    cte2
group by grp, grp2
...