Есть ли способ оптимизировать этот запрос (после добавления rand () требуется слишком много времени для ограничения запроса)? [Дубликат]

Нет, не разбит, но большинство десятичных дробей должно быть аппроксимировано

Резюме

Арифметика с плавающей точкой точный, к сожалению, он не очень хорошо сочетается с нашим обычным представлением числа base-10, так что получается, что мы часто даем ему ввод, который немного от того, что мы написали.

Даже простые числа, такие как 0.01, 0.02, 0.03, 0.04 ... 0.24, не представляются точно как двоичные дроби, даже если в мантиссе были тысячи бит точности, даже если у вас были миллионы. Если вы отсчитываете с шагом 0,01, пока вы не достигнете 0,25, вы получите первую фракцию (в этой последовательности), представленную в base10 и base2. Но если вы попытались использовать FP, ваш 0,01 был бы слегка отключен, поэтому единственный способ добавить 25 из них до хорошего точного 0.25 потребовал бы длинной цепи причинности, включающей защитные биты и округление.

Мы постоянно даем аппарату FP что-то вроде простого в базе 10, но это повторяющаяся фракция в базе 2.

Как это произошло?

Когда мы пишем в десятичной форме, каждая дробь является рациональным числом форма

& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; & nbsp; x / (2n + 5n).

В двоичном выражении мы получаем только член 2n , то есть:

& nbsp ; & NBSP; & NBSP; & NBSP; & NBSP; & NBSP; & NBSP; & NBSP; & NBSP; & NBSP; x / 2n

Итак, в десятичной форме мы не можем представлять 1/3. Поскольку база 10 включает в себя 2 как простой коэффициент, каждое число, которое мы можем записать как двоичную дробь , также может быть записано в виде базовой дроби. Однако вряд ли что-либо, что мы пишем как base10, представляется в двоичном виде. В диапазоне от 0,01, 0,02, 0,03 ... 0,99 только цифры три могут быть представлены в нашем формате FP: 0,25, 0,50 и 0,75, поскольку они равны 1/4, 1/2, и 3/4 - все числа с простым множителем, использующим только 2n-член.

В базе 10 мы не можем представлять 1/3. Но в двоичном коде мы не можем делать 1/10 или 1/3.

Так что, хотя каждая двоичная дробь может быть записана в десятичной системе, обратное неверно. И фактически большинство десятичных дробей повторяются в двоичном формате.

Работа с ним

Разработчикам обычно дают указание & Lt; epsilon , лучшим советом может быть округление до целочисленных значений (в библиотеке C: round () и roundf (), т. е. оставаться в формате FP), а затем сравнивать. Округление до определенной длины десятичной дроби решает большинство проблем с выходом.

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

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

Мне нравится ответ на пиццу от Chris , потому что он описывает фактическую проблему, а не только обычная ручная работа о «неточности». Если бы FP были просто «неточными», мы могли бы исправить , что и сделали бы это несколько десятилетий назад. Причина, по которой у нас нет, - это то, что формат FP компактен и быстр, и это лучший способ хрустить множество чисел. Кроме того, это наследие космической эры и гонки вооружений и ранние попытки решить большие проблемы с очень медленными компьютерами с использованием небольших систем памяти. (Иногда отдельные магнитные сердечники для 1-битного хранилища, но это другая история. )

Заключение

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

86
задан Benjamin 16 February 2016 в 22:02
поделиться

8 ответов

Попробуйте это:

SELECT  *
FROM    (
        SELECT  @cnt := COUNT(*) + 1,
                @lim := 10
        FROM    t_random
        ) vars
STRAIGHT_JOIN
        (
        SELECT  r.*,
                @lim := @lim - 1
        FROM    t_random r
        WHERE   (@cnt := @cnt - 1)
                AND RAND(20090301) < @lim / @cnt
        ) i

Это особенно эффективно на MyISAM (так как COUNT(*) мгновенно), но даже в InnoDB он 10 раз более эффективен, чем ORDER BY RAND().

Основная идея заключается в том, что мы не сортируем, а вместо этого сохраняем две переменные и вычисляем running probability строки, которая будет выбрана на текущем шаге.

См. Эту статью в моем блоге для более подробной информации:

Обновление:

Если вам нужно выбрать только одну случайную запись, попробуйте следующее:

SELECT  aco.*
FROM    (
        SELECT  minid + FLOOR((maxid - minid) * RAND()) AS randid
        FROM    (
                SELECT  MAX(ac_id) AS maxid, MIN(ac_id) AS minid
                FROM    accomodation
                ) q
        ) q2
JOIN    accomodation aco
ON      aco.ac_id =
        COALESCE
        (
        (
        SELECT  accomodation.ac_id
        FROM    accomodation
        WHERE   ac_id > randid
                AND ac_status != 'draft'
                AND ac_images != 'b:0;'
                AND NOT EXISTS
                (
                SELECT  NULL
                FROM    accomodation_category
                WHERE   acat_id = ac_category
                        AND acat_slug = 'vendeglatohely'
                )
        ORDER BY
                ac_id
        LIMIT   1
        ),
        (
        SELECT  accomodation.ac_id
        FROM    accomodation
        WHERE   ac_status != 'draft'
                AND ac_images != 'b:0;'
                AND NOT EXISTS
                (
                SELECT  NULL
                FROM    accomodation_category
                WHERE   acat_id = ac_category
                        AND acat_slug = 'vendeglatohely'
                )
        ORDER BY
                ac_id
        LIMIT   1
        )
        )

Это предполагает, что ваши ac_id распределены более или менее равномерно.

62
ответ дан Quassnoi 27 August 2018 в 01:12
поделиться

Вот как я это сделаю:

SET @r := (SELECT ROUND(RAND() * (SELECT COUNT(*)
  FROM    accomodation a
  JOIN    accomodation_category c
    ON (a.ac_category = c.acat_id)
  WHERE   a.ac_status != 'draft'
        AND c.acat_slug != 'vendeglatohely'
        AND a.ac_images != 'b:0;';

SET @sql := CONCAT('
  SELECT  a.ac_id,
        a.ac_status,
        a.ac_name,
        a.ac_status,
        a.ac_images
  FROM    accomodation a
  JOIN    accomodation_category c
    ON (a.ac_category = c.acat_id)
  WHERE   a.ac_status != ''draft''
        AND c.acat_slug != ''vendeglatohely''
        AND a.ac_images != ''b:0;''
  LIMIT ', @r, ', 1');

PREPARE stmt1 FROM @sql;

EXECUTE stmt1;
1
ответ дан Bill Karwin 27 August 2018 в 01:12
поделиться

Это зависит от того, насколько вы должны быть случайными. Решение, с которым вы связаны, очень хорошо работает с ИМО. Если у вас большие пробелы в поле идентификатора, это все равно довольно случайным.

Однако вы должны иметь возможность сделать это в одном запросе, используя это (для выбора одного значения):

SELECT [fields] FROM [table] WHERE id >= FLOOR(RAND()*MAX(id)) LIMIT 1

Другие решения:

  • Добавьте в таблицу постоянное поле float, называемое random, и заполните его случайными числами. Затем вы можете сгенерировать случайное число в PHP и сделать "SELECT ... WHERE rnd > $random"
  • Захватить весь список идентификаторов и кэшировать их в текстовом файле. Прочитайте файл и выберите из него случайный идентификатор.
  • Кэш результатов запроса как HTML и сохраните его в течение нескольких часов.
13
ответ дан DisgruntledGoat 27 August 2018 в 01:12
поделиться

Я оптимизирую много существующих запросов в моем проекте. Решение Quassnoi помогло мне ускорить запросы! Однако мне сложно включить упомянутое решение во все запросы, особенно для сложных запросов, связанных с множеством подзапросов на нескольких больших таблицах.

Поэтому я использую менее оптимизированное решение. По сути, он работает так же, как и решение Квасноя.

SELECT  accomodation.ac_id,
        accomodation.ac_status,
        accomodation.ac_name,
        accomodation.ac_status,
        accomodation.ac_images
FROM    accomodation, accomodation_category
WHERE   accomodation.ac_status != 'draft'
        AND accomodation.ac_category = accomodation_category.acat_id
        AND accomodation_category.acat_slug != 'vendeglatohely'
        AND ac_images != 'b:0;'
        AND rand() <= $size * $factor / [accomodation_table_row_count]
LIMIT $size

$size * $factor / [accomodation_table_row_count] определяет вероятность выбора случайной строки. Rand () будет генерировать случайное число. Строка будет выбрана, если rand () меньше или равно вероятности. Это эффективно выполняет случайный выбор, чтобы ограничить размер таблицы. Поскольку есть вероятность, что он вернет меньше заданного предела, нам нужно увеличить вероятность, чтобы мы выбрали достаточно строк. Следовательно, мы умножаем $ size на $ factor (обычно я устанавливаю $ factor = 2, работает в большинстве случаев). Наконец, мы выполняем limit $size

. В настоящее время проблема заключается в том, что мы выставляем класс accomodation_table_row_count. Если мы знаем размер таблицы, мы МОЖЕМ жестко закодировать размер таблицы. Это будет работать быстрее, но, очевидно, это не идеально. Если вы используете Myisam, получение таблицы считается очень эффективным. Поскольку я использую innodb, я просто делаю простой подсчет + выбор. В вашем случае это будет выглядеть так:

SELECT  accomodation.ac_id,
        accomodation.ac_status,
        accomodation.ac_name,
        accomodation.ac_status,
        accomodation.ac_images
FROM    accomodation, accomodation_category
WHERE   accomodation.ac_status != 'draft'
        AND accomodation.ac_category = accomodation_category.acat_id
        AND accomodation_category.acat_slug != 'vendeglatohely'
        AND ac_images != 'b:0;'
        AND rand() <= $size * $factor / (select (SELECT count(*) FROM `accomodation`) * (SELECT count(*) FROM `accomodation_category`))
LIMIT $size

Сложная часть - это правильная вероятность. Как вы можете видеть, следующий код фактически вычисляет только приблизительный размер таблицы темпа (на самом деле, слишком грубый!): (select (SELECT count(*) FROM accomodation) * (SELECT count(*) FROM accomodation_category)) Но вы можете уточнить эту логику, чтобы приблизиться к приближению размера таблицы. Обратите внимание, что лучше выбрать OVER-select, чем выбирать строки. т. е. если вероятность установлена ​​слишком низко, вы рискуете не выбирать достаточное количество строк.

Это решение работает медленнее, чем решение Quassnoi, поскольку нам нужно пересчитать размер таблицы. Тем не менее, я считаю, что это кодирование намного более управляемо. Это компромисс между точностью + производительность и сложностью кодирования. Сказав это, на больших таблицах это намного быстрее, чем Order by Rand ().

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

0
ответ дан lawrenceshen 27 August 2018 в 01:12
поделиться

Это даст вам один дополнительный запрос, который будет использовать индекс для получения случайного идентификатора, тогда другой запрос будет запускать вашу объединенную таблицу.

SELECT  accomodation.ac_id,
        accomodation.ac_status,
        accomodation.ac_name,
        accomodation.ac_status,
        accomodation.ac_images
FROM    accomodation, accomodation_category
WHERE   accomodation.ac_status != 'draft'
        AND accomodation.ac_category = accomodation_category.acat_id
        AND accomodation_category.acat_slug != 'vendeglatohely'
        AND ac_images != 'b:0;'
AND accomodation.ac_id IS IN (
        SELECT accomodation.ac_id FROM accomodation ORDER BY RAND() LIMIT 1
)
0
ответ дан PengOne 27 August 2018 в 01:12
поделиться

(Да, я получу пилинг за то, что у меня недостаточно мяса, но разве вы не можете быть веганом в течение одного дня?)

Случай: последовательный АВТО_ИНКРИМЕТ без пробелов, 1 строка возвращается Случай: последовательный AUTO_INCREMENT без пробелов, 10 строк. Случай: AUTO_INCREMENT с пробелами, возвращена 1 строка. Случай: дополнительный столбец FLOAT для случайной случайности. Случай: столбец UUID или MD5

. Эти 5 случаев могут быть сделаны очень эффективными для больших таблиц. Подробнее см. В моем блоге .

0
ответ дан Rick James 27 August 2018 в 01:12
поделиться
function getRandomRow(){
    $id = rand(0,NUM_OF_ROWS_OR_CLOSE_TO_IT);
    $res = getRowById($id);
    if(!empty($res))
    return $res;
    return getRandomRow();
}

//rowid is a key on table
function getRowById($rowid=false){

   return db select from table where rowid = $rowid; 
}
-1
ответ дан Stephen Rauch 27 August 2018 в 01:12
поделиться

Решение для вашего фиктивного примера будет:

SELECT  accomodation.ac_id,
        accomodation.ac_status,
        accomodation.ac_name,
        accomodation.ac_status,
        accomodation.ac_images
FROM    accomodation,
        JOIN 
            accomodation_category 
            ON accomodation.ac_category = accomodation_category.acat_id
        JOIN 
            ( 
               SELECT CEIL(RAND()*(SELECT MAX(ac_id) FROM accomodation)) AS ac_id
            ) AS Choices 
            USING (ac_id)
WHERE   accomodation.ac_id >= Choices.ac_id 
        AND accomodation.ac_status != 'draft'
        AND accomodation_category.acat_slug != 'vendeglatohely'
        AND ac_images != 'b:0;'
LIMIT 1

Чтобы узнать больше об альтернативах ORDER BY RAND(), вы должны прочитать эту статью .

0
ответ дан tereško 27 August 2018 в 01:12
поделиться
Другие вопросы по тегам:

Похожие вопросы: