2015-08-18 3 views
0

Я не SQL pro, но я считаю, что решил свою проблему, хотя и довольно неэффективным способом. Я надеюсь, что кто-то может указать на лучший метод, чем я придумал. Я пытаюсь найти дублированный или похожий контент в индексе терминов, созданный RelevanSSI (плагин полного текстового поиска для Wordpress) - однако это происходит за пределами установки Wordpress, и это фактическая база данных, поэтому Wordpress, это API и любые другие таблицы обычно связанные с ним, выходят за рамки этого.Получить неагрегатные данные в сводном запросе

RelevanSSI Index таблица выглядит следующим образом:

CREATE TABLE `wp_relevanssi` (
`doc` bigint(20) NOT NULL DEFAULT '0', 
`term` varchar(50) NOT NULL DEFAULT '0', 
`content` mediumint(9) NOT NULL DEFAULT '0', 
`title` mediumint(9) NOT NULL DEFAULT '0', 
`comment` mediumint(9) NOT NULL DEFAULT '0', 
`tag` mediumint(9) NOT NULL DEFAULT '0', 
`link` mediumint(9) NOT NULL DEFAULT '0', 
`author` mediumint(9) NOT NULL DEFAULT '0', 
`category` mediumint(9) NOT NULL DEFAULT '0', 
`excerpt` mediumint(9) NOT NULL DEFAULT '0', 
`taxonomy` mediumint(9) NOT NULL DEFAULT '0', 
`customfield` mediumint(9) NOT NULL DEFAULT '0', 
`mysqlcolumn` mediumint(9) NOT NULL DEFAULT '0', 
`taxonomy_detail` longtext NOT NULL, 
`customfield_detail` longtext NOT NULL, 
`mysqlcolumn_detail` longtext NOT NULL, 
`type` varchar(210) NOT NULL DEFAULT 'post', 
`item` bigint(20) NOT NULL DEFAULT '0', 
`term_reverse` varchar(50) NOT NULL DEFAULT '0', 
UNIQUE KEY `doctermitem` (`doc`,`term`,`item`), 
KEY `terms` (`term`(20)), 
KEY `docs` (`doc`), 
KEY `typeitem` (`type`,`item`), 
KEY `relevanssi_term_reverse_idx` (`term_reverse`(10)) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 

И я успешно получаю (я думаю) информацию я хочу с помощью следующего запроса:

SELECT r1.doc, r2.doc, 
    50 * COUNT(r1.term) * (
     (c1.total + c2.total)/
     (c1.total * c2.total) 
    ) AS ScorePct 
FROM `wp_relevanssi` r1 
LEFT JOIN `wp_relevanssi` r2 
ON r1.term = r2.term 
AND r1.doc > r2.doc 
AND r1.type = r2.type 
AND (r1.content > 0 or r1.title > 0 or r1.taxonomy > 0 or r1.tag > 0) 
AND (r2.content > 0 or r2.title > 0 or r2.taxonomy > 0 or r2.tag > 0) 
LEFT JOIN (
    SELECT doc, COUNT(term) AS total 
    FROM `wp_relevanssi` 
    GROUP BY doc 
) c1 
ON r1.doc = c1.doc 
LEFT JOIN (
    SELECT doc, COUNT(term) AS total 
    FROM `wp_relevanssi` 
    GROUP BY doc 
) c2 
ON r2.doc = c2.doc 
GROUP BY r1.doc, r2.doc 
HAVING ScorePct >50 
ORDER BY ScorePct DESC 

Мой вопрос таков большой ола 'хитроумные подзапросы упали в соединения. Мне кажется, мне нужен хотя бы один подзапрос, чтобы сделать это (по сути, получить общее количество терминов для конкретного документа), потому что после этого первого LEFT JOIN у нас есть только информация о совпадении терминов в основном запросе, отбросив несоответствующие , (Пожалуйста, продолжайте и скажите мне, что я здесь не прав, я бы хотел узнать, что подзапрос не нужен).

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

EDIT: Так что я просто должен был решить эту проблему с другим подходом - глядя на один документ, в то время (как документ изменен), я могу упростить запрос к:

SELECT r1.doc, r2.doc, count(*) AS matches 
FROM `wp_relevanssi` r1 
INNER JOIN `wp_relevanssi` r2 
ON r1.term = r2.term 
AND r1.doc <> r2.doc 
AND r1.type = r2.type 
AND (r1.content > 0 or r1.title > 0 or r1.taxonomy > 0 or r1.tag > 0) 
AND (r2.content > 0 or r2.title > 0 or r2.taxonomy > 0 or r2.tag > 0) 
WHERE r1.doc = %d 
GROUP BY r1.doc, r2.doc 
ORDER BY matches DESC 
LIMIT 0,10 

Который работает в разумные сроки, даже с 650000 строк, а последующие с:

SELECT doc, COUNT(term) AS total 
FROM `wp_relevanssi` 
WHERE doc IN (%d,%d,%d...) 
GROUP BY doc 

затем сделать остальную часть партитуры матча за пределами БД.

+0

попробовать композитный индекс на '(срок , type, doc) '. 'doc' должен быть последним, потому что он используется в неравенстве в вашем соединении. этот индекс может ускорить ваше присоединение – FuzzyTree

+0

Привет @FuzzyTree, спасибо за предложение, в этом случае, к сожалению, структура таблицы не в моих силах - сама таблица исходит от стороннего плагина, я просто пытаюсь сделать некоторый анализ на нем , Из интереса и моего собственного назидания, вы имеете в виду, что это ускорит первое соединение (с самим собой) или последующие объединения с подзапросами? –

+0

Если схема находится вне вашего контроля, но вы хотите поэкспериментировать, скопируйте таблицу. –

ответ

1
  • COUNT(term) означает, что вам необходимо проверить term для того NOT NULL. Если нет, то просто скажите COUNT(*).

  • LEFT JOINs похоже, что они идентичны; что дает? Смотри ниже.

  • JOIN (SELECT ...) оптимизирует плохо , когда у вас есть более чем один из них.

  • LEFT означает, что в таблице «справа» могут отсутствовать строки, но в этом случае вы хотите NULLs. Вам это нужно?

  • Показатели «Префикс» (KEY terms (term(20))) редко бывают полезными и часто препятствуют использованию индекса. Удалите (20).

  • Таблицы InnoDB должны иметь явный PRIMARY KEY. Ключ UNIQUE, который вы могли бы превратить в него.

  • Этот запрос представляется O (N * N).То есть, он будет быстро (т. Е. Квадратично) замедляться по мере увеличения числа строк (N) в wp_relevanssi.

Для подзапроса дублировать, рассмотрим следующую информацию и использовать term_counts в двух местах.

CREATE TABLE term_counts (
    PRIMARY KEY(doc) 
) 
    SELECT doc, 
      COUNT(term) AS total 
     FROM `wp_relevanssi` 
     GROUP BY doc; 

Из-за этого

(r1.content > 0 or r1.title > 0 or r1.taxonomy > 0 or r1.tag > 0) 

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

Из

ON r1.term = r2.term 
AND r1.doc > r2.doc 
AND r1.type = r2.type 

Я согласен с

INDEX(term, type, doc) 

(документ должен быть последним, срок и тип может быть в любом порядке.)

+0

Спасибо за руководство. Поскольку у меня нет контроля над схемой, и мне приходится работать с динамическими данными (это не просто статический набор данных, на который я могу сделать один раз запрос) Я не могу реализовать большинство этих изменений, но он подтверждает для меня большая часть проблемы заключается в том, что дублированный подзапрос (я надеялся, что движок сможет оптимизировать, учитывая, что запрос был одинаков). Мне просто пришлось немного изменить свой подход, чтобы заняться этим. –

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