Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
SELECT '1-2, 4, 6, 10, 13-15, 18...';
SELECT GROUP_CONCAT(__range)
FROM (
SELECT IF(__from = __to, __from, CONCAT(__from, '-', __to)) AS __range
FROM (
SELECT (@n + 1) AS __from, (id - 1) AS __to, @n := id AS __change
FROM (
SELECT id, @n := 0 FROM test ORDER BY id
) t
) t2
WHERE __to >= __from
UNION
SELECT CONCAT((@n + 1), '...') AS __range
) t3
SELECT CONCAT_WS(',',
GROUP_CONCAT( IF(f = t, f, CONCAT(f, '-', t)) ),
CONCAT((@n + 1), '...')
) AS result
FROM (
SELECT (@n + 1) AS f, (id - 1) AS t, @n := id AS c
FROM (SELECT @n := 0) t, test
ORDER BY id
) t2
WHERE t >= f
Функцию LISTAGG можно использовать в следующих версиях Oracle / PLSQL:
Oracle 12c, Oracle 11g Release 2
DROP TABLE TEST;
CREATE TABLE Test ( Id NUMBER NOT NULL );
INSERT INTO Test(id) VALUES (3);
INSERT INTO Test(id) VALUES (5);
INSERT INTO Test(id) VALUES (7);
INSERT INTO Test(id) VALUES (8);
INSERT INTO Test(id) VALUES (9);
INSERT INTO Test(id) VALUES (11);
INSERT INTO Test(id) VALUES (12);
INSERT INTO Test(id) VALUES (16);
INSERT INTO Test(id) VALUES (17);
select nid from
(select ROWNUM nid from Test, Test where ROWNUM <= (select max(id) from Test)+1)
left join Test T on T.id = nid
where T.id is null
order by nid
NID
----------
1
2
4
6
10
13
14
15
18
select rlist =
(
select "text()" =
iif(id1 < id2, convert(varchar(15), id1) +
iif(id1 < (id2 - 1), '-' + convert(varchar(15), id2 - 1), ''), '') +
iif(id3 is null, iif(id1 < id2, ', ', '') + convert(varchar(15), id2 + 1) + '...', ', ')
from
(
select
id1 = isnull(lag(id) over (order by id), 0) + 1,
id2 = id,
id3 = lead(id) over (order by id)
from test
) t
where ( id1 < id2 ) or ( id3 is null )
order by id2
for xml path('')
)
SELECT CONCAT(IFNULL(CONCAT(GROUP_CONCAT(miss_num), ','), '')
, IFNULL(MAX(id) + 1, @start_num)
, '...'
) miss_num
FROM (
SELECT @prev_id prev_id
, CASE
WHEN @prev_id + 1 = id THEN NULL
WHEN @prev_id + 2 = id THEN @prev_id + 1
ELSE CONCAT(@prev_id + 1, '-', id - 1)
END miss_num
, @prev_id := id id
FROM (SELECT @start_num := 1 start_num, @prev_id := @start_num - 1 prev_id) p
, `Test` t
WHERE t.id >= p.start_num
ORDER BY t.id
) t
select
concat(
ifnull(group_concat(if(id-p=1,'',concat(p+1,if(id-p=2,'',concat('-',id-1)),',')) separator ''),'')
,max(id)+1,'...')
s
from (
select @p p
,@p := id id
from (
select 0 id, @rn:=0 rn from dual
union all
select id, @rn:=@rn+1 from test
) v1
) v2
select
listagg(decode(id1,id2,to_char(id1),to_char(id1)||'-'||to_char(id2)),',')
within group(order by id1)s -- просто форматирование
from (
select min(id) id1,max(id) id2 -- это у нас и есть схлопнутые диапазоны, т.е. типа (1,3); (4,4); (7,10)
from (select id,row_number()over(order by id) rn
from test)
group by id - rn -- группируем по дельте от ряда
)
select
listagg(decode(id2
,id1 ,to_char(id1)
,null,to_char(id1)||'...'
,to_char(id1)||'-'||to_char(id2)
)
,',')
within group(order by id1)s -- просто требуемое форматирование
from (
select
id2+1 id1 -- конец предыдущего + 1
,lead(id1) over(order by id1)-1 id2 --начало следующего - 1
from (
select min(id) id1,max(id) id2 -- это у нас и есть схлопнутые диапазоны, т.е. типа (1,3); (4,4); (7,10)
from (select 0 id, 0 rn from dual -- добавляем "начало"
union all
select id,row_number()over(order by id) rn
from test)
group by id - rn
)
)
select listagg(id1||decode(id2
,id1 ,null
,null,'...'
,'-'||id2)
,',')
within group(order by id1)s
from (select max(id)+1 id1
,lead(min(id)) over(order by min(id)) id2
from (select 0 id, 0 rn from dual
union all
select id,row_number()over(order by id) rn from test)
group by id - rn)
Как sql-запросом извлечь из базы данных информацию, которой там нет