SQL-запрос для единственного (равномерно случайного) значения с плавающей точкой, если могут существовать дублирующие значения с плавающей точкой - PullRequest
0 голосов
/ 22 мая 2019

Учитывая таблицу с полем с плавающей точкой, которая проиндексирована (и допускает дублирование), я предполагаю, что могу запросить строку на основе следующего ближайшего значения с плавающей запятой, используя:

SELECT * FROM my_table WHERE my_float_column > 0.1234 ORDER BY my_float_column LIMIT 1

Я хочу запросить случайную строку из этой таблицы равномерно в диапазоне [0, 1], для этого я предлагаю сгенерировать равномерно случайное число с плавающей точкой и подставить его в запрос выше.

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

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

Есть ли лучшее решение для запроса случайным единообразным способом? Или есть решение, обеспечивающее получение случайной строки с LIMIT 1 в этих условиях?

1 Ответ

1 голос
/ 22 мая 2019

Во-первых, если вы хотите равенства, вам нужно равенство:

SELECT *
FROM my_table
WHERE my_float_column >= 0.1234
LIMIT 1

В противном случае вы никогда не получите 0.5, если передадите 0.5. Также обратите внимание, что представления с плавающей запятой могут быть сложными для сравнения. 0.5 имеет точное представление, но большинство других значений являются приближениями, которые могут вызвать проблемы для сравнений на равенство. , , но я собираюсь игнорировать это.

Значения с плавающей точкой также не годятся для представления равномерного распределения. Существует намного больше чисел с плавающей запятой (как представлено в типе данных) между 0 и 0,00001, чем между 0,99999 и 1,00000. Числа с фиксированной точкой могут быть лучшим представлением в таком диапазоне.

И все это говорит о том, что на самом деле ваш вопрос - это случайность для равных значений. Во-первых, ваш метод имеет недостатки, потому что вы просто возвращаете любое значение, большее, чем вы хотите. Это более правильно:

SELECT *
FROM my_table
WHERE my_float_column >= 0.1234
ORDER BY my_float_column DESC
LIMIT 1;

И, чтобы получить случайность, используйте rand() в качестве второго ключа:

ORDER BY my_float_column, rand()

Для производительности я бы порекомендовал индекс на my_float_column. rand() является фактором снижения производительности, поскольку он запрещает использование индекса, но вы можете изменить запрос:

SELECT t.*
FROM my_table t
WHERE t.my_float_column >= 0.1234 AND
      t.my_float_column <= (SELECT COALESCE(MIN(t2.my_float_column), t.my_float_column))
                            FROM my_table t2
                            WHERE t2.my_float_column > 0.1234
                           )
ORDER BY my_float_column DESC, rand()
LIMIT 1;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...