Генерация случайного значения, которого нет в другой таблице MySQL - PullRequest
0 голосов
/ 04 ноября 2018

У меня проблема с синтаксисом SQL. Я хочу добавить случайный номер телефона между 10000 и 99999. У меня пока есть этот запрос, но я не могу запустить его в PHPMyAdmin:

SELECT round(RAND()*99999) AS `NUM` 
WHERE `NUM` NOT IN (SELECT `PHONE_NUMBER` FROM `PHONE`);

Это ошибка:

1064 - Что-то не так в его синтаксисе рядом с 'WHERE NUM NOT IN (SELECT PHONE_NUMBER FROM PHONE)' в строке 1

Ответы [ 3 ]

0 голосов
/ 05 ноября 2018

Проблема с ответом @GordonLinoff заключается в том, что он, возможно, не возвращает ничего, кроме числа, которого нет в другой таблице. Есть несколько способов обойти это: использовать сохраненную функцию или таблицу чисел. Вот как это сделать с сохраненной функцией:

DELIMITER //
DROP FUNCTION IF EXISTS get_random_phone //
CREATE FUNCTION get_random_phone(min INT, max INT) RETURNS INT
NOT DETERMINISTIC
BEGIN
  DECLARE num INT;
  DECLARE finished INT DEFAULT 0;
  DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished = 1;
  REPEAT
    SET num = min + FLOOR(RAND() * (max - min + 1));
    SELECT phone_number INTO num FROM phone WHERE phone_number = num;
  UNTIL finished = 1
  END REPEAT;
  RETURN num;
END //

Тогда ваш запрос просто становится

SELECT get_random_phone(10000, 99999)

В качестве альтернативы вы можете создать таблицу чисел и LEFT JOIN для таблицы телефонных номеров, выбрав только строки без совпадений, а затем упорядочив по RAND() и используя LIMIT 1, чтобы вернуть только один результат. Обратите внимание, что это может быть довольно медленно без индексов на num и phone_number.

CREATE TABLE numbers (num INT PRIMARY KEY) AS
SELECT n1.n + n10.n*10 + n100.n*100 + n1000.n*1000 + n10000.n*10000 AS num FROM
(SELECT 0 AS n UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4
 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) n1
CROSS JOIN
(SELECT 0 AS n UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4
 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) n10
CROSS JOIN
(SELECT 0 AS n UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4
 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) n100
CROSS JOIN
(SELECT 0 AS n UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4
 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) n1000
CROSS JOIN
(SELECT 0 AS n UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4
 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) n10000
ORDER BY NUM

В этом случае ваш запрос будет:

SELECT num
FROM numbers
LEFT JOIN phone ON phone.phone_number = numbers.num
WHERE phone.phone_number IS NULL AND num BETWEEN 10000 AND 99999
ORDER BY RAND()
LIMIT 1
0 голосов
/ 05 ноября 2018

Не собираюсь на работу. Самый близкий подход был бы следующим: защитить ваш первый запрос, заключив его в подзапрос:

SELECT NUM
    FROM (SELECT 10000 + ROUND(RAND()*89999) AS NUM) AS SUBQ
    WHERE NUM NOT IN (SELECT PHONE_NUMBER FROM PHONE);

Но, хотя он выбирает один номер между 10000 и 99999, он сможет проверить его не только , а затем . Если проверка завершится неудачно, запрос ничего не даст, и вам придется повторить его (или усложнить его с помощью UNION, что все еще не дает абсолютной гарантии).

Другой возможностью было бы создание другой таблицы с всеми числами от 10000 до 99999 и выполнение LEFT JOIN, запрашивающее, чтобы правая сторона была NULL. Затем вы должны выбрать случайную запись из этого результата; Я не уверен, но в этом случае вам, возможно, придется запустить ORDER BY RAND (), а затем LIMIT 1, чтобы извлечь одну запись, после возможного выбора девятисот тысяч. Это будет дорого (но см. Ответ в конце).

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

В противном случае вы можете предварительно заполнить таблицу телефона всеми номерами (таблица уже значительно заполнена, так что это не большие дополнительные расходы), добавив столбец, в котором указано, что номер «свободен». Затем вы выбираете случайную запись из таблицы с условием, что свободный маркер дает значение true. В этом случае вы захотите прочитать этот ответ .

0 голосов
/ 05 ноября 2018

Вы можете сделать:

SELECT NUM
FROM (SELECT round(RAND()*99999) AS `NUM`) x 
WHERE `NUM` NOT IN (SELECT `PHONE_NUMBER` FROM `PHONE`);

NUM неизвестно в предложении WHERE. И вы не можете иметь WHERE без FROM.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...