Как использовать LIMIT на LEFT JOIN? - PullRequest
0 голосов
/ 29 декабря 2018

У меня есть 2 таблицы с именами categories и products с этим соотношением:

categories.category_id = products.product_category_id

Я хочу показать все категории с 2 из них!

[
    [category 1] =>
            [
                'category_id' => 1,
                'category_title' => 'category_title 1',
                [products] => [
                    '0' => [
                            'product_category_id' => 1,
                            'product_id' => 51,
                            'product_title' => 'product_title 1',
                        ],
                    '1' => [
                            'product_category_id' => 1,
                            'product_id' => 55,
                            'product_title' => 'product_title 2',
                        ]
                ]
            ],
    [category 2] =>
            [
                'category_id' => 2,
                'category_title' => 'category_title 2',
                [products] => [
                    '0' => [
                            'product_category_id' => 2,
                            'product_id' => 32,
                            'product_title' => 'product_title 3',
                        ],
                    '1' => [
                            'product_category_id' => 2,
                            'product_id' => 33,
                            'product_title' => 'product_title 4',
                        ]
                ]
            ],
    ...
]

Я красноречивый Laravel Я могу использовать что-то вроде этого:

$categories = Category::with(['products' => function($q) {
    $q->limit(2)
}])->get();

Но я не использую Laravel и мне нужен чистый код SQL!Я пробовал этот код:

SELECT
    CT.category_id,
    PT.product_category_id,
    CT.category_title,
    PT.product_id,
    PT.product_title
FROM
    categories CT
LEFT JOIN(
SELECT
    *
FROM
    products 
    LIMIT 2
) AS PT
ON
    CT.category_id = PT.product_category_id
WHERE
    CT.category_lang = 'en' 

Но есть проблема с этим кодом!Кажется, что MYSQL сначала получает 2 первые строки в таблице products, а затем пытается получить LEFT JOIN от categories до этих 2 строк!и это вызывает возврат null значений для продуктов (хотя, если я удаляю LIMIT, это прекрасно работает, но мне нужно LIMIT)

Я также проверял этот код: ( ОБНОВЛЕНО )

SELECT
    CT.category_id,
    PT.product_category_id,
    CT.category_title,
    PT.product_id,
    PT.product_title
FROM
    categories CT
LEFT JOIN(
    SELECT
        *
    FROM
        products
    WHERE
        product_category_id = CT.category_id
    LIMIT 5
) AS PT
ON
    CT.category_id = PT.product_category_id
WHERE
    CT.category_lang = 'en'

Но я получил эту ошибку:

1054 - Неизвестный столбец 'CT.category_id' в 'выражении where'

Я не могу получить доступ к CT.category_id в подзапросе.

Какое лучшее решение?

1 Ответ

0 голосов
/ 29 декабря 2018

В MySQL вам нужно будет использовать переменные.Основная идея:

select p.*
from (select p.*,
             (@rn := if(@c = product_category_id, @rn + 1,
                        if(@c := product_category_id, 1, 1)
                       )
             ) as rn
      from (select p.* 
            from products p
            order by p.product_category_id
           ) p cross join
           (select @c := -1, @rn := 0) params
     ) p
where rn <= 2;

Вы можете использовать это как свой подзапрос, а затем присоединиться к остальной информации категории.

Здесь - это дБ <>возьмите, если этот код работает.

В MySQL 8 + / MariaDB 10.2+ это гораздо проще написать так:

select p.*
from (select p.*,
             row_number() over (partition by product_category_id order by product_category_id) as seqnum
      from products p
     ) p
where seqnum <= 2;

Примечание: вы можете указать, какие 2 продукта вы хотите (скажем,(первые два в алфавитном порядке) путем корректировки предложений order by в двух запросах.

EDIT:

В MariaDB 10.2 вы, очевидно, не можете использовать подзапрос дляупорядочение, поэтому соответствующий запрос будет выглядеть так:

select p.*
from (select p.*,
             (@rn := if(@c = product_category_id, @rn + 1,
                        if(@c := product_category_id, 1, 1)
                       )
             ) as rn
      from products p cross join
           (select @c := -1, @rn := 0) params
      order by p.product_category_id
     ) p
where rn <= 2;

Здесь - это db <> скрипка для этого.

Вы также можете использовать этот метод:

select p.*
from products p
where (select count(*)
       from products p2
       where p2.product_category_id = p.product_category_id and p2.id <= p.id
      ) <= 2;

Я не очень рекомендую это, потому что коррелированный подзапрос обычно имеет гораздо худшую производительность, чем order by.Но если в категории не так много товаров, это будет хорошо (с правильными индексами).

...