查询结果需要太长时间。有没有更好的方法来编写这个MySQL查询?

时间:2017-05-25 16:35:06

标签: mysql sql query-optimization

我正在尝试optimize mysql一个完美运行但查询时间过长的查询。我的库存表几乎 300,000条记录(还不错)。我不确定使用subqueryjoin或其他index会加快我的搜索结果。我确实在学生和库存表中都有索引的district_id列。

基本上,下面的query会拉出教师名单中所有学生的所有库存。因此,首先必须搜索学生表以查找哪些学生在教师名单中,然后必须在每个学生的库存表中搜索。因此,如果教师有30多名学生,则可以通过库存进行大量搜索,每个学生可以拥有30多个库存。任何意见将是有益的!

SELECT  inventory.inventory_id, items.title, items.isbn, items.item_num,
        items.price, conditions.condition_name, inventory.check_out,
        inventory.check_in, inventory.student_id, inventory.teacher_id
    FROM  inventory, conditions, items, students
    WHERE  students.teacher_id = '$teacher_id'
      AND  students.district_id = $district_id
      AND  inventory.student_id = students.s_number
      AND  inventory.district_id = $district_id
      AND  inventory.item_id = items.item_id
      AND  items.consumable !=1
      AND  conditions.condition_id = inventory.condition_id
    ORDER BY  inventory.student_id, inventory.inventory_id

这是表结构:

CREATE TABLE `inventory` (
  `id` int(11) NOT NULL,
  `inventory_id` varchar(10) CHARACTER SET utf8 NOT NULL DEFAULT '0',
  `item_id` int(6) NOT NULL DEFAULT '0',
  `district_id` int(2) NOT NULL DEFAULT '0',
  `condition_id` int(1) NOT NULL DEFAULT '0',
  `check_out` date NOT NULL DEFAULT '0000-00-00',
  `check_in` date NOT NULL DEFAULT '0000-00-00',
  `student_id` varchar(10) CHARACTER SET utf8 NOT NULL DEFAULT '0',
  `teacher_id` varchar(6) CHARACTER SET utf8 NOT NULL DEFAULT '0',
  `acquisition_date` date NOT NULL DEFAULT '0000-00-00',
  `notes` text CHARACTER SET utf8 NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

2 个答案:

答案 0 :(得分:0)

首先,你重写它以使用显式JOIN:

SELECT inventory.inventory_id, 
       items.title, items.isbn, items.item_num, items.price, 
       conditions.condition_name,
       inventory.check_out,  inventory.check_in, 
       inventory.student_id, inventory.teacher_id  
FROM inventory
  JOIN conditions ON (conditions.condition_id = inventory.condition_id)
  JOIN items ON (inventory.item_id = items.item_id AND items.consumable != 1)
  JOIN students ON (inventory.student_id = students.s_number) 
WHERE students.teacher_id = '$teacher_id' 
  AND students.district_id = $district_id
  AND inventory.district_id = $district_id 
ORDER BY inventory.student_id, inventory.inventory_id

然后检查JOIN。例如:

  JOIN items ON (inventory.item_id = items.item_id AND items.consumable != 1)

表示需要在item_id和耗材上扫描items表,可能是常量。如果可能的话,最好不要使用负面条件。但至少你在item_id上索引项目(除非它已经是主键,很可能)。如果消费品可以假设,例如,值0,1,2,3,那么你去:

  JOIN items ON (inventory.item_id = items.item_id AND items.consumable IN (0, 2, 3))

并使用CREATE INDEX在耗材上添加索引。

您可能会注意到,库存中的一些列总是在其他JOIN中使用,并且还存在一些常量约束。

所以另一个有用的索引可能是

 CREATE INDEX ... ON inventory(district_id, student_id, item_id, condition_id)

另一个有用的索引是

ON学生(teacher_id,district_id,student_id,s_number)

允许立即限制所涉及的学生的WHERE,并在不加载表的情况下检索JOIN所需的信息,只使用索引。

答案 1 :(得分:0)

切换到InnoDB!我要说的一些内容在InnoDB中效率较低。

SELECT  i.inventory_id,
        items.title, items.isbn, items.item_num, items.price,
        c.condition_name,
        i.check_out, i.check_in, i.student_id, i.teacher_id
    FROM  inventory AS i
    JOIN  conditions AS c  ON c.condition_id = i.condition_id
    JOIN  items  ON i.item_id = items.item_id
    JOIN  students AS s  ON  i.student_id = s.s_number
    WHERE  s.teacher_id = '$teacher_id'
      AND  s.district_id = $district_id
      AND  i.student_id = s.s_number
      AND  i.district_id = $district_id
      AND  items.consumable != 1
    ORDER BY  i.student_id, i.inventory_id 

如果希望以students

开头,请为优化工具提供帮助
students: INDEX(district_id, teacher_id, s_number)

注意:这也是"覆盖",从而避免索引BTree和数据BTree之间的弹跳。 (students的PK是多少?请提供SHOW CREATE TABLE。)

如果更好地使用ORDER BY

inventory: INDEX(district_id, student_id, inventory_id)

还需要:

items:      (item_id)       -- probably already the PRIMARY KEY?
conditions: (condition_id)  -- probably already the PRIMARY KEY?

验证或添加这4个索引。 (优化程序将动态选择要执行的操作。)

相关问题