【问题标题】:Where should I place Indexes in MySQL Tables我应该将索引放在 MySQL 表中的什么位置
【发布时间】:2011-09-21 21:12:14
【问题描述】:

我有以下三个WHERE 子句:

WHERE primaryId = $imgId AND imgWidth = $maxImageWidth AND imgHeight = $maxImageHeight

WHERE primaryId = $imgId AND imgWidth = $maxImageWidth AND imgHeight != $maxImageHeight

WHERE primaryId = $imgId AND imgWidth != $maxImageWidth AND imgHeight = $maxImageHeight"

他们正在对两个 MySQL InnoDB 表进行操作,该表使用UNION ALL 加入查询。

我不确定我应该如何在这两个表中设置索引;我是否应该有一个多列索引,包含imgWidthimgHeight,还是应该包含primaryId

查询只能使用一个索引是真的吗?如果没有,我可以将每一列设置为索引吗?

或者在这种情况下多列索引不起作用?


这是第一个WHERE 子句的整个查询示例。其他同理,各有分句:

SELECT 'allEqual' AS COL1,COUNT(*) AS imgCount FROM (
    SELECT imgHeight, imgWidth, imgId AS primaryId FROM primary_images
    UNION ALL 
    SELECT imgHeight, imgWidth, primaryId FROM secondary_images
) AS union_table
WHERE primaryId = $imgId AND imgWidth = $maxImageWidth AND imgHeight = $maxImageHeight

这是primary_images 表的架构:

CREATE  TABLE IF NOT EXISTS `new_arrivals_images`.`primary_images` (
  `imgId` SMALLINT(6) UNSIGNED NOT NULL AUTO_INCREMENT ,
  `imgTitle` VARCHAR(255) NULL DEFAULT NULL ,
  `view` VARCHAR(45) NULL DEFAULT NULL ,
  `secondary` ENUM('true','false') NOT NULL DEFAULT false ,
  `imgURL` VARCHAR(255) NULL DEFAULT NULL ,
  `imgWidth` SMALLINT(6) UNSIGNED NULL DEFAULT NULL ,
  `imgHeight` SMALLINT(6) UNSIGNED NULL DEFAULT NULL ,
  `imgDate` DATETIME NULL DEFAULT NULL ,
  `imgClass` ENUM('Jeans','T-Shirts','Shoes','Dress Shirts','Trackwear & Sweatwear') NULL DEFAULT NULL ,
  `imgFamily` ENUM('Hugo Boss','Lacoste','True Religion','7 For All Mankind','Robin\'s Jeans','Robert Graham') NULL DEFAULT NULL ,
  `imgGender` ENUM('Men\'s','Women\'s') NOT NULL DEFAULT Mens ,
  PRIMARY KEY (`imgId`) ,
  UNIQUE INDEX `imgDate_UNIQUE` (`imgDate` DESC) )
ENGINE = InnoDB;

secondary_images 表的架构:

CREATE  TABLE IF NOT EXISTS `new_arrivals_images`.`secondary_images` (
  `imgId` SMALLINT(6) UNSIGNED NOT NULL AUTO_INCREMENT ,
  `primaryId` SMALLINT(6) UNSIGNED NOT NULL ,
  `view` VARCHAR(45) NULL DEFAULT NULL ,
  `imgURL` VARCHAR(255) NULL DEFAULT NULL ,
  `imgWidth` SMALLINT(6) UNSIGNED NULL DEFAULT NULL ,
  `imgHeight` SMALLINT(6) UNSIGNED NULL DEFAULT NULL ,
  `imgDate` DATETIME NULL DEFAULT NULL ,
  PRIMARY KEY (`imgId`, `primaryId`) ,
  INDEX `fk_secondary_images_primary_images` (`primaryId` ASC) ,
  UNIQUE INDEX `imgDate_UNIQUE` (`imgDate` DESC) ,
  CONSTRAINT `fk_secondary_images_primary_images`
    FOREIGN KEY (`primaryId` )
    REFERENCES `new_arrivals_images`.`primary_images` (`imgId` )
    ON DELETE CASCADE
    ON UPDATE CASCADE)
ENGINE = InnoDB;

【问题讨论】:

  • 您的代码是一个 SQL 注入漏洞。请将您的所有$vars 括在单引号中:where field1 = '$var' ... 并且不要忘记在将它们注入查询之前使用$var = mysql_real_escape_string($var)。见:stackoverflow.com/questions/332365/…

标签: mysql database database-design indexing


【解决方案1】:

查询只能使用一个索引是真的吗?

没有。那太傻了。

如果没有,我可以将每一列设置为索引吗?

是的,这是一个选项,但前提是您可以相互独立地使用该列。
如果您总是组合字段,就像您在这里所做的那样,使用复合索引会更有效。

我不确定应该如何在这两个表中设置索引;我是否应该有一个带有 imgWidth 和 imgHeight 的多列索引,或者它是否也应该包含 primaryId?

如果你想可以使用结合(imgWidth, imgHeight)的复合索引
您必须记住,如果不在 where 子句中使用 imgWidth,您将无法访问 imgHeight 上的索引。
您必须始终使用复合索引的最左侧部分(或全部)。

在 InnoDB 上,主键总是包含在每个二级索引中,因此包含它会适得其反。

在 InnoDB 上增加了奖励
如果只选择索引字段,InnoDB 将永远不会真正读取 tabledata,因为所需的所有数据都在索引中。这将大大加快速度。

您有一个 SQL 注入漏洞
您的代码似乎有一个 SQL 注入漏洞。请将您的所有 $vars 括在单引号中:where field1 = '$var' ... 并且不要忘记在将它们注入查询之前使用 $var = mysql_real_escape_string($var);。见:How does the SQL injection from the "Bobby Tables" XKCD comic work?

为了速度和安全,查询应为:

SELECT 'allEqual' AS COL1, COUNT(*) AS imgCount FROM (
    SELECT imgId AS primaryId FROM primary_images pi
    WHERE pi.ImgId = '$imgId' 
      AND pi.imgWidth = '$maxImageWidth' 
      AND pi.imgHeight = '$maxImageHeight'
    UNION ALL 
    SELECT primaryId FROM secondary_images si
    WHERE si.primaryId = '$imgId' 
      AND si.imgWidth = '$maxImageWidth'    
      AND si.imgHeight = '$maxImageHeight'  
) AS union_table                      

这样会使用正确的索引,不会检索到不需要的数据。
MySQL 不能对联合数据使用索引,因为它是两个不同表的合并。这就是为什么您需要在内部选择中执行where

【讨论】:

  • 那是约翰的热门回答。你更清楚地解释了我几天来一直在读的东西。谢谢一堆。我很好奇,我有另一个查询,其中 WHERE 子句是可变的,也就是说内容会根据情况而变化。如果我在另一个问题中发布,您介意看一下吗?
  • 是的,但不是今天,我现在要睡觉了。如果您在下面发表评论并附上问题的链接,我会在 2 明天查看。
  • 好吧,荷兰……我想这比我早 8 小时 ;) 所以我会在 睡觉之前发布它,然后你应该得到就在那时!再次感谢。
  • 很抱歉,我没有更改发布我希望您查看的其他问题,直到现在。这是:stackoverflow.com/q/7521010/708274
  • 我也终于有机会彻底查看您提供的修改后的查询,我有一些问题。您是说不需要在查询的secondary_images 部分的WHERE 中使用imgWidthimgHeight,因为它只会返回1 行?澄清一下,imgIdprimary_images 表的主键,但如果在这种情况下它不包含两个最大值,我不希望它被计算在内。 secondary_images 表中有一个多列主键:imgId 是唯一的,primaryId 不是。
【解决方案2】:

您的 primaryId 列是否有任何重复项?还是它是主键?如果它是一个主键,那么它也将作为一个很好的索引。在 InnoDB 中,如果它是主键,它可能已经是一个索引。

换句话说,您的 WHERE 子句 primaryId = $imgId 的区分度如何?如果它通常不匹配,或者只匹配一个,或者只匹配几行,那么另一个索引将无济于事。如果它匹配成百上千行,另一个索引可能会有所帮助。

查询绝对可以使用多个索引。

这是最大问题是“您要做什么?”的案例之一。似乎您正在尝试选择一个或两个尺寸与您的输入匹配的图像。

考虑通过重做逻辑并去掉 UNION ALL 子句(变成三个查询)来提高效率。

    WHERE primaryId = $imgId 
      AND (imgWidth = $maxImageWidth OR imgHeight = $maxImageHeight)

【讨论】:

  • 我添加了查询其余部分的示例,以及两个表的组合。我要出去了,但我会尽力回答任何问题,并尽快评论你的答案。非常感谢您的回复。 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-12-04
  • 2013-02-26
  • 2012-07-22
  • 2012-11-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多