Помогите мне оптимизировать этот запрос MySql - PullRequest
1 голос
/ 31 августа 2011

У меня есть запрос MySql, который выполняется очень долго (около 7 секунд). Кажется, проблема в OR в этой части запроса: «(tblprivateitem.userid =? Userid OR tblprivateitem.userid = 1)». Если я пропущу часть «ИЛИ tblprivateitem.userid = 1», это займет всего 0,01 секунды. Поскольку мне нужна эта часть, мне нужно найти способ оптимизировать этот запрос. Есть идеи?

QUERY:

SELECT 
    tbladdeditem.addeditemid,
    tblprivateitem.iitemid,
    tblprivateitem.itemid
FROM tbladdeditem 
INNER JOIN tblprivateitem 
    ON tblprivateitem.itemid=tbladdeditem.itemid 
        AND (tblprivateitem.userid=?userid OR tblprivateitem.userid=1)
WHERE tbladdeditem.userid=?userid

EXPLAIN:

id    select_type    table            type    possible_keys    key    key_len    ref                    rows    extra
1     SIMPLE         tbladdeditem     ref     userid           userid 4          const                  293     Using where
1     SIMPLE         tblprivateitem   ref     userid,itemid    itemid 4          tbladdeditem.itemid    2       Using where

ТАБЛИЦА:

tbladdeditem содержит 1 100 000 строк:

CREATE TABLE `tbladdeditem` (
    `addeditemid` int(11) NOT NULL auto_increment,
    `itemid` int(11) default NULL,
    `userid` mediumint(9) default NULL,
    PRIMARY KEY  (`addeditemid`),
    KEY `userid` (`userid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

tblprivateitem содержит 2 700 000 строк:

CREATE TABLE `tblprivateitem` (
    `privateitemid` int(11) NOT NULL auto_increment,
    `userid` mediumint(9) default '1',
    `itemid` int(10) NOT NULL,
    `iitemid` mediumint(9) default NULL,
    PRIMARY KEY  (`privateitemid`),
    KEY `userid` (`userid`),
    KEY `itemid` (`itemid`) //Changed this index to only use itemid instead
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Ответы [ 3 ]

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

Так как у вас есть условие предиката tbladdeditem.userid=?userid в предложении where, я не думаю, что оно вам нужно в условии соединения. Попробуйте удалить его из условия соединения и (Если вы используете Or для обработки случая, когда параметр имеет значение null, затем используйте Coalesce вместо OR), если не оставить его как Or

-- If Or is to provide default for when (?userid is null...
      SELECT  a.addeditemid, p.iitemid,  p.itemid 
      FROM tbladdeditem a 
        JOIN tblprivateitem p
          ON p.itemid=a.itemid 
      WHERE a.userid=?userid 
         AND p.userid=Coalesce(?userid, 1)
-- if not then
      SELECT  a.addeditemid, p.iitemid,  p.itemid 
      FROM tbladdeditem a 
        JOIN tblprivateitem p
          ON p.itemid=a.itemid 
      WHERE a.userid=?userid 
         AND (p.userid=?userid Or p.userid = 1)

Во-вторых, если в этих двух таблицах нет индекса для столбца userId, рассмотрите возможность добавления одного.

Наконец, если все это не удалось, попробуйте преобразовать в два отдельных запроса и объединить их вместе:

      Select  a.addeditemid, p.iitemid,  p.itemid 
      From tbladdeditem a 
        Join tblprivateitem p
          On p.itemid=a.itemid 
            And p.userId = a.Userid
      Where p.userid=?userid 
      Union
      Select a.addeditemid, p.iitemid,  p.itemid 
      From tbladdeditem a 
        Join tblprivateitem p
          On p.itemid=a.itemid 
            And p.userId = a.Userid
      Where p.userid = 1
1 голос
/ 31 августа 2011

ОБНОВЛЕНИЕ

Я сделал так, чтобы мои запросы и схема точно соответствовали вашему исходному вопросу, ключу из нескольких столбцов и всем.Единственное возможное отличие состоит в том, что я заполнил каждую таблицу двумя миллионами записей.Мой запрос (ваш запрос) выполняется за 0,15 секунды.

delimiter $$
set @userid = 6
$$
SELECT 
    tbladdeditem.addeditemid,    tblprivateitem.iitemid,    tblprivateitem.itemid
FROM tbladdeditem 
INNER JOIN tblprivateitem 
    ON tblprivateitem.itemid=tbladdeditem.itemid 
        AND (tblprivateitem.userid=@userid or tblprivateitem.userid = 1)
WHERE tbladdeditem.userid=@userid

У меня есть то же объяснение, что и вы, и с моими данными мой запрос возвращает более тысячи совпадений без каких-либо проблем.Быть в полном недоумении, поскольку у вас действительно не должно быть этих проблем - возможно ли, что вы используете очень ограниченную версию MySQL?Вы работаете в 64-битной версии?Достаточно памяти?

Я сделал предположение, что ваш запрос не работает должным образом, а когда мой был, предположил, что я исправил вашу проблему.Так что теперь я ем ворону.Я опубликую некоторые из проспектов, которые я пошел вниз.Но я говорю вам, ваш запрос, как вы разместили его изначально, работает просто отлично.Я могу только представить, что ваш MySQL побежал на жесткий диск или что-то.Извините, я не мог больше помочь.

ПРЕДЫДУЩИЙ ОТВЕТ (который также является обновлением)

Я сломал и воссоздал вашу проблему в моей собственной базе данных.После попытки независимых индексов в userid и itemid мне не удалось получить запрос ниже нескольких секунд, поэтому я настроил очень специфические ключи из нескольких столбцов, как указано в запросе.Обратите внимание, что tbladdeditem запрос с несколькими столбцами начинается с itemid, а на tblprivateitem столбцы меняются местами:

Вот схема, которую я использовал:

CREATE TABLE `tbladdeditem` (
  `addeditemid` int(11) NOT NULL AUTO_INCREMENT,
  `itemid` int(11) NOT NULL,
  `userid` mediumint(9) NOT NULL,
  PRIMARY KEY (`addeditemid`),
  KEY `userid` (`userid`),
  KEY `i_and_u` (`itemid`,`userid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `tblprivateitem` (
  `privateitemid` int(11) NOT NULL AUTO_INCREMENT,
  `userid` mediumint(9) NOT NULL DEFAULT '1',
  `itemid` int(10) NOT NULL,
  `iitemid` mediumint(9) NOT NULL,
  PRIMARY KEY (`privateitemid`),
  KEY `userid` (`userid`),
  KEY `itemid` (`itemid`),
  KEY `u_and_i` (`userid`,`itemid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Я заполнилкаждая таблица с 2 миллионами записей случайных данных.Я сделал несколько предположений:

  • userid варьируется от 1 до 2000
  • itemid варьируется от 1 до 10000

Это дает каждому пользователю около тысячи записей вкаждая таблица.

Вот две версии запроса (я использую верстак для своего редактора):

Версия 1 - выполнить всю фильтрацию при объединении.

Результат: 0,016 секунды, чтобы вернуть 1297 строк

delimiter $$
set @userid = 3
$$
SELECT 
    a.addeditemid,
    p.iitemid,
    p.itemid
FROM tblprivateitem as p
INNER JOIN tbladdeditem as a
    ON (p.userid in (1, @userid))
        AND p.itemid = a.itemid 
        AND a.userid = @userid
$$

Вот объяснение:

EXPLAIN: 
id select_type table type  key     ref  rows extra
1  SIMPLE      p     range u_and_i      2150 Using where; Using index
1  SIMPLE      a     ref   i_and_u      1    Using where; Using index

Версия 2 - предварительная фильтрация

Результат: 0,015 секунды, чтобы вернуть 1297 строк

delimiter $$
set @userid = 3
$$
SELECT 
    a.addeditemid,
    p.iitemid,
    p.itemid
from 
  (select userid, itemid, iitemid from tblprivateitem 
      where userid in (1, @userid)) as p
  join tbladdeditem as a on p.userid = a.userid and a.itemid = p.itemid;
where a.userid = @userid
$$

Вот объяснение:

id select_type table      type  key     ref               rows extra
1  PRIMARY     <derived2> ALL   null    null              2152
1  PRIMARY     a          ref   i_and_u p.itemid,const    1    Using where; Using index
2  DERIVED     p1         range u_and_i                   2150 Using where
1 голос
/ 31 августа 2011

Я бы попробовал это вместо этого, на вашем исходном JOIN у вас есть OR, связанный с параметром, переместите его в ваше предложение WHERE.

SELECT 
    tbladdeditem.addeditemid,
    tblprivateitem.iitemid,
    tblprivateitem.itemid
FROM tbladdeditem 
INNER JOIN tblprivateitem 
    ON tblprivateitem.itemid=tbladdeditem.itemid 
WHERE tbladdeditem.userid=?userid
    AND (tblprivateitem.userid=?userid OR tblprivateitem.userid=1)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...