1

这可能是历史上发布到 SO 的最长查询。本质上,我想知道这对于执行我想要的查询是否是正确的想法,并且鉴于它非常慢,我将如何加快它的速度。

我一直在阅读有关数据规范化等的信息,也许这太规范化了?鉴于用户可以拥有数十万个项目,我需要最快的方式来执行它,并且它可以扩展。

我有六张桌子

--
-- Table structure for table `emails`
--

CREATE TABLE IF NOT EXISTS `emails` (
  `ID` int(5) NOT NULL AUTO_INCREMENT,
  `email` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `sent` smallint(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (`ID`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=5062 ;

-- --------------------------------------------------------

--
-- Table structure for table `ips`
--

CREATE TABLE IF NOT EXISTS `ips` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `ip` varchar(255) NOT NULL,
  PRIMARY KEY (`ID`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=7534 ;

-- --------------------------------------------------------

--
-- Table structure for table `user_items`
--

CREATE TABLE IF NOT EXISTS `user_items` (
  `ID` int(10) NOT NULL AUTO_INCREMENT COMMENT 'Allows sorting by last added..',
  `name` varchar(255) NOT NULL,
  `owner` varchar(255) NOT NULL,
  `folder` int(10) NOT NULL,
  `version` int(5) NOT NULL,
  PRIMARY KEY (`ID`),
  KEY `name` (`name`),
  KEY `folder` (`folder`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=10431 ;

-- --------------------------------------------------------

--
-- Table structure for table `data`
--

CREATE TABLE IF NOT EXISTS `data` (
  `ID` int(10) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `info` varchar(255) DEFAULT NULL,
  `inserted` varchar(255) NOT NULL,
  `version` int(5) NOT NULL,
  PRIMARY KEY (`ID`),
  KEY `inserted` (`inserted`),
  KEY `version` (`version`),
  KEY `name_version` (`name`,`version`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=7207 ;

-- --------------------------------------------------------

--
-- Table structure for table `data_emails`
--

CREATE TABLE IF NOT EXISTS `data_emails` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `email_id` int(5) NOT NULL,
  `name` varchar(255) NOT NULL,
  `version` int(5) NOT NULL,
  `time` int(255) NOT NULL,
  PRIMARY KEY (`ID`),
  KEY `version` (`version`),
  KEY `name_version` (`name`,`version`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=9849 ;

-- --------------------------------------------------------

--
-- Table structure for table `data_ips`
--

CREATE TABLE IF NOT EXISTS `data_ips` (
  `ID` int(5) NOT NULL AUTO_INCREMENT,
  `ns_id` int(5) NOT NULL,
  `name` varchar(255) NOT NULL,
  `version` int(5) NOT NULL,
  `time` int(255) NOT NULL,
  PRIMARY KEY (`ID`),
  KEY `version` (`version`),
  KEY `name_version` (`name`,`version`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=17988 ;

我需要实现的目标如下。我需要获取每个 user_item 并获取与之关联的数据、电子邮件和 ips。user_items 通过列 'name' 和 version 链接到 data、data_emails 和 data_ips。

data_emails 和 data_nameservers 分别链接到 emails 和 ips,其中 email_id/ip_id = ID

因为多个连接会导致行乘法,所以我不得不使用嵌套子查询。因为一个用户可以有多个与一个项目相关联的电子邮件和 ips,所以我使用 group_concat 对所有特定行进行分组。然后我在我的 PHP 中分解了这一列 - 这本身似乎效率低下,但我看不到其他方法吗?

我玩过索引,但是由于复杂的连接以及我对此很陌生的事实,我不确定要拥有哪些索引。任何人都可以建议索引并解释它们吗?

SELECT user_items.ID, user_items.name, user_items.update,data.*, x.emails,
    x.e_status, y.ip, x.email_counts, y.ip_counts
FROM user_items
    LEFT JOIN data AS data
        ON (data.name = user_items.name AND data.version = user_items.version)
    LEFT JOIN (
        SELECT data_emails.name, data_emails.version,
            GROUP_CONCAT( b.email SEPARATOR ',' ) as emails,
            GROUP_CONCAT( b.sent SEPARATOR ',' ) as e_status,
            GROUP_CONCAT( b.email_count SEPARATOR ',' ) as email_counts
        FROM data_emails 
            LEFT JOIN (
                SELECT emails.ID, emails.email, emails.sent, data_emails.name,
                    data_emails.version, data_emails.email_id,
                    COUNT(data_emails.ID) as email_count
                FROM data_emails 
                    LEFT JOIN emails ON (data_emails.email_id = emails.ID)
                GROUP BY data_emails.email_id
            ) b ON (data_emails.email_id = b.ID) 
        GROUP BY data_emails.name 
    ) x ON (data.name = x.name AND x.version = user_items.version) 
    LEFT JOIN (
        SELECT data_ips.name,data_ips.version,
            GROUP_CONCAT( c.ip SEPARATOR ',' ) as ip,
            GROUP_CONCAT( c.ips_count SEPARATOR ',' ) as ip_counts
        FROM data_ips 
            LEFT JOIN (
            SELECT ips.ID, ips.ip, data_ips.name, data_ips.version,
                    data_ips.ip_id, COUNT(data_ips.ID) as ips_count
            FROM data_ips 
                LEFT JOIN ips ON (data_ips.ip_id = ips.ID)
                GROUP BY data_ips.ip_id
            ) c  ON (data_ips.ip_id = c.ID) 
        GROUP BY data_ips.name
    ) y ON (data.name = y.name AND y.version = user_items.version)
WHERE user_items.folder = '2'
GROUP BY user_items.name

为了完整起见,Explain 对查询的输出如下:

+----+-------------+-------------+--------+------- -------------+--------------+---------+--------- ------------------------------------+--------+------ ---------------+
| 编号 | 选择类型 | 表| 类型 | 可能的键 | 关键 | key_len | 参考 | 行 | 额外 |
+----+-------------+-------------+--------+------- -------------+--------------+---------+--------- ------------------------------------+--------+------ ---------------+
| 1 | 初级 | 用户项目 | 参考 | 文件夹 | 文件夹 | 4 | 常量 | 第1139章 使用临时的;使用文件排序 |
| 1 | 初级 | 数据 | 参考 | 版本,名称版本 | 名称版本 | 261 | gua.user_items.name,gua.user_items.version | 1 | |
| 1 | 初级 | <派生2> | 全部 | 空 | 空 | 空 | 空 | 5591 | |
| 1 | 初级 | <派生4> | 全部 | 空 | 空 | 空 | 空 | 5443 | |
| 4 | 派生 | 数据_ips | 全部 | 空 | 空 | 空 | 空 | 16301 | 使用临时的;使用文件排序 |
| 4 | 派生 | <派生5> | 全部 | 空 | 空 | 空 | 空 | 7533 | |
| 5 | 派生 | 数据_ips | 全部 | 空 | 空 | 空 | 空 | 16301 | 使用临时的;使用文件排序 |
| 5 | 派生 | IPS | eq_ref | 初级 | 初级 | 4 | gua.data_ips.ns_id | 1 | |
| 2 | 派生 | 数据电子邮件 | 全部 | 空 | 空 | 空 | 空 | 10138 | 使用临时的;使用文件排序 |
| 2 | 派生 | <派生3> | 全部 | 空 | 空 | 空 | 空 | 5061 | |
| 3 | 派生 | 数据电子邮件 | 全部 | 空 | 空 | 空 | 空 | 10138 | 使用临时的;使用文件排序 |
| 3 | 派生 | 电子邮件 | eq_ref | 初级 | 初级 | 4 | gua.data_emails.email_id | 1 | |
+----+-------------+-------------+--------+------- -------------+--------------+---------+--------- ------------------------------------+--------+------ ---------------+
4

0 回答 0