Попытка создать подмножество из оператора IN - PullRequest
2 голосов
/ 02 августа 2011

Я пишу решение для пользователя, которое сопоставляет список телефонных номеров, которые они вводят с базой данных клиентов.

Пользователь должен ввести разделенный запятыми список телефонных номеров (целые числа) и запроснеобходимо сообщить пользователю, какие телефонные номера из их списка НЕ ​​находятся в базе данных.

Единственный способ, которым я мог бы подумать, - это сначала создать подмножество NUMBER_LIST, которое включает все телефонные номера, к которым я могу присоединиться.а затем исключить этот список из того, что я возвращаю из своей базы данных клиентов.

WITH NUMBER_LIST AS (
    SELECT INTEGERS 
    FROM (
        SELECT level - 1 + 8000000000 INTEGERS
           FROM dual
        CONNECT BY level <= 8009999999-8000000000+1
    )
    WHERE INTEGERS IN (8001231001,8001231003,8001231234,8001231235,...up to 1000 phone numbers)
)

Проблема в том, что приведенный выше код прекрасно работает для создания моего подмножества, для чисел от 800-000-0000 до 800-999-9999.Номера телефонов в моем списке и базе данных клиентов могут иметь ЛЮБОЙ диапазон (не только 800 номеров).Я сделал это просто в качестве теста.Генерация подмножества из этого запроса занимает около 6 секунд.Если я создаю CONNECT BY LEVEL, чтобы включить все числа от 100-000-0000 до 999-999-9999, на которых мой запрос выполняется из памяти, чтобы создать такое большое подмножество (и я считаю, что создание огромного списка смехотворно излишнеи разбить его, используя мой оператор IN).

Проблема заключается в создании начального подмножества.Я могу обработать остальную часть запроса, но мне нужно иметь возможность генерировать подмножество чисел для запроса по моей базе данных клиентов из моего оператора IN.

Несколько вещей, которые нужно запомнить:

  1. У меня нет возможности сначала загружать числа во временную таблицу.Пользователь сам будет вводить оператор «IN (..., ..., ...)».
  2. Это должен быть один оператор, без дополнительных функций или объявлений переменных
  3. База данных - Oracle 10g, и я использую SQL Developer для создания запроса.
  4. Пользователь понимает, что он может ввести только 1000 чисел в оператор IN.Это должно быть достаточно надежно, чтобы выбрать любые 1000 номеров из всего диапазона кодов города.
  5. Конечный результат - получить список телефонных номеров, которых НЕ в базе данных.Простое NOT IN ... не будет работать, потому что это вернет, какие числа находятся в базе данных, но не в моем списке.

Как я могу сделать эту работу для всех чисел между 1000000000-9999999999 (или все 10-значный номер телефона США).Возможно, я ошибаюсь, создав свой первоначальный ОГРОМНЫЙ список, а затем исключаю все, кроме моего оператора IN, но я не уверен, куда идти.

Большое спасибо за вашу помощь заранее,Я многому научился у всех вас.

Ответы [ 4 ]

3 голосов
/ 02 августа 2011

Вы можете использовать следующее:

SELECT *
  FROM (SELECT regexp_substr(&x, '[^,]+', 1, LEVEL) phone_number
           FROM dual
         CONNECT BY LEVEL <= length(&x) - length(REPLACE(&x, ',', '')) + 1)
 WHERE phone_number NOT IN (SELECT phone_table.phone_number 
                              FROM phone_table)

Первый запрос создаст список с отдельными телефонными номерами.

2 голосов
/ 02 августа 2011

Эта проблема очень тесно связана с проблемой «как связать список», которая возникала здесь несколько раз. Я отправил ответ Динамический запрос с HibernateCritera API & Oracle - производительность в прошлом.

Что-то вроде этого должно делать то, что вы хотите:

create table phone_nums (phone varchar2(10));

insert into phone_nums values ('12345');

insert into phone_nums values ('23456');

with bound_inlist
  as
  (
  select
    substr(txt,
           instr (txt, ',', 1, level  ) + 1,
           instr (txt, ',', 1, level+1) - instr (txt, ',', 1, level) -1 )
           as token
    from (select ','||:txt||',' txt from dual)
  connect by level <= length(:txt)-length(replace(:txt,',',''))+1
  )
  select *
from bound_inlist a
where  not exists (select null from phone_nums where phone = token); 

Здесь список номеров телефонов, разделенных запятыми, привязан к запросу, поэтому вы правильно используете переменные связывания, и вы сможете ввести, возможно, неограниченное количество телефонных номеров для проверки за один раз (хотя я бы проверил оба границы символов 4000 и 32767, чтобы быть уверенным).

1 голос
/ 02 августа 2011

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

Какой клиентский инструмент используется для отправки этого запроса? Есть ли причина, по которой вы не можете запросить все телефонные номера из базы данных и выполнить сравнение на клиенте?

1 голос
/ 02 августа 2011

Если вы ограничены тем, что это ДОЛЖНО быть решено с помощью IN (n1,n2,n3,...,n1000), тогда ваш подход будет единственным решением.

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

Вы можете немного адаптировать свой подход?

WITH NUMBER_LIST (number) AS (
            SELECT n1    FROM DUAL
  UNION ALL SELECT n2    FROM DUAL
  UNION ALL SELECT n3    FROM DUAL
  ...
  UNION ALL SELECT n1000 FROM DUAL
)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...