MySQL在N项表中加入1对1

时间:2016-10-17 16:43:55

标签: mysql performance join

我的商店数据库结构需要一些帮助。要从类别中获取项目,已经有一些连接,但我想从其他表格添加图像到项目。主要项目表没有图像ID,因此我们只需从其他表中获取一个权重= 1。

所以,有一个结构:

CREATE TABLE IF NOT EXISTS `categories` (
  `category_id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
  `name`        VARCHAR(255)
                CHARACTER SET utf8mb4     DEFAULT NULL
  COMMENT 'Category title',
  `parent_id`   INT(11)          NOT NULL DEFAULT '0'
  COMMENT 'Category Parent ID',
  `status`      TINYINT(1)                DEFAULT '0'
  COMMENT 'Category active status',
  `weight`      INT(11)                   DEFAULT '0',
  `slug`        VARCHAR(255)
                CHARACTER SET utf8mb4     DEFAULT NULL
  COMMENT 'Category url alias',
  PRIMARY KEY (`category_id`)
)
  ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4
  COLLATE = utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS `items` (
  `item_id`     INT(11) UNSIGNED           NOT NULL AUTO_INCREMENT,
  `name`        VARCHAR(255)
                CHARACTER SET utf8mb4      NOT NULL DEFAULT ''
  COMMENT 'Item name',
  `description` TEXT CHARACTER SET utf8mb4 NOT NULL
  COMMENT 'Item description',
  `user_id`     INT(11) UNSIGNED           NOT NULL DEFAULT '0'
  COMMENT 'User id',
  `category_id` INT(11) UNSIGNED           NOT NULL DEFAULT '0'
  COMMENT 'Category id',
  `price`       DECIMAL(10, 2)             NOT NULL DEFAULT '0.00'
  COMMENT 'Item price',
  `status`      INT(1) UNSIGNED            NOT NULL DEFAULT '1'
  COMMENT 'Item status',
  `deleted`     INT(1) UNSIGNED            NOT NULL DEFAULT '0'
  COMMENT 'Delite status',
  `blocked`     INT(1) UNSIGNED            NOT NULL DEFAULT '0'
  COMMENT 'Block status',
  PRIMARY KEY (`item_id`)
)
  ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4
  COLLATE = utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS `items_images` (
  `id`      INT(11) UNSIGNED      NOT NULL AUTO_INCREMENT,
  `item_id` INT(10) UNSIGNED      NOT NULL DEFAULT '0',
  `file`    VARCHAR(255)
            CHARACTER SET utf8mb4 NOT NULL DEFAULT '',
  `weight`  INT(11) UNSIGNED      NOT NULL DEFAULT '0',
  `status`  TINYINT(1) UNSIGNED   NOT NULL DEFAULT '0',
  `deleted` TINYINT(1) UNSIGNED   NOT NULL DEFAULT '0',
  `created` INT(11) UNSIGNED      NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
)
  ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4
  COLLATE = utf8mb4_unicode_ci
  ROW_FORMAT = COMPACT;

从那里可以看出,类别结构里面还包含parent_id,所以我们总能得到类别树。

使用此查询也可以正常运行。

SELECT
  `i`.*,
  `c1`.`name` AS `category_name`,
  `c1`.`slug` AS `category_slug`,
  `c2`.`name` AS `subcategory_name`,
  `c2`.`slug` AS `subcategory_slug`
FROM `items` AS `i`
  LEFT JOIN `categories` AS `c2` ON `c2`.`category_id` = `i`.`category_id`
  LEFT JOIN `categories` AS `c1` ON `c1`.`category_id` = `c2`.`parent_id`
WHERE `i`.`deleted` = 0 AND `i`.`blocked` = 0 AND `i`.`status` = 1
ORDER BY `i`.`created` DESC
LIMIT 40

但是,如果我加入items_images喜欢:

SELECT
  `i`.*,
  `ii`.`file` AS `image`,
  `c1`.`name` AS `category_name`,
  `c1`.`slug` AS `category_slug`,
  `c2`.`name` AS `subcategory_name`,
  `c2`.`slug` AS `subcategory_slug`
FROM `items` AS `i`
  LEFT JOIN `items_images` AS `ii`
    ON `i`.`item_id` = `ii`.`item_id` AND `ii`.`weight` = 1
   AND `ii`.`status` = 1 AND `ii`.`deleted` = 0
  LEFT JOIN `categories` AS `c2` ON `c2`.`category_id` = `i`.`category_id`
  LEFT JOIN `categories` AS `c1` ON `c1`.`category_id` = `c2`.`parent_id`
WHERE `i`.`deleted` = 0 AND `i`.`blocked` = 0 AND `i`.`status` = 1
 ORDER BY `i`.`created` DESC LIMIT 40

有时需要花费1分钟才能完成14k项目的40k图像。

有什么我可以改进的吗?!请注意,可能还有没有图像的项目。这不是必需的。

小补充。即使单个图像加入项目也会使查询运行一分钟。这里有一个样本:

SELECT `i`.*, `ii`.`file` AS `image` FROM `items` AS `i` 
LEFT JOIN `items_images` AS `ii` ON `i`.`item_id` = `ii`.`item_id`
         AND `ii`.`weight` = 1 AND `ii`.`status` = 1 AND `ii`.`deleted` = 0 
WHERE `i`.`deleted` =0 AND `i`.`sold` =0 AND `i`.`blocked` =0 AND `i`.`status` = 1 
ORDER BY `i`.`created` DESC

3 个答案:

答案 0 :(得分:2)

  1. 命名约定不一致。
  2. 索引parent_id

    ALTER TABLE类别ADD INDEX parent_id_ind(parent_id ASC);

  3. 尝试再次运行您的查询。

  4. 在数据库中,您可以使用索引来提高数据检索的速度。通常在JOIN,WHERE和ORDER BY子句中使用的列上创建索引。

答案 1 :(得分:2)

我建议你改进你的结构,你可以接受或离开它,

我的建议:

    DROP TABLE IF EXISTS `category`;
  CREATE TABLE `category` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT 'Category title',
  `category_id` int(11) NOT NULL DEFAULT '0' COMMENT 'Category Parent ID',
  `status` enum('status1','status2') COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'Category active status',
  `weight` int(11) DEFAULT '0',
  `slug` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT 'Category url alias',
  PRIMARY KEY (`id`),
  KEY `category_id_ind` (`category_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

    DROP TABLE IF EXISTS `item`;
    CREATE TABLE `item` (
      `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
      `name` varchar(255) CHARACTER SET utf8mb4 NOT NULL DEFAULT '' COMMENT 'Item name',
      `description` text CHARACTER SET utf8mb4 NOT NULL COMMENT 'Item description',
      `user_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT 'User id',
      `category_id` int(11) unsigned NOT NULL DEFAULT '0' COMMENT 'Category id',
      `price` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT 'Item price',
      `status` enum('status1','status2') COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'status1' COMMENT 'Item status',
      `is_deleted` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT 'Delite status',
      `is_blocked` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT 'Block status',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

    DROP TABLE IF EXISTS `item_images`;
    CREATE TABLE `item_images` (
      `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
      `item_id` int(10) unsigned NOT NULL DEFAULT '0',
      `file_path` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL ,
      `weight` int(11) unsigned NOT NULL DEFAULT '0',
      `status` tinyint(1) unsigned NOT NULL DEFAULT '0',
      `is_deleted` tinyint(1) unsigned NOT NULL DEFAULT '0',
      `creation_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=COMPACT;

答案 2 :(得分:0)

添加复合索引:

items:  INDEX(deleted, blocked, status, created)  -- created must be last
items_images:  INDEX(item_id, weight, status, deleted)
categories:  INDEX(category_id, deleted, blocked, status)

除非另有说明,否则列的顺序并不重要。

删除LEFT,除非您认为有必要。

请提供EXPLAIN SELECT ...

迟缓的可能原因是必须检查weight=1的大量图像行。最重要的是,缺少任何有用的索引意味着进行昂贵的表扫描。