2016-04-02 2 views
1

Я использую Prestashop Related Product PRO plugin, который действительно полезен, когда дело доходит до некоторых случайных продуктов из той же категории, но использует метод Prestashop ORDER BY RAND по умолчанию и когда я включаю этот метод, чтобы показать 24 случайные продукты, время загрузки которых не используется, от 4000мс до 7000 мс, потому что он ждет, когда база данных покажет некоторые случайные продукты.Prestashop product ORDER BY RAND оптимизация

Когда я уменьшаю его до 8 продуктов, это 1500-2000 мс, но он все еще слишком длинный, когда дело доходит до оценки SEO. Я искал решение в подключении, но я не мог понять, но я нашел это:

Presta 1.6.1.4 здесь. В классах \ category.php что-то о линии 744 мы имеем что-то вроде этого:

$sql = 'SELECT p.*, product_shop.*, stock.out_of_stock, IFNULL(stock.quantity, 0) AS quantity'.(Combination::isFeatureActive() ? ', IFNULL(product_attribute_shop.id_product_attribute, 0) AS id_product_attribute, 
       product_attribute_shop.minimal_quantity AS product_attribute_minimal_quantity' : '').', pl.`description`, pl.`description_short`, pl.`available_now`, 
       pl.`available_later`, pl.`link_rewrite`, pl.`meta_description`, pl.`meta_keywords`, pl.`meta_title`, pl.`name`, image_shop.`id_image` id_image, 
       il.`legend` as legend, m.`name` AS manufacturer_name, cl.`name` AS category_default, 
       DATEDIFF(product_shop.`date_add`, DATE_SUB("'.date('Y-m-d').' 00:00:00", 
       INTERVAL '.(int)$nb_days_new_product.' DAY)) > 0 AS new, product_shop.price AS orderprice 
      FROM `'._DB_PREFIX_.'category_product` cp 
      LEFT JOIN `'._DB_PREFIX_.'product` p 
       ON p.`id_product` = cp.`id_product` 
      '.Shop::addSqlAssociation('product', 'p'). 
      (Combination::isFeatureActive() ? ' LEFT JOIN `'._DB_PREFIX_.'product_attribute_shop` product_attribute_shop 
      ON (p.`id_product` = product_attribute_shop.`id_product` AND product_attribute_shop.`default_on` = 1 AND product_attribute_shop.id_shop='.(int)$context->shop->id.')':'').' 
      '.Product::sqlStock('p', 0).' 
      LEFT JOIN `'._DB_PREFIX_.'category_lang` cl 
       ON (product_shop.`id_category_default` = cl.`id_category` 
       AND cl.`id_lang` = '.(int)$id_lang.Shop::addSqlRestrictionOnLang('cl').') 
      LEFT JOIN `'._DB_PREFIX_.'product_lang` pl 
       ON (p.`id_product` = pl.`id_product` 
       AND pl.`id_lang` = '.(int)$id_lang.Shop::addSqlRestrictionOnLang('pl').') 
      LEFT JOIN `'._DB_PREFIX_.'image_shop` image_shop 
       ON (image_shop.`id_product` = p.`id_product` AND image_shop.cover=1 AND image_shop.id_shop='.(int)$context->shop->id.') 
      LEFT JOIN `'._DB_PREFIX_.'image_lang` il 
       ON (image_shop.`id_image` = il.`id_image` 
       AND il.`id_lang` = '.(int)$id_lang.') 
      LEFT JOIN `'._DB_PREFIX_.'manufacturer` m 
       ON m.`id_manufacturer` = p.`id_manufacturer` 
      WHERE product_shop.`id_shop` = '.(int)$context->shop->id.' 
       AND cp.`id_category` = '.(int)$this->id 
       .($active ? ' AND product_shop.`active` = 1' : '') 
       .($front ? ' AND product_shop.`visibility` IN ("both", "catalog")' : '') 
       .($id_supplier ? ' AND p.id_supplier = '.(int)$id_supplier : ''); 

    if ($random === true) { 
     $sql .= ' ORDER BY RAND() LIMIT '.(int)$random_number_products; 
    } else { 
     $sql .= ' ORDER BY '.(!empty($order_by_prefix) ? $order_by_prefix.'.' : '').'`'.bqSQL($order_by).'` '.pSQL($order_way).' 
     LIMIT '.(((int)$p - 1) * (int)$n).','.(int)$n; 
    } 

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

if ($random === true) { 
     $sql .= ' ORDER BY RAND() LIMIT '.(int)$random_number_products; 
    } 

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

https://www.warpconduit.net/2011/03/23/selecting-a-random-record-using-mysql-benchmark-results/

и здесь

http://jan.kneschke.de/projects/mysql/order-by-rand/ (в этом случае результаты были просто изумительны)

Но есть случай. Мои навыки программирования ограничены для реализации этих методов в Prestashop: (Это просто сложно для меня, поэтому кто-то может помочь мне с редактированием этих строк одним из новых методов. Может ли кто-нибудь, у кого есть лучший опыт и знания, помочь мне? Или придумать что-нибудь, что лучше тогда те, которые я предложил?

+0

Вы правы. 'ORDER BY RAND()' - это печально известный антипаттерн. Представленные вами решения являются приемлемыми. особенно Ян Кнешке. Чтобы реализовать их, вам понадобится некоторый способ узнать или приблизиться к количеству строк, которые появятся в вашем результирующем наборе, если вы остановите все операции «LIMIT». Завышенность приемлема, но чем больше переоценка, тем хуже будет ваша производительность запросов. –

+0

Ключ в том, что я попытался реализовать его методы, и я потерпел неудачу. Я просто не могу сделать это в Престашопе. –

+0

Какова ваша оценка неограниченной длины набора результатов? –

ответ

2

Давайте предположим, что вы хотите, чтобы представить выбор K = $random_number_products внутри множества всех п строк в вашей prefix_product таблице. это означает, что вы надеетесь выбрать k/n ваших рядов случайным образом.

RAND() генерирует псевдослучайное число в диапазоне [0,1]. Поэтому для реализации k/n выбор, вам необходимо RAND() <= k/n или, переместив его в целочисленный домен сравнения, n*RAND() <= k. Если ваше приложение завершится неудачно, если ваш запрос выберет слишком мало случайных строк, вам нужно увеличить значение k, чтобы увеличить вероятность выбора любой строки. Давайте просто скажем k+5 для хорошей меры.

Затем вам нужно добавить что-то к концу статьи SELECT в запросе, сразу после orderprice, как это:

 SELECT..., 
     INTERVAL '.(int)$nb_days_new_product.' DAY)) > 0 AS new, product_shop.price AS orderprice, 
     (SELECT COUNT(*) FROM `'._DB_PREFIX_.'product`) * RAND() AS selector 
     ... 

Это присваивает случайный селектор между 0 и COUNT (*) значение для каждая строка в результирующем наборе.

Наконец, в конце вашего запроса поставьте это.

if ($random === true) { 
    $sql .= ' HAVING selector <= ', (5+$random_number_products); 
    $sql .= ' ORDER BY selector LIMIT '.(int)$random_number_products; 
} 

Я думаю, что это сработает.

  • HAVING выбирает подмножество ваших строк. Для этого конкретного приложения вам нужно HAVING, а не WHERE, потому что оно относится к сгенерированному столбцу.
  • 5+ немного переоценивает размер этого подмножества.
  • ORDER BY рандомизирует порядок выбранных строк.
  • LIMIT избавляется от лишних строк, полученных в результате переоценки.

Возможно, у меня остались некоторые синтаксические ошибки. Если так, прошу прощения.

ORDER BY RAND() LIMIT n является особенно вонючим случаем неприятного антипаттера ORDER BY anything LIMIT n. Он тратит ресурсы сервера. Он генерирует весь набор результатов (в ОЗУ сервера или на диске, если он не вписывается в ОЗУ), затем сортирует его в какой-то порядок, затем возвращает несколько строк, чем отбрасывает остальные. Секрет хорошей производительности в этих случаях - это отбросить строки как можно раньше и отсортировать наименьший набор результатов.

Но он работает. Поэтому, если запрос выполняется нечасто, просто сохраните его. В вашем случае запрос выполняется часто.

(Prestashop? ORDER BY RAND()? На самом деле? Когда вы получите этот рабочий отправить им сообщение об ошибке с вашего исправления на месте.)

+0

Как неопределенный комментарий, у меня есть точка зрения, что я откуда-то получил, что MySQL 'RAND' является неэффективной (и, следовательно, медленной/процессорной) командой и всегда подставлял ее для более быстрого более плавного случайного Shuffle PHP (или аналогичный). Согласитесь? – Martin

+0

Нет, я не согласен с тем, что реализация MySQL в RAND() 'как-то неэффективна сама по себе. Это простой и дешевый численный расчет. Это 'ORDER BY RAND()', который дал «RAND()» плохую репутацию. Подумайте о том, как вычислить «RAND()», так как Христофор Колумб идет по улице, чтобы поговорить с королевой Изабеллой. Подумайте о «ORDER BY» как отправке покупателю в другой город, чтобы купить веревку. Подумайте о каждой строке вашего результирующего набора как отправке корабля на Ямайку. Это точные меры относительных затрат. –

+0

Ах, да, да, честно говоря, использование «RAND», которое я видел больше всего, было с «ORDER BY», так что объясняет, как я пришел к моему заключению. Спасибо за разъяснение :-) – Martin

0

Практически все алгоритмы там есть O (N) или хуже. В моем blog on faster random searches я ссылаюсь на них как «не очень быстро», включая классическую страницу Джан. Я представляю 5 случаев; Я не знаю, какие могут быть применены к вашей ситуации:

  • случая: Порядковый AUTO_INCREMENT без пробелов, 1 строка, возвращаемая
  • Корпус: Последовательная AUTO_INCREMENT без пробелов, 10 строк
  • Корпус: AUTO_INCREMENT с зазорами, 1 строка, возвращаемая
  • Корпус: Дополнительный столбец FLOAT для рандомизации
  • Корпус: UUID или столбец MD5

Все случаи работать быстрее чем полное сканирование таблицы.

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