Проблема с базой данных: выбор перевода по умолчанию из таблицы - PullRequest
3 голосов
/ 24 августа 2009

Добрый день,

У меня есть таблица MySQL описания , которая содержит такие поля, как: lang_id, label, short_description, long_description и is_default.

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

Сейчас мои запросы выглядят так:

 SELECT 
    description.id AS record_id
    description.label,
    description.short_description,
    description.long_description            
 FROM
    products,
    description,
    languages
 WHERE
    products.id = '.$someProductID.' AND
    products.id = description.product_id AND
    languages.id = description.lang_id AND
    languages.code = "'.$someLang.'"

Есть ли у кого-нибудь решение для получения описания продукта по умолчанию, когда требуемый перевод не существует?

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

IFNULL(description.label, (SELECT label FROM description WHERE product_id = '.$someProductID.' AND is_default = 1) ) AS label

Но я не очень знаком с такими сложными запросами и не мог заставить его работать.

Я открыт для предложений;)

Спасибо!

Ответы [ 4 ]

2 голосов
/ 24 августа 2009

Вот этот:

SELECT  p.*, COALESCE (dn.name, den.name) AS cname
FROM    products p
LEFT JOIN
        description dn
ON      dn.product_id = p.id
        AND dn.language =
        (
        SELECT  id
        FROM    language
        WHERE   code = 'your_language'
        )
LEFT JOIN
        description den
ON      den.product_id = p.id
        AND den.is_default
        )
WHERE   p.id = @my_product

, или этот:

SELECT  p.*,
        COALESCE (dn.name,
        (
        SELECT  den.name
        FROM    description den
        WHERE   den.product_id = p.id
                AND den.is_default
        )
        ) AS cname
FROM    products p
LEFT JOIN
        description dn
ON      dn.product_id = p.id
        AND dn.language =
        (
        SELECT  id
        FROM    language
        WHERE   code = 'your_language'
        )
WHERE   p.id = @my_product

Во всех базах данных, кроме MySQL, первая более эффективна, когда у вас мало переводов для вашего языка, вторая более эффективна, когда у вас много переводов.

В MySQL второй запрос (с COALESCE) всегда эффективнее.

Подробности о производительности см. В этой серии статей в моем блоге по этой проблеме:

и перейдите к другим RDBMS

1 голос
/ 24 августа 2009

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

0 голосов
/ 24 августа 2009

Спасибо за вашу помощь, ребята, я подхожу к решению!

@ jmucchiello: Я не ищу проверки на стороне PHP. Я хочу, чтобы MySQL делал всю работу, так как я буду использовать ее во многих других SQL-запросах.

Другими словами, я хочу сделать этот запрос, сохранить его в виде, чтобы иметь упрощенный способ получения переведенных описаний моих продуктов. Что-то вроде: descriptionView.

Я бы тогда использовал это так:

SELECT * FROM descriptionView WHERE lang_code = "en" AND product_id = 80007

Прямо сейчас вот что у меня есть:

SELECT 
    descriptions.code,
    IFNULL(t1.label, t2.label) AS label,
    IFNULL(t1.short_description, t2.short_description) AS short_description,
    IFNULL(t1.long_description, t2.long_description) AS long_description
FROM
    descriptions
LEFT JOIN
    translations t1
ON
    t1.description_id = descriptions.id AND t1.lang_id = 
    (
        SELECT
            id
        FROM
            languages
        WHERE
            code = "fr"
    )
LEFT JOIN
    translations t2
ON
    t2.description_id = descriptions.id AND t2.is_default = 1

WHERE
    descriptions.id = 1

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

Проблема в том, что по какой-то причине я не могу изменить первое левое соединение на:

LEFT JOIN
        translations t1
    ON
        t1.description_id = descriptions.id AND t1.lang_id = languages.id

и добавить языки в предложении FROM.

Наконец, я бы удалил предложение WHERE, так как мне нужен весь результат.

Таким образом, мой последний запрос будет выглядеть так:

CREATE VIEW descriptionView AS
    SELECT 
        languages.code as lang,
        descriptions.code,
        IFNULL(t1.label, t2.label) AS label,
        IFNULL(t1.short_description, t2.short_description) AS short_description,
        IFNULL(t1.long_description, t2.long_description) AS long_description
    FROM
        descriptions,
        languages
    LEFT JOIN
        translations t1
    ON
        t1.description_id = descriptions.id AND t1.lang_id = languages.id
    LEFT JOIN
        translations t2
    ON
        t2.description_id = descriptions.id AND t2.is_default = 1

Затем я могу запросить это представление, используя:

SELECT * FROM descriptionView WHERE lang_code = "en" AND product_id = 80007
0 голосов
/ 24 августа 2009

Вы можете получить языковые идентификаторы заранее и использовать их для правильного построения запроса:

// this code is performed once and the results are stored for use in all description lookups
SELECT id INTO :def_lang_id FROM languages WHERE code = :default_lang_code;
SELECT id INTO :usr_lang_id FROM languages WHERE code = :user_lang_code;

// this is an example of using the above results to speed your search for the "correct" descriptions
if ($def_lang_id == $usr_lang_id) {
    $sql = "SELECT d.id, d.label, d.short_desc, d.long_desc
            FROM products p, description d
            WHERE p.id = :some_prod_id
              AND p.id = d.product_id
              AND d.lang_id = :usr_lang_id";
} else {
    $orderdirection = $def_lang_id < $usr_lang_id ? "DESC" : "ASC";

    $sql = "SELECT d.id, d.label, d.short_desc, d.long_desc
            FROM products p, description d
            WHERE p.id = :some_prod_id
              AND p.id = d.product_id
              AND d.lang_id in (:def_lang_id, :usr_lang_id)
            ORDER BY d.lang_id $langdirection";
}

Теперь, если пользователь использует язык по умолчанию, он получает только одно описание. Если они используют другой язык, lang_id альтернативного языка сортируется перед значением default_id в запросе. Поэтому, если есть альтернативное описание, оно возвращается в первой строке запроса, а если альтернативного описания нет, возвращается только описание по умолчанию.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...