2010-11-11 5 views
1

Cette requête vous montrera tous les sujets non lus dans un forum donné. La sortie de EXPLAIN EXTENDED était un peu alarmante. Je me demande si quelqu'un ici peut donner un aperçu de la façon dont je peux optimiser.Optimisation de la requête simple

SELECT topic.* 
FROM topic 
INNER JOIN board ON topic.board_id = board.id OR topic.board_id = board.mirror_board_id 
INNER JOIN category ON board.category_id = category.id 
INNER JOIN group_assoc 
ON (
    group_assoc.board_id = board.id AND 
    group_assoc.group_id IN (4,15,18,22) AND 
    group_assoc.viewable = 1 
) 
WHERE topic.last_post_time > 1288278402 
AND category.forum_id = 2 
AND board.id NOT IN(4,3) 
AND NOT EXISTS (
    SELECT * 
    FROM topic_read_assoc 
    WHERE topic_id = topic.id 
    AND member_id = 332 
) 
ORDER BY topic.last_post_time DESC 

sortie:

id select_type table type possible_keys key key_len ref rows filtered Extra 
1 PRIMARY category ref PRIMARY,forum_id_2 forum_id_2 4 const 5 100.00 Using temporary; Using filesort 
1 PRIMARY board ref PRIMARY,mirror_board_id,category_id_2 category_id_2 4 source_forum.category.id 4 100.00 Using where 
1 PRIMARY group_assoc ref board_id,board_id_2,board_id_3 board_id_3 4 source_forum.board.id 4 100.00 Using where; Using index 
1 PRIMARY topic ALL board_id_2 NULL NULL NULL 2462 100.00 Range checked for each record (index map: 0x4) 
2 DEPENDENT SUBQUERY topic_read_assoc ref topic_id topic_id 8 source_forum.topic.id,const 1 100.00 Using index 
+5

oui. requête vraiment simple. :) – zod

+1

@zod: pourquoi, il correspond à l'écran, à la fois verticalement et horizontalement! – Quassnoi

Répondre

1

Plutôt que d'utiliser EXISTE pour la table group_assoc, utilisez une jointure interne à bord (ce qui est en vigueur qui se passe de toute façon avec « Où board_id = board.id ». Mettez les informations de filtrage dans la clause WHERE

va de même pour topic_read_assoc -... utiliser une jointure interne sur le sujet oN topic_id = topic.id

de plus, cela est juste pour le rendre easie r, vous pouvez utiliser la syntaxe IN pour Board.id il est donc seulement une ligne comme ceci:

OU ... board.id NOT IN (3,4)

EDIT> Comme Quassnoi à juste titre souligné ci-dessous L'ajout de jointures internes entraînera des doublons. Donc, utilisez DISTINCT ou GROUP BY ce que vous voulez voir. La requête ne devrait pas utiliser SELECT * de toute façon en passant. Si une colonne est ajoutée à cette table, votre code pourrait être cassé (pas dans la requête elle-même, mais par ce qui est fait avec les résultats).

+0

Qu'est-ce qui est mauvais dans 'EXISTS'? – Quassnoi

+0

EXISTS n'est pas mauvais en soi, mais un JOIN fonctionnera mieux dans la plupart des cas. – Konerak

+0

@Konerak: quels cas exactement? – Quassnoi

0

ce serait ma requête pour montrer tous les sujets non lus dans un forum

select * from forum_topic where forum_id = 1 and num_views = 0; 

exemple simple:

-- TABLES 

drop table if exists forum; 
create table forum 
(
forum_id int unsigned not null auto_increment primary key, 
title varchar(255) unique not null, 
num_topics int unsigned not null default 0 
)engine=innodb; 


drop table if exists forum_topic; 
create table forum_topic 
(
topic_id int unsigned not null auto_increment primary key, 
forum_id int unsigned not null, 
subject varchar(255) unique not null, 
num_views int unsigned not null default 0, 
num_replies int unsigned not null default 0, 
key (forum_id) 
)engine=innodb; 

delimiter # 

create trigger forum_topic_after_ins_trig after insert on forum_topic 
for each row 
begin 
update forum set num_topics=num_topics+1 where forum_id = new.forum_id; 
end# 

delimiter ; 

-- STORED PROCEDURES 

drop procedure if exists get_forum_topic; 

delimiter # 

create procedure get_forum_topic 
(
in p_topic_id int unsigned 
) 
begin 
    update forum_topic set num_views=num_views+1 where topic_id = p_topic_id; 
    select * from forum_topic where topic_id = p_topic_id; 
end # 

delimiter ; 

-- TEST DATA 

insert into forum (title) values ('forum1'),('forum2'); 

insert into forum_topic (forum_id, subject) values 
(1,'forum 1 topic 1'), (1,'forum 1 topic 2'), 
(2,'forum 2 topic 1'), (2,'forum 2 topic 2'); 

-- TESTING 

call get_forum_topic(1); 
call get_forum_topic(3); 
call get_forum_topic(2); 
call get_forum_topic(3); 
call get_forum_topic(2); 
call get_forum_topic(3); 

select * from forum; 
select * from forum_topic; 

select * from forum_topic where num_views = 0; 
+0

Je préfère rester avec une requête de base et ne pas s'aventurer dans les vues, les triggers, ou les procédures – Webnet

+0

je vais répéter la requête BASIC pour vous "select * from forum_topic où forum_id = 1 et num_views = 0" comment vous parvenez à mettre en œuvre ceci à l'aide d'un compteur de champ plus sprocs et tiggers contre un certain sql inline (eeek) nécessitant beaucoup d'allers-retours db est à vous, mais mettons en perspective ici, la question ci-dessus est la requête BASIC est un peu errrrm .. verbose. –

2

Créer un index sur .

Vous pouvez également supprimer LIMIT 1 de la sous-requête EXISTS, il est redondant.

+0

Bon appel sur l'index! – Webnet