powered by simpleCommunicator - 2.0.60     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Sybase ASA, ASE, IQ [игнор отключен] [закрыт для гостей] / Аналог top при группировке
6 сообщений из 6, страница 1 из 1
Аналог top при группировке
    #34849423
Bepcyc
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Такая задача:

Есть таблица контактов (CONTACT), в ней есть поле ID клиента и поле номера его телефона - BRIEF (текстовое). Каждый клиент может иметь сколько угодно телефонов, но в конечную выгрузку должно попасть максимум 5 телефонов.

Вопрос, как это сделать без курсора?

Если бы top можно было использовать внутри group by, то это бы подошло, но он так не работает ;(
Если использовать HAVING, то получим только тех у кого не более 5 телефонов ;(
...
Рейтинг: 0 / 0
Аналог top при группировке
    #34849545
Используй нумератор внутри группы.
Ты не указал версию и тип сервера.
Могу предложить вариант с аналитическими (оконными) функциями.
Код: plaintext
1.
2.
3.
4.
5.
select * 
  from (
          select id, BRIEF, 
                 row_number() over(partition by ID order by BRIEF) as rn
            from CONTACT
       ) v
...
Рейтинг: 0 / 0
Аналог top при группировке
    #34850090
Bepcyc
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Извиняюсь

ASE 12.5.3

здесь нет row_number() ;((
...
Рейтинг: 0 / 0
Аналог top при группировке
    #34850470
москит
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Есть такой "фокус", который строится на том, что при insert во временную таблицу с identity запросом с order by, мы не получаем ни "дырок" в identity, и сортировка по identity даёт сортировку, которая использовалась при insert.

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
create table #test(id int identity, id_client int, Phone varchar( 20 ))

insert #test
select  1 , '123'
union all
select  1 , '456'
union all
select  1 , '789'
union all
select  1 , '012'
union all
select  2 , '585'
union all
select  3 , '111'
union all
select  3 , '222'
union all
select  3 , '333'
order by  1 ,  2 

create index index1 on #test (id)
go

-- не больше двух тел. номеров
select id_client, Phone
from #test t1 
where not exists(select  1  from #test t2 (index index1) where t2.id = t1.id +  2  and t2.id_client = t1.id_client)

drop table #test
go
...
Рейтинг: 0 / 0
Аналог top при группировке
    #34850839
Bepcyc
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
москитЕсть такой "фокус", который строится на том, что при insert во временную таблицу с identity запросом с order by, мы не получаем ни "дырок" в identity, и сортировка по identity даёт сортировку, которая использовалась при insert.

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
create table #test(id int identity, id_client int, Phone varchar( 20 ))

insert #test
select  1 , '123'
union all
select  1 , '456'
union all
select  1 , '789'
union all
select  1 , '012'
union all
select  2 , '585'
union all
select  3 , '111'
union all
select  3 , '222'
union all
select  3 , '333'
order by  1 ,  2 

create index index1 on #test (id)
go

-- не больше двух тел. номеров
select id_client, Phone
from #test t1 
where not exists(select  1  from #test t2 (index index1) where t2.id = t1.id +  2  and t2.id_client = t1.id_client)

drop table #test
go


Очень красивое решение!
Сначала подумал, что неверное, но потом осознал всю хитрость этого + 2 ;)
Спасибо огромное, сделаю так.

Конечно же, интересно было бы придумать решение без временной таблицы, но это уже чисто спортивный интерес и мне даже кажется, что в данном случае нереализуемый.
...
Рейтинг: 0 / 0
Аналог top при группировке
    #34852932
Бабичев Сергей
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
BepcycКонечно же, интересно было бы придумать решение без временной таблицы, но это уже чисто спортивный интерес и мне даже кажется, что в данном случае нереализуемый.А что его придумывать? Решение давным давно было придумано. И существует два метода его построения:
1) self-join таблицы с последующей группировкой,
2) коррелированный скалярный подзапрос во фразе SELECT + derived-table.
Одно НО - оба варианта достаточно накладные для сервера.

Вот тебе пара примеров. Обрати внимание на столбец RN - по сути, это есть сквозной нумератор телефонов в пределах одного ID. Для каждого абонента этот столбец будет нумеровать телефоны от единицы и до тех пор, пока телефоны не закончатся.

На примере тестовых данных от москита:
IDPHONE10121123145617892585311132223333

1) Вариант № 1 - self-join + group by:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
-- I вариант (self-join + group by):
select t1.*, 
       count( 1 ) as rn -- Данный столбец будет нумеровать телефоны в пределах каждого ID
  from test t1
  join test t2
    on t1.id = t2.id 
   and t1.phone >= t2.phone
 group by t1.id, t1.phone
 order by t1.id, t1.phone

Query finished, retrieving results...

ID   PHONE   RN
--   -----   --
  1     012        1  
  1     123        2  
  1     456        3  
  1     789        4  
  2     585        1  
  3     111        1  
  3     222        2  
  3     333        3  

 8  row(s) retrieved

--
-- Соответственно, для того, чтобы получить не более 2-х телефонов для каждого человека запрос примет вид:
select t1.*, count( 1 ) as rn
  from test t1
  join test t2
    on t1.id = t2.id 
   and t1.phone >= t2.phone
 group by t1.id, t1.phone
 having count( 1 ) <=  2 
order by t1.id, t1.phone

Query finished, retrieving results...

ID   PHONE   RN
--   -----   --
  1     012        1  
  1     123        2  
  2     585        1  
  3     111        1  
  3     222        2  

 5  row(s) retrieved



2) Вариант №2 - коррелированный скалярный подзапрос во фразе SELECT + derived-table:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
select t1.*, 
       (
         select count( 1 )
           from test t2
          where t1.id = t2.id 
            and t1.phone >= t2.phone
       ) as rn
  from test t1
order by id, phone

Query finished, retrieving results...

ID   PHONE   RN
--   -----   --
  1     012        1  
  1     123        2  
  1     456        3  
  1     789        4  
  2     585        1  
  3     111        1  
  3     222        2  
  3     333        3  

 8  row(s) retrieved

--
-- Соответственно, для того, чтобы получить не более 2-х телефонов для каждого человека запрос примет вид:
select *
  from (
         select t1.*, 
                (
                  select count( 1 )
                    from test t2
                   where t1.id = t2.id 
                     and t1.phone >= t2.phone
                ) as rn
           from test t1) v
 where rn <=  2  
order by id, phone

Query finished, retrieving results...

ID   PHONE   RN
--   -----   --
  1     012        1  
  1     123        2  
  2     585        1  
  3     111        1  
  3     222        2  

 5  row(s) retrieved
...
Рейтинг: 0 / 0
6 сообщений из 6, страница 1 из 1
Форумы / Sybase ASA, ASE, IQ [игнор отключен] [закрыт для гостей] / Аналог top при группировке
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]