2013-09-25 2 views
0

Я пытаюсь сделать эффективный SQL-код для MySQL, чтобы получить некоторые значения, но в случайном порядке и в разной сумме. Проблема в том, что таблицы довольно большие (~ 4 м строк, ~ 400 МБ), и у меня нет много времени для этого (на данный момент для каждой попытки требуется около 1-2 минут). Кроме того, есть индекс для каждого из столбцов, но не UNIQUE, и это строковое значение, а не auto-inc val.MySQL: UNION и многие ORDER BY RANDOM

производящая Im длинный SQL запрос:

(SELECT fieldA,'id1' AS id FROM myTable WHERE (fieldB LIKE 'xxxx:%') ORDER BY RAND() LIMIT 7) 
UNION ALL 
(SELECT fieldA,'id2' AS id FROM myTable WHERE (fieldB ='123123') ORDER BY RAND() LIMIT 5) 
etc... 

Я хотел бы заказать эту таблицу только один раз (это занимает так много времени). Я уже пробовал:

и У меня была только удача с последним (предложение III от OP), но «магическое» число из 16 не делает трюка - это хорошо для небольших таблиц, а не для таблиц с ~ 4000000 строк.

Это Ouput образца EXPLAIN:

id select_type  table type possible_keys key  key_len  ref  rows Extra 
1 PRIMARY  myTable  range fieldB  fieldB  143  NULL 64198 Using where; Using temporary; Using filesort 
2 UNION myTable  ALL  NULL NULL NULL NULL 4386943  Using where; Using temporary; Using filesort 
3 UNION myTable  range fieldB  fieldB  143  NULL 34374 Using where; Using temporary; Using filesort 
4 UNION myTable  ref  fieldB  fieldB  143  const 1999 Using where; Using temporary; Using filesort 
5 UNION myTable  range fieldB  fieldB  143  NULL 1 Using where; Using temporary; Using filesort NULL 
UNION RESULT <union1,2,3,4,5> ALL  NULL NULL NULL NULL NULL  

Так что я думаю, что ORDER BY RAND главная проблема - это «Использование временного, используя FileSort» для каждого UNION частей.

определение Таблица:

CREATE TABLE IF NOT EXISTS `myTable` (
    `fieldA` varchar(42) NOT NULL, 
    `XYZ` varchar(36) NOT NULL, 
    `fieldB` varchar(47) NOT NULL, 
    KEY `fieldA` (`fieldA`), 
    KEY `XYZ` (`XYZ`), 
    KEY `fieldB` (`fieldB`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8; 

Он хранит только простые, короткие строки, но многие из них.

Любые советы, meaby есть другой подход?

@edit, прямо сейчас Im используя как MySQL и PHP для того чтобы достигнуть его:

  1. Im получения списка обязательных для заполнения значений FieldB, сделав союзы

    SELECT fieldB, "xxxx:%" AS orygLike FROM myTable WHERE fieldB LIKE "xxxx:%" GROUP BY fieldB 
    

    и т.д. для каждого UNIONed query - только для тех, кто находится в режиме LIKE, если это «=», я уже знаю, какое поле B является действительным :)

  2. Затем Im удалось создать массив сопоставления fieldBVal => orygLIKE (например, "xxxx:yyyy"=>"xxxx:%")

  3. Я перечисляю ВСЕ ИДЫ поля А, которые могут использоваться по ID, используя WHERE id IN (id1,id2,id3...) - таким образом у меня есть все ID, которые могут быть использованы. Прямо здесь Im объединяет массивы вместе и выбирает случайные идентификаторы с array_rand.

  4. Простой:

    SELECT * FROM myTable WHERE id IN (RndID1, RndID2, RndID3 etc...) 
    

его очень быстро и дает хорошие результаты :)

Благодаря fancyPants за указание о ID автоматического вкл поле

+0

Вы пытались использовать EXPLAIN? вы, вероятно, сможете увидеть, где ваш запрос не использует индексы, и вы, вероятно, могли бы что-то сделать соответствующим образом. –

+0

Да, включите его в мой вопрос. –

+0

~ 400 МБ? Вы сохраняете изображения как BLOBS в реальной базе данных? Если это так, подумайте о сохранении их в отдельной таблице приложений. – Strawberry

ответ

2

У вас есть запрос в там это сканирование всех строк таблицы.

Смотрите эту строку от вашего объяснения

2 UNION myTable  ALL  NULL NULL NULL NULL 4386943  Using where; Using temporary; Using filesort 

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

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

У попробовать с этим запросом (но обратите внимание, что это не гарантирует, вы получаете 7 рядов с FieldB, как «ххх:%» и 5 строк с FieldB = «123123» и так далее):

SELECT 
fieldA, 
CASE WHEN fieldB LIKE 'xxxx:%' THEN 'id1' 
    WHEN fieldB ='123123' THEN 'id2' 
END AS id 
FROM myTable 
WHERE 
(fieldB LIKE 'xxxx:%') 
OR fieldB ='123123' 
ORDER BY RAND() 
LIMIT 12 /*7 + 5*/ 

РЕДАКТИРОВАТЬ:

"LIKE '%'", конечно, бесполезно, так как это выбирает каждую строку. Он буквально говорит «дайте мне все». Если вы хотите сделать его сверхбыстрым, вот идея:

Добавить столбец так:

ALTER TABLE yourTableName ADD COLUMN id INT AUTO_INCREMENT PRIMARY KEY; 

Тогда вы получите наибольший идентификатор, доступный в вашей таблице и рассчитать Randoms перед рукой:

SET @my_max := (SELECT MAX(id) FROM yourTableName); 
SET @r := RAND() * @my_max; 
SELECT * FROM yourTable WHERE id >= @r LIMIT 1; 

Сделайте это снова, если вам нужно больше из них. Я сделал >= и LIMIT 1 вместо простого id = @r в случае, если вы удалите несколько строк некоторое время.

По крайней мере, эта часть запроса затем молниеносно.

+0

Ну, я сделал это - дал псевдонимы, и вот где я делаю «НРАВИТСЯ»% ». Не понимаю, у него есть LIMIT, так почему же все-таки сделано? Я изменил его на один запрос без WHERE, но никаких изменений. Да, это ИСТИНА, это имеет огромное влияние. Я хотел попробовать какой-нибудь хороший подзапрос (например, получить одну упорядоченную версию myTable, а затем выбрать несколько строк из нее для «LIKE»% »(так что любой случайный), а затем другие запросы для получения требуемых строк). В любом случае, я не могу получить ожидаемые результаты ... То же самое с вашей попыткой - Ive уже пробовал, но, как вы указали, это не даст мне ожидаемых результатов ... –

+0

@JakubKrol Отредактировал мой ответ. – fancyPants

+0

Ваше редактирование замечательно.Мне это очень нравится - мне просто нужно делать огромные SQL-запросы (или часто запрашивать), но это очень быстро, и я получаю ожидаемые результаты. Единственное, чего я не хочу, это то, что случайность не является «полной», так как Im не получает строки из последних 80% таблицы. Также попробовал трюк MediaWiki с «случайным» столбцом, но не успел. –