SQL нужно возвращать диапазоны из столбца
У меня есть таблица, которая имеет целочисленный столбец с именем ID со значениями, которые могут иметь пробелы (например, 1,2,3,4,7,8,10,14,15,16,20)
Я хотел бы найти запрос, который в приведенном выше примере приведет к следующему:
1-4 7-8 10 14-16 20
= ОБНОВЛЕНИЕ =
Благодаря приведенному ниже коду (который, кажется, хорошо работает в SQL Server), я чувствую, что очень близок к тому, чтобы заставить его работать в MS-Access, что является целью. Я все еще получаю синтаксическую ошибку, хотя в моем отчете я не могу понять...
ВЫБЕРИТЕ val FROM (
SELECT islands.PORTID, CSTR(islands.PORTID ) as val
FROM MYTABLE islands
WHERE NOT EXISTS (SELECT * FROM MYTABLE t2 WHERE t2.PORTID = islands.PORTID - 1)
AND NOT EXISTS (SELECT * FROM MYTABLE t2 WHERE t2.PORTID = islands.PORTID + 1)
UNION
SELECT
rngStart.PORTID
,CSTR(rngStart.PORTID ) + '-'
+ CSTR(MIN(rngEnd.PORTID)) as val
FROM MYTABLE rngStart
INNER JOIN MYTABLE checkNext ON checkNext.PORTID = rngStart.PORTID + 1
INNER JOIN
(
SELECT PORTID
FROM MYTABLE tblRangeEnd
WHERE NOT EXISTS (SELECT * FROM MYTABLE t2 WHERE t2.PORTID = tblRangeEnd.PORTID + 1)
) rngEnd on rngEnd.PORTID > rngStart.PORTID
WHERE NOT EXISTS (SELECT * FROM MYTABLE t2 WHERE t2.PORTID = rngStart.PORTID - 1)
GROUP BY rngStart.PORTID
) как заказ по PORTID ASC
4 ответа
SELECT val FROM
(
-- Get the islands
SELECT islands.ID, CAST(islands.ID as varchar(10)) as val
FROM @t1 islands
WHERE NOT EXISTS (SELECT * FROM @t1 t2 WHERE t2.ID = islands.ID - 1)
AND NOT EXISTS (SELECT * FROM @t1 t2 WHERE t2.ID = islands.ID + 1)
UNION
-- Get the ranges
SELECT
rngStart.ID
,CAST(rngStart.ID as varchar(10)) + '-'
+ CAST(MIN(rngEnd.ID) as varchar(10)) as val
FROM @t1 rngStart
INNER JOIN @t1 checkNext ON checkNext.ID = rngStart.ID + 1
INNER JOIN
(
SELECT ID
FROM @t1 tblRangeEnd
WHERE NOT EXISTS (SELECT * FROM @t1 t2 WHERE t2.ID = tblRangeEnd.ID + 1)
) rngEnd on rngEnd.ID > rngStart.ID
WHERE NOT EXISTS (SELECT * FROM @t1 t2 WHERE t2.ID = rngStart.ID - 1)
GROUP BY rngStart.ID
) as tbl
ORDER BY ID ASC
Я использовал табличную переменную с именем @t1, но заменил ее именем таблицы.
Вот оно в действии.
РЕДАКТИРОВАТЬ
Чтобы это работало в Access, вам нужно немного изменить соединения. Попробуй это:
SELECT val FROM
(
SELECT islands.PORTID, CSTR(islands.PORTID) as val
FROM MYTABLE islands
WHERE NOT EXISTS
(SELECT * FROM MYTABLE t2 WHERE t2.PORTID = islands.PORTID - 1)
AND NOT EXISTS
(SELECT * FROM MYTABLE t2 WHERE t2.PORTID = islands.PORTID + 1)
UNION
SELECT
rngStart.PORTID
,CSTR(rngStart.PORTID) + '-' + CSTR(MIN(endPORTID)) as val
FROM MYTABLE rngStart
INNER JOIN
(
SELECT checkNext.PORTID as nextPORTID, rngEnd.PORTID as endPORTID
FROM MYTABLE checkNext
INNER JOIN
(
SELECT rngEnd.PORTID
FROM MYTABLE rngEnd
WHERE NOT EXISTS
(SELECT * FROM MYTABLE t2 WHERE t2.PORTID = rngEnd.PORTID + 1)
) AS rngEnd on rngEnd.PORTID > checkNext.PORTID - 1
) AS checkNext ON checkNext.nextPORTID = rngStart.PORTID + 1
WHERE NOT EXISTS
(SELECT * FROM MYTABLE t2 WHERE t2.PORTID = rngStart.PORTID - 1)
GROUP BY rngStart.PORTID
) as tbl
ORDER BY PORTID ASC
Еще одна попытка:
SELECT
MIN(i), MAX(i)
FROM (
select
i - (SELECT COUNT(*) FROM tbl t WHERE t.i < tbl.i) g,
i
from tbl
) t
GROUP BY g
Конечно, это будет медленно, но я не вижу другого способа нумерации строк в доступе.
create table #sequence (id int not null primary KEY)
insert into #sequence(id)
select 1
union all select 2
union all select 3
union all select 4
union all select 7
union all select 8
union all select 10
union all select 14
union all select 15
union all select 16
union all select 20
--Find Contig ranges
select l.id as start,
(
select min(a.id) as id
from #sequence as a
left outer join #sequence as b on a.id = b.id - 1
where b.id is null
and a.id >= l.id
) AS fend
from #sequence as l
left outer join #sequence as r on r.id = l.id - 1
where r.id is null;
--Find missing values in sequence
select l.id + 1 as start, min(fr.id) - 1 as stop
from #sequence as l
left outer join #sequence as r on l.id = r.id - 1
left outer join #sequence as fr on l.id < fr.id
where r.id is null and fr.id is not null
group by l.id, r.id;
drop table #sequence
Это даст вам как диапазоны, так и промежутки между диапазонами, чтобы вы могли видеть, что у вас есть или что вам нужно. предоставленные образцы данных
Это будет работать, если вы получите доступ к функции row_number()
,
with C as
(
select ID,
ID - row_number() over(order by ID) as grp
from YourTable
)
select min(ID) as MinID,
max(ID) as MaxID
from C
group by grp
или с подзапросом вместо общего табличного выражения.
select min(ID) as MinID,
max(ID) as MaxID
from (select ID,
ID - row_number() over(order by ID) as grp
from YourTable) as C
group by grp
Результат:
MinID MaxID
----------- -----------
1 4
7 8
10 10
14 16
20 20
Попробуйте это на SQL Server http://data.stackexchange.com/stackru/q/119411/