оптимизация OR в связях таблиц

berkut

Новичок
оптимизация OR в связях таблиц

как оптимизировать связь по ON (t1.=? OR t1. = ??) ? Собственно есть 3 таблицы: шины, шины-машины(связь какой машине какая подходит шина) и синонимы шин(шина указывает на другую шину). нужно выбрать ID машин, к которым подходит определённая марка шины с учётом синонимов.
не проигнорьте плиз, старался структуру упрощенную делал
Код:
CREATE TABLE car_tyres (
  car_id mediumint(9) unsigned NOT NULL default '0',
  tyre_id mediumint(9) unsigned NOT NULL default '0',
  UNIQUE KEY tyre_id (tyre_id,car_id)
) ENGINE=MyISAM DEFAULT CHARSET=cp1251;

INSERT INTO `car_tyres` (car_id, tyre_id) VALUES
  (1,1),
  (1,2);

CREATE TABLE tyres (
  id mediumint(9) unsigned NOT NULL auto_increment,
  name varchar(20) NOT NULL default '',
  params varchar(20) NOT NULL default '',
  PRIMARY KEY  (id),
  FULLTEXT KEY name (name)
) ENGINE=MyISAM DEFAULT CHARSET=cp1251;

INSERT INTO `tyres` (`id`, `name`, `params`) VALUES 
  (1, 'kama', ''),
  (2, 'пирелли', ''),
  (3, 'неВыберется', ''),
  (4, '*naKama', '');
  
CREATE TABLE tyre_synonyms (
  tyre_id mediumint(9) unsigned NOT NULL default '0',
  synonym_of mediumint(9) unsigned NOT NULL default '0',
  UNIQUE KEY tyre_id (tyre_id,synonym_of)
) ENGINE=MyISAM DEFAULT CHARSET=cp1251;

INSERT INTO `tyre_synonyms` (tyre_id, synonym_of) VALUES
  (4,1);
запрос:
Код:
SELECT SQL_NO_CACHE car_tyres.*
FROM tyres
    LEFT JOIN tyre_synonyms ON tyres.id = tyre_synonyms.tyre_id
    INNER JOIN car_tyres ON (car_tyres.tyre_id = tyres.id 
        OR car_tyres.tyre_id = tyre_synonyms.synonym_of)
WHERE MATCH(tyres.name) AGAINST ('*naKama kama' IN BOOLEAN MODE)
GROUP BY car_tyres.car_id
EXplain
Код:
+----+-------------+---------------+----------+---------------+---------+-------
--+---------------+------+------------------------------------------------+
| id | select_type | table         | type     | possible_keys | key     | key_len | ref           | rows | Extra                                          |
+----+-------------+---------------+----------+---------------+---------+-------
--+---------------+------+------------------------------------------------+
|  1 | SIMPLE      | tyres         | fulltext | PRIMARY,name  | name    |0 |               |    1 | Using where; Using temporary; Using filesort   |
|  1 | SIMPLE      | tyre_synonyms | ref      | tyre_id       | tyre_id |3 | test.tyres.id |    1 | Using index                                    |
|  1 | SIMPLE      | car_tyres     | ALL      | tyre_id       | NULL    |    NULL | NULL          |    2 | Range checked for each record (index map: 0x1) |
+----+-------------+---------------+----------+---------------+---------+-------
--+---------------+------+------------------------------------------------+
3 rows in set (0.00 sec)
Главный вопрос: как избавится от Range checked for each record? Из-за него огромные тормоза - по 6 секунд выполняется на 8000 рядах в car_tyres

-~{}~ 07.05.08 09:16:

т.е. даже using temporary, file sort у меня не так страшны как each record. Всё дело в том, что под условие попадает не так много рядов.

-~{}~ 07.05.08 09:19:

смысл запроса и синонимов в том, что при поиске по '*naKama'(это отдельная шина, но она ещё и является указателем на kama) должны выбраться машины, к которым подходит резина kama.
 

Gas

может по одной?
Главный вопрос: как избавится от Range checked for each record?
обычное решение в лоб - избавиться от OR сделав 2 запроса через union, но вот будет ли быстрее, даже предположить не могу :)

Кстати, wildcard(*) же работает только если стоит в конце слова, а не начале или ошибаюсь?
 

berkut

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

-~{}~ 07.05.08 10:52:

типо кошу под указатель си. юнион попробую

-~{}~ 07.05.08 14:46:

вот чтобы эмулировать мой запрос юнионами, нужно использовать временную таблицу - так как мне нужна группировка по всем данным, а не DISTINCT. пробую так:
Код:
SELECT * FROM (
    (SELECT SQL_NO_CACHE car_tyres.*
    FROM tyres    
        LEFT JOIN tyre_synonyms ON tyres.id = tyre_synonyms.tyre_id
        INNER JOIN car_tyres ON car_tyres.tyre_id = tyre_synonyms.synonym_of
    WHERE MATCH(tyres.name) AGAINST ('*naKama kama' IN BOOLEAN MODE)
    )
    UNION
    (SELECT SQL_NO_CACHE car_tyres.*
    FROM tyres    
        INNER JOIN car_tyres ON car_tyres.tyre_id = tyres.id
    WHERE MATCH(tyres.name) AGAINST ('*naKama kama' IN BOOLEAN MODE)
    )
) AS t
ошибка: Every derived table must have its own alias
пробывал этот AS t засунуть под скобку - тоже болт.
подобный запрос вообще возможен? т.е. я хочу чтобы сначала отработал юнион(во временной таблице повис, и по этой таблице сделать GROUP BY... ORDER BY

-~{}~ 07.05.08 14:51:

сам юнион работает гораздо быстрее, но не выполняет требуемый функционал - я не могу сгруппировать результаты юниона

-~{}~ 07.05.08 14:51:

а если группировать внутри каждого запроса из юнион - это уже не то
 

Gas

может по одной?
berkut
разве дописывание GROUP BY после AS t не даёт нужного результата? (я на 4-ке запустил, работает, но адекватность результатов не проверял).
 

berkut

Новичок
хе как плохо. на 4,1,8-max запрос не пашет, а в пятой пашет. именно запрос из примера, копи-паст. дажже без груп бай
 

Gas

может по одной?
я тестил на 4.1.20
в 4.1.19 пофикшен баг
"A FULLTEXT query in a UNION could result in unexpected behavior." - может это имеет отношение, а может и нет.
в любом случае или обновляй 4-ку или переходи на 5-ку.
 

berkut

Новичок
ёлки. еслиб я мог обновить.. а где про фул-текст сказано? и почему.. может имеется ввиду не булевый, ибо сортирует сразу... хз

-~{}~ 07.05.08 15:15:

всё понял, это был баг и его пофиксили
 

Gas

может по одной?
berkut
поделись потом результатами, насколько новый запрос медленнее/быстрее первоначального.
 

berkut

Новичок
чудеса продолжаются. экспортировал реальную бд в mysql 5.0.6 nt - изначальный запрос стал тормознее в 6 раз. запускаю его в 4 - одно, в пятёрке - в 6 раз медленее выполняется. эксплейны одинаковые. обновлю пятёрку, отпишусь

-~{}~ 08.05.08 15:25:

Gas
вообщем на реальном запросе, когда под выборку попадает не очень много рядов выигрыш с юнион в 8-10 раз. Если рядов много, то разница уменьшается.
 
Сверху