ЛЕВОЕ СОЕДИНЕНИЕ, ГДЕ условия не дают должных результатов - PullRequest
1 голос
/ 09 августа 2011

Я пишу специальный плагин для Wordpress для клиента, и у меня есть работающая функция поиска по ключевым словам / настраиваемым полям, которая работает.Вы вводите ключевое слово или фразу, и она выполняет поиск по нескольким полям для ключевых слов, возвращая только отдельные результаты.

Если я ищу газету под названием «Вестник свободы и света», я получаю 5 результатов статьи.

SELECT
  SQL_CALC_FOUND_ROWS
  DISTINCT
  wp_posts.* FROM wp_posts
LEFT JOIN `wp_postmeta` ON `wp_posts`.`ID` = `wp_postmeta`.`post_id`
WHERE
  1=1
  AND `post_type` = 'post'
  AND `post_status` = 'publish'
  AND (`wp_postmeta`.`meta_key` = 'newspaper_title' AND `wp_postmeta`.`meta_value` = 'The Herald of Freedom & Torch Light')
GROUP BY wp_posts.ID
ORDER BY wp_posts.post_date DESC
LIMIT 0, 10

Если я попытаюсь найти ту же газету с темой "Политика", я получу 0 результатов (когда должно быть не менее 3).

SELECT
  SQL_CALC_FOUND_ROWS
  DISTINCT
  wp_posts.* FROM wp_posts
LEFT JOIN `wp_postmeta` ON `wp_posts`.`ID` = `wp_postmeta`.`post_id`
WHERE
  1=1
  AND `post_type` = 'post'
  AND `post_status` = 'publish'
  AND (`wp_postmeta`.`meta_key` = 'newspaper_title' AND `wp_postmeta`.`meta_value` = 'The Herald of Freedom & Torch Light')
  AND (`wp_postmeta`.`meta_key` = 'article_subject' AND `wp_postmeta`.`meta_value` = 'Politics')
GROUP BY wp_posts.ID
ORDER BY wp_posts.post_date DESC
LIMIT 0, 10

Я искалвокруг, и большинство ответов на вопросы о СОЕДИНЕНИЯХ с несколькими условиями говорят «переместить условия в объединение».Что ж, я сделал это с помощью следующего запроса, и он показывает 16 результатов (включая отредактированные пост-ревизии!) Без надлежащего поиска по ключевым словам, которые я дал.И не только это, но и при перемещении условий поиск по 1 условию, например, по параметру газеты_титле, приведет к получению тех же 16 результатов!

SELECT
  SQL_CALC_FOUND_ROWS
  DISTINCT
  wp_posts.* FROM wp_posts
LEFT JOIN `wp_postmeta` ON
  `wp_posts`.`ID` = `wp_postmeta`.`post_id`
  AND (`wp_postmeta`.`meta_key` = 'newspaper_title' AND `wp_postmeta`.`meta_value` = 'The Herald of Freedom & Torch Light')
  AND (`wp_postmeta`.`meta_key` = 'article_subject' AND `wp_postmeta`.`meta_value` = 'Politics')
WHERE
  1=1
  AND `post_type` = 'post'
  AND `post_status` = 'publish'
GROUP BY wp_posts.ID
ORDER BY wp_posts.post_date DESC
LIMIT 0, 10

Как мне переписать свой SQL, чтобы эти несколько условий хорошо сочетались друг с другом?У меня есть 7 других полей для работы в этой функции поиска с похожими условиями.

Ответы [ 4 ]

2 голосов
/ 09 августа 2011

Ваша проблема в том, что вы пытаетесь логически сравнить значения, содержащиеся в разных строках.В одной строке не может быть мета-ключа, который является и «газетным_титлом», и «статьей-субъектом».Если вы измените И на ИЛИ, то вы получите записи, которые являются или не совпадают.

Я думаю, что решение здесь состоит в том, чтобы использовать сводную таблицу для мета значений.Идея здесь состоит в том, чтобы объединить информацию, содержащуюся в нескольких строках, в одну строку для каждого post_id, а затем в целевом объекте where, где все столбцы имеют значение 1. Я в качестве примера собрал скрипт, основанный на информации, которую вы 'мы предоставили:

Пожалуйста, убедитесь, что этот скрипт запущен в тестовой среде и не конфликтует с вашими существующими данными

create table wp.posts (post_id int, description varchar(25), post_date date);
create table wp.meta (post_id int, meta_key varchar(15), meta_value varchar(25));

-- Setup post records
insert into wp.posts values
    (1, 'Post #1', MAKEDATE(2011, 5)),  (2, 'Post #2', MAKEDATE(2011, 8)),
    (3, 'Post #3', MAKEDATE(2011, 30)), (4, 'Post #4', MAKEDATE(2011, 5)),
    (5, 'Post #5', MAKEDATE(2011, 7)),  (6, 'Post #6', MAKEDATE(2011, 2));

-- Setup meta data for post records                           
insert into wp.meta values
(1, 'newspaper_title', 'NY Post'),    (2, 'newspaper_title', 'NY Post'),          
(1, 'day', 'Monday'),                 (2, 'day', 'Wednesday'),
(1, 'article_subject', 'Local'),      (2, 'article_subject', 'Politics'),

(3, 'newspaper_title', 'The Times'),  (4, 'newspaper_title', 'The Times'),     
(3, 'day', 'Friday'),                 (4, 'day', 'Tuesday'),   
(3, 'article_subject', 'Politics'),   (4, 'article_subject', 'Politics'),

(5, 'newspaper_title', 'The Herald'), (6, 'newspaper_title', 'Daily Tribune'),    
(5, 'day', 'Sunday'),                 (6, 'day', 'Wednesday'), 
(5, 'article_subject', 'Arts'),       (6, 'article_subject', 'Local');

-- Show all the data
SELECT p.description, p.post_date, meta_key, meta_value
FROM wp.posts p JOIN wp.meta m ON (p.post_id = m.post_id)
ORDER BY p.post_id;

-- Search based on newspaper_title = 'The Times' AND article_subject = 'Politics'    
SELECT p.*
FROM wp.posts p
JOIN
  (
    SELECT post_id,
           max(CASE WHEN (meta_key = 'newspaper_title' AND meta_value = 'The Times')  
               THEN 1 ELSE 0 END) targetNewspaper,
           max(CASE WHEN (meta_key = 'article_subject' AND meta_value = 'Politics') 
               THEN 1 ELSE 0 END) targetSubject
    FROM wp.meta
    GROUP BY post_id
  ) m
ON (p.post_id = m.post_id)
WHERE targetNewspaper = 1 AND targetSubject = 1
ORDER BY p.post_date;

Последний запрос в скрипте - этотот, который вы после.С тестовым набором данных он возвращает:

post_id     description               post_date                 
----------- ------------------------- ------------------------- 
4           Post #4                   2011-01-05                
3           Post #3                   2011-01-30  

Для каждого атрибута, который необходимо проверить, вы добавили бы дополнительный оператор case, как показано выше в мета-запросе, и добавили к предложению where условие, чтобы проверить, является ли онобыл найден.(т.е. newTargetedValue = 1)

Обновление на основе комментария OP:

По моему мнению, метод оценки или подсчета не так гибок, как использование сводной таблицы.Внутренняя / сводная таблица, по сути, устанавливает флаги для атрибутов, которые соответствуют на основе предоставленных вами случаев.(Значение будет 1 или 0). В вашем текущем примере вы просто складываете И все вместе, так что все должно быть установлено, чтобы можно было использовать счет или счет.Если позже вам потребовалось логически сравнить эти атрибуты для более сложного поиска, счет / оценка больше не работает.Я попытаюсь объяснить на примере.

Скажем, я попросил вас добавить в результаты поиска, которые вы уже предоставили в вопросе, где я хочу, чтобы все сообщения, которые имели мета-значение "день" =«Воскресенье» независимо от газеты.Короче говоря, я хочу:

  • Все «политические» колонки из «The Times».
  • Вместе со всеми сообщениями, которые произошли в «воскресенье» (независимо от того, какая газета былаin)

Это не сработает со счетом / счетом, потому что совпадающие строки могут возвращать 1, 2 или 3 строки в зависимости от количества совпадающих атрибутов.

  • Количество = 1 (т. Е. Только атрибут статей в воскресных публикациях)
  • Количество = 2 Любые 2 совпадающих атрибута (т. Е. Воскресные сообщения и статьи о политике)
  • Количество= 3 Соответствует всем критериям (например, статья о политике в воскресном выпуске «The Times»)

С помощью сводной таблицы вы все равно можете использовать логические выражения: (включая мета-флаги для ясности)

SELECT p.*, m.targetNewspaper, targetSubject, targetDay
FROM wp.posts p
JOIN
  (
    SELECT post_id,
           max(CASE WHEN (meta_key = 'newspaper_title' AND meta_value = 'The Times')  
               THEN 1 ELSE 0 END) targetNewspaper,
           max(CASE WHEN (meta_key = 'article_subject' AND meta_value = 'Politics') 
               THEN 1 ELSE 0 END) targetSubject,
           max(CASE WHEN (meta_key = 'day' AND meta_value = 'Sunday')               
               THEN 1 ELSE 0 END) targetDay
    FROM wp.meta
    GROUP BY post_id
  ) m
ON (p.post_id = m.post_id)
WHERE (targetNewspaper = 1 AND targetSubject = 1) OR targetDay = 1
ORDER BY p.post_date;

Вот результаты:

post_id  description   post_date   targetNewspaper   targetSubject   targetDay            
-------- ------------- ----------- ----------------- --------------- ----------- 
4        Post #4       2011-01-05  1                 1               0                    
5        Post #5       2011-01-07  0                 0               1                    
3        Post #3       2011-01-30  1                 1               0                    

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

Надеюсь, что это объяснение сделало вещи немного более удобочитаемыми.

1 голос
/ 09 августа 2011

Ваш код как таковой пытается найти строки, в которых meta_key одновременно является и «газетным_титлом», и «статьей-субъектом». Это, конечно, невозможно. То, что вы действительно хотите спросить, это то, "какие строки в wp_posts имеют строку в wp_postmeta с" газетным_титлом " и другую строку с" article_subject ".

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

SELECT
    PT.parent_id
FROM
    Parent_Table PT
LEFT OUTER JOIN Child_Table CT ON
    CT.parent_id = PT.parent_id AND
    CT.tag IN (@tag1, @tag2)
GROUP BY
    PT.parent_id
HAVING
    COUNT(DISTINCT CT.tag) = 2

Вы можете изменить вышеуказанный запрос, чтобы использовать вместо этого подзапрос, например 2 = (SELECT COUNT(*)...)

Используя вашу конкретную ситуацию (прошу прощения за любые незначительные проблемы с синтаксисом, поскольку я обычно не работаю с MySQL):

SELECT
    wp_posts.*
FROM
    wp_posts wp
INNER JOIN (
    SELECT
        wp2.id,
        COUNT(*) AS cnt
    FROM
        wp_posts wp2
    INNER JOIN wp_postmeta wpm ON
        wpm.post_id = wp2.id AND
        (wpm.meta_key = 'newspaper_title' AND wpm.meta_value = 'The Herald of Freedom & Torch Light') OR
        (wpm.meta_key = 'article_subject' AND wpm.meta_value = 'Politics')
    GROUP BY
        wp2.id
    ) AS SQ ON SQ.id = wp.id AND SQ.cnt = 2
0 голосов
/ 09 августа 2011

Я подумал, что поделюсь с вами тем, что нашел.

1.) Это проблема схемы.

Мы оставили WordPress в формате post / post-meta и использовали плагин для хранения наших записей в таблицах post-meta. Это создало серьезную проблему: вместо того, чтобы иметь определенную таблицу, предназначенную для хранения наших данных, у нас была запись (только с заголовком) и набор связанных строк пост-мета (стиль ключа-значения).

Поэтому поиск по 1 запросу нецелесообразен.

2.) Клиент запросил поиск по нескольким полям (поздно).

Я взял наш существующий поиск по ключевым словам, который работал по всем соответствующим пост-мета строкам, и попытался расширить его. Первоначально это работало, потому что оно захватило первый результат и вернуло его ... и ему было наплевать на множественные совпадения. Как только я попытался использовать несколько условий, применимых только к таблице post-meta, я установил предел того, что SQL должен делать.

Мы действительно должны были сделать наш собственный стол.

3.) Мы действительно должны были создать свой собственный стол с самого начала.

Я знал, что, возможно, клиент захочет искать очень конкретными способами. И я должен был предвидеть трудности распределения фактических данных столбца по нескольким строкам во второй таблице. За исключением крайнего случая, это плохой дизайн.

Заключение

Если вам нужно искать практически по всем атрибутам поста одновременно, не полагайтесь на пользовательские поля Wordpress. Они очень мощные для всего , кроме поиска . Создайте свою собственную таблицу, свяжите ее с сообщениями, выполните поиск в своей пользовательской таблице и присоединяйтесь к действительным сообщениям, а не наоборот!

0 голосов
/ 09 августа 2011

LEFT JOIN - это соединение OUTER, что означает, что вы получите все строки из таблицы слева с совпадающими строками или нулевыми значениями, если совпадение не найдено из таблицы справа.

Переключитесь на INNER JOIN, чтобы ограничить число строк, возвращаемых только этими, которые имеют совпадения в таблице справа.

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