Вычитать списки строк в Oracle SQL - PullRequest
0 голосов
/ 22 января 2019

У меня есть две таблицы с пользователем в своих сервисах.Из-за размера таблицы user_services преобразование в строки не очень практично (большинство пользователей имеют сотни сервисов).Как я могу вычесть один список из другого?

user_services (aprox 2mi recrods)
USER    SERVICES    
Rick    1,3,2,66,19
Jerry   1,2,19

serices_remove
serv_rm
1,16,19,32

output
USER    SERVICES
Rick    3,2,66
Jerry   2

Я использую Oracle 12c.

1 Ответ

0 голосов
/ 22 января 2019

Я бы по-прежнему предлагал преобразовать значение, разделенное запятыми, в строки. Управление этими столбцами CSV на обоих концах запроса является утомительным, подверженным ошибкам и неэффективным. При такой настройке вы не сможете использовать индексы.

С другой стороны, рассмотрим:

  • таблицы поиска, в которых каждая запись хранит идентификатор пользователя и (одну) связанную службу
  • таблица исключений, где каждая запись представляет службу для удаления

В большом наборе данных сканирование индекса происходит на порядок быстрее, чем полное сканирование: при наличии соответствующего индекса требуется всего 25 сравнений, чтобы найти соответствующую запись из 10 миллионов строк (log2 из 10 M равен 23,5).

Это также позволит вам упростить ваш запрос как:

SELECT l.*
FROM lookup_table l
LEFT JOIN exclusion_table e ON e.service = l.service
WHERE e.service IS NULL

Или:

SELECT l.* 
FROM lookup_table l
WHERE NOT EXISTS (
    SELECT 1 FROM exclusion_table e WHERE e.service = l.service
)

Если требуется агрегирование для каждого пользователя, оно может быть обработано с использованием функции SQL (или еще лучше на уровне представления).

Совет: вот запрос, который вы можете использовать для инициализации вашей новой таблицы; он разбивает столбец CSV на новые строки:

select distinct usr, trim(regexp_substr(serv, '[^,]+', 1, level)) serv
from (SELECT usr, services serv FROM user_services) t
connect by instr(serv, ',', 1, level - 1) > 0

См. это дБ <> скрипка

 with user_services as (
     select 'Rick' usr, '1,3,2,66,19' services from dual
     union select 'Jerry', '1,2,19' from dual
 )
 select distinct usr, trim(regexp_substr(serv, '[^,]+', 1, level)) serv
 from (SELECT usr, services serv FROM user_services) t
 connect by instr(serv, ',', 1, level - 1) > 0
      order by 1,2

 USR   | SERV
 :---- | :---
 Jerry | 1<br>
 Jerry | 19<br>
 Jerry | 2<br>
 Rick  | 1<br>
 Rick  | 19<br>
 Rick  | 2<br>
 Rick  | 3<br>
 Rick  | 66<br>
 
...