2013-05-21 2 views
1

У меня есть эта таблица (ЛИЦА) с 25M строками:MySQL Случайных строк в окрестностях

ID int(10) PK 
points int(6) INDEX 
some other columns 

Я хочу, чтобы показать случайные строки пользователя 4, которые несколько близко друг к другу в точках. Я нашел этот запрос после некоторого поиска и настройки для генерации случайных строк, что впечатляет быстро:

SELECT person_id, points 
FROM persons AS r1 JOIN 
     (SELECT (RAND() * 
        (SELECT MAX(person_id) 
         FROM persons)) AS id) 
     AS r2 
WHERE r1.person_id>= r2.id and points > 0 
ORDER BY r1.person_id ASC 
LIMIT 4 

Так что я запрашиваю это на PHP. Это дает мне большие и быстрые результаты (менее 0,05 секунды при нагревании). Но эти строки действительно просто случайны (с по крайней мере 1 балл с points > 0). Я хотел бы показать несколько строк, которые немного близки, не обязательно каждый раз, но предположим, что я делаю этот запрос с лимитом 50 и выбираю случайную строку в PHP и 3 ближайших строках (на основе точек) рядом с ним. Я думаю, вам нужно будет отсортировать результат, выбрать случайную строку и показать строки после/до нее. Но я понятия не имею, как это сделать, поскольку я совершенно новичок в PHP.

Любых предложения, все отзывы приветствуются :)

+0

запрос идет от 0.05 сек до 2.6213 сек. –

+0

. Тогда дело в оптимизации таблицы, есть ли у вас индекс по точкам? –

+0

Да, у него есть индекс на нем –

ответ

3

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

ALTER TABLE persons ADD INDEX (points); 

SELECT person_id, points 
FROM  persons JOIN (
      SELECT RAND() * MAX(points) AS pivot 
      FROM persons 
      WHERE points > 0 
     ) t ON t.pivot <= points 
ORDER BY points 
LIMIT 4 

Обратите внимание, что этот подход будет выбирать шарнир с использованием равномерного распределения вероятностей в диапазоне значений points; если points очень неоднородны, вы можете в конечном итоге развернуть некоторые значения намного чаще, чем другие (что приводит к кажущимся «неслучайным» результатам).

Чтобы решить эту проблему, вы можете выбрать случайную запись по более равномерно распределенной колонке (возможно, person_id?), А затем использовать значение points этой случайной записи в качестве точки опоры; то есть, заменить следующий подзапрос в отчете выше:

  SELECT points AS pivot 
      FROM  persons JOIN (

         SELECT FLOOR(
           MIN(person_id) 
          + RAND() * (MAX(person_id)-MIN(person_id)) 
          ) AS random 
         FROM persons 
                    WHERE  points > 0 

        ) r ON r.random <= person_id 
      WHERE points > 0 
      ORDER BY person_id 
      LIMIT 1 
+0

Вы должны определенно проверить EXPLAIN там, чтобы увидеть, что он выполняет только один подзапрос один раз, а не для каждой строки. Должен работать, хотя. ;) – ToBe

+0

@eggyal, после тестирования я обнаружил, что выбраны только строки нижних точек, когда у меня есть 300 рядов между 1 и 100 точками. Я получаю только randoms (более 60 раз) с точками от 1 до 20. Как это возможно? –

+0

@eggyal, большое спасибо. Один последний вопрос, похоже, вы знаете намного больше, чем я о SQL :), скажем, у меня 300 человек в таблице, точки от 1 до 100. Предположим, что у 5 человек есть 23 балла в качестве примера, этот запрос всегда будет выбирать первые 4, можно ли дать им все шансы появиться в случайной функции? Поскольку я хочу дать каждому человеку такую ​​же% шанса на показ. –

0

Удаление подзапроса из него будет это значительно повысить производительность и кэширование, чтобы вы могли, например, получить список ваших идентификаторов, поместить его в файл, а затем (например, путем чтения случайных строк из файла). Это улучшит его на много, так как вы можете увидеть, будет ли вы запускать EXPLAIN в этом запросе и сравнить его, изменив запрос на загрузку только данных для 4 (все еще случайных) идентификаторов.

+0

Нет, это неправда, я много читал об этом, и это довольно сложно, но, к примеру, ответ ToBe занимает 3 секунды, а ответ @eggyal занимает 0.006 секунды –

+0

@KevinVermaat, что не так? Что этот подход не будет оптимизировать? Конечно, это будет! Чтение случайных 4 строк из файла происходит намного быстрее, чем выбор 4 случайных идентификаторов из базы данных :). Конечно, это не «sql-way», но он работает. –

+0

Эй, извините, я читал слишком быстро, вы правы, но, к сожалению, мне нужно решение DB. –

0

Я бы предложил сделать два отдельных sql-запроса в PHP, а не объединять их/подзапросы. Во многих случаях оптимизатор не может упростить ваш запрос и должен выполнять каждый отдельно. Итак, в вашем случае. если у вас есть 1000 людей, оптимизатор будет делать следующую wueries в худшем случае:

  • Получить 1000 людей строк
  • Do Sub Выберите для каждого человека, который получает'S 1000 людей рядов
  • Регистрация 1000 людей соединяемых строк в результате в 1.000.000 строк
  • фильтр все они

Короче: 1001 запросов с 1.000.000 строк

Мой совет?

Выполните два запроса и NO присоединяется или суб-выбирает, как и (особенно в сочетании имеет драматическое падение производительности в большинстве случаев)

SELECT person_id, points 
FROM persons 
ORDER BY RAND() LIMIT 1 

Теперь использовать найденные точки для второго запроса

SELECT person_id, points, ABS(points - <POINTS FROM ABOVE>) AS distance 
FROM persons 
ORDER BY distance ASC LIMIT 4 
+0

Ваш первый запрос занял 3.0974 секунды для выполнения. Я много читал о случайных строках для таблиц, это большой, разумно избегать порядка RAND();) –

+0

Альтернатива этому? Btw, решение выше по-прежнему использует RAND(), но как столбец, а не внутри условия. Это можно сделать и здесь, точка этого ответа состоит в том, чтобы разделить случайное получение и получить другие строки на два запроса. ;) – ToBe

+0

Хорошо, я проанализировал вышеупомянутый метод рандомизации @eggyal.Кажется, что это работает хорошо, и в сочетании с добавленной математикой для получения 4 предметов в радиусе действия, это должен быть путь. Публичное мнение по-прежнему кажется, что PHP может быть вопиющим во многих случаях, может быть, не в этом ... – ToBe

Смежные вопросы