Переведите этот чудовищный запрос с данными, связанными с HABTM, в находку CakePHP - PullRequest
2 голосов
/ 12 января 2012

Уже поздно, и я написал это чудовище запроса, чтобы получить связанные продукты на основе продукта, который я уже нашел.

Мне нужно выбрать продукты той же категории (HABTM), родительский продуктпродукты с одним и тем же родителем (братья и сестры / соседи) и продукты, которые являются прямыми дочерними элементами текущего продукта (существует только один уровень вложенности).У меня есть идентификатор продукта и его parent_id текущего продукта.Если бы можно было поставить условия для продукта, как для Product.published = 1, это было бы замечательно, но если он сделает запрос настолько большим, я всегда могу проверить это после.Кроме того, мне нужно исключить текущий продукт.

SELECT `products`.*
FROM `products`, `categories_products`
WHERE
(
    (
        `categories_products`.`product_id` = `products`.`id`
        AND `categories_products`.`category_id` IN (
            SELECT `category_id`
            FROM `categories_products`
            WHERE `categories_products`.`product_id` = '$product_id'
        )
    )
    OR `products`.`parent_id` = '$parent_id'
    OR `products`.`parent_id` = '$product_id'
    OR `products`.`id` = '$parent_id'
)
AND `product`.`id` <> '$product_id'

GROUP BY `products`.`id`

Возможно, можно даже оптимизировать его немного больше, пока у меня есть:

public function related($productData, $limit = 4) {

    $conditions = array(
        'OR' => array(array('Product.parent_id' => $productData['Product']['id'])), // Children of product),
        'Product.id <>' => $productData['Product']['id']
    );

    if(!empty($product['parent_id'])) {
        $conditions['OR'][] = array('Product.parent_id' => $productData['Product']['parent_id']); // Siblings
        $conditions['OR'][] = array('Product.id' => $productData['Product']['parent_id']); // Parent of product
    }

    return $this->find('all', array(
        'conditions' => $conditions,
        'contain' => array('Category'),
        'group' => 'Product.id',
        'limit' => $limit
    ));
}

Ответы [ 4 ]

1 голос
/ 13 января 2012

Вам нужно будет использовать синтаксис Сложных условий поиска для торта (прокрутите вниз до последовательности Подзапросов).

0 голосов
/ 27 июня 2012

Эта проблема снова возникла, и я понял это на 100%! Вот мой законченный код:

// Model
public function related($product, $limit = 9) {

    // Children of product
    $conditions = array(
        'OR' => array(array('Product.parent_id' => $product['Product']['id'])), // Children of product),
        'Product.id <>' => $product['Product']['id'],
        'Product.published' => 1
    );

    // Siblings and parent of product if applicable
    if (!empty($product['Product']['parent_id'])) {

        $conditions['OR'][] = array('Product.parent_id' => $product['Product']['parent_id']);
        $conditions['OR'][] = array('Product.id' => $product['Product']['parent_id']);
    }

    // Products in the same categories
    // Get category IDs in an array
    $categoryIds = Set::extract($product['Category'], '{n}.id');

    $conditionsSubQuery['category_id IN(?)'] = implode(',', $categoryIds);

    $db = $this->getDataSource();
    $subQuery = $db->buildStatement(
            array(
        'fields' => array('product_id'),
        'table' => 'categories_products',
        'joins' => array(),
        'alias' => 'c_p',
        'conditions' => $conditionsSubQuery,
        'order' => null,
        'group' => null,
        'limit' => null
            ), $this->CategoryProduct
    );
    $subQuery = 'Product.id IN (' . $subQuery . ') ';
    $subQueryExpression = $db->expression($subQuery);

    $conditions['OR'][] = $subQueryExpression;

    return $this->find('all', array(
                'conditions' => $conditions,
                'contain' => array('Category'),
                'group' => 'Product.id',
                'limit' => $limit
            ));
0 голосов
/ 20 января 2012

Я бы предотвратил необходимость рекурсивной попытки ВЫБРАТЬ В по категории.Предварительно построить это на основе одного рассматриваемого продукта и получить все его отдельные категории.Исходя из этого, получить различные продукты, которые соответствуют категории.Теперь у вас есть предварительный запрос «CommonByCategory», который УЖЕ будет единичным экземпляром идентификаторов.

Затем снова выполните жесткое объединение с продуктами «OriginalProduct» на основе КОНКРЕТНОГО идентификатора, к которому вы пытаетесь соответствовать.Поскольку он всегда будет существовать и никогда не изменится, мы можем использовать его в качестве указателя для сопоставляемых братьев и сестер, а также для совпадения родительского идентификатора (в случае ненулевого значения - с помощью примененных тестов IFNULL ()

Поскольку каждый продукт будет сканироваться только ОДИН РАЗ и не будет возвращать несколько записей из-за возможностей нескольких категорий, «GROUP BY» не требуется.

SELECT STRAIGHT_JOIN 
      p.*
   from 
      products p
         left join 
            ( SELECT DISTINCT 
                    cp2.product_id
                 from
                    ( SELECT cp.Category_ID
                         from categories_products cp
                         where cp.product_id = '$product_id' ) JustCats
                     join categories_products cp2
                        ON JustCats.Category_ID = cp2.Category_ID ) as CommonByCategory
            ON p.ID = CommonByCategory.product_ID

         join products OriginalProduct
            ON OriginalProduct.ID = '$product_id'

   where
          p.id <> '$product_id'
      and ( IFNULL( CommonByCategory.Product_ID, -1) > 0
          OR p.id = IFNULL( OriginalProduct.Parent_ID, -1 )
          OR p.parent_id = OriginalProduct.id
0 голосов
/ 17 января 2012

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

После стольких слов это переписывание вашего текущего sql, которое, мы надеемся, даст вам некоторую производительность.

SELECT * FROM products p
WHERE 
    p.published = 1 AND 
    p.id != $product_id AND
    (
        p.id IN 
            (
                SELECT DISTINCT(cp2.product_id) FROM categories_product cp1
                LEFT JOIN categories_product cp2 ON cp1.category_id = cp2.category_id
                WHERE cp1.product_id = $product_id
                UNION SELECT $parent_id
            )
        OR p.parent_id IN($parent_id, $product_id)
    )
;

Я пытался избавиться от ненужных операторов group byНадеюсь, это поможет.

PS: могут быть синтаксические ошибки, так как я написал это в текстовом редакторе.

...