避免UNION在MySQL中使用两个几乎相同的表

时间:2012-07-06 13:56:50

标签: mysql sql optimization

我不擅长MySQL,我会根据其类型和is_auto字段为计算用户发送的消息编写查询。

消息可以是“小文本消息”或“简报”类型。我创建了两个实体,它们之间有一些不同的字段。重要的一个是 messages_count,表newsletter中没有,并且在查询中使用

CREATE TABLE IF NOT EXISTS `small_text_message` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `messages_count` int(11) NOT NULL,
  `username` varchar(255) NOT NULL,
  `method` varchar(255) NOT NULL,
  `content` longtext,
  `sent_at` datetime DEFAULT NULL,
  `status` varchar(255) NOT NULL,
  `recipients_count` int(11) NOT NULL,
  `customers_count` int(11) NOT NULL,
  `sheduled_at` datetime DEFAULT NULL,
  `sheduled_for` datetime DEFAULT NULL,
  `is_auto` tinyint(1) NOT NULL,
  `user_id` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

CREATE TABLE `newsletter` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `subject` varchar(78) DEFAULT NULL,
  `content` longtext,
  `sent_at` datetime DEFAULT NULL,
  `status` varchar(255) NOT NULL,
  `recipients_count` int(11) NOT NULL,
  `customers_count` int(11) NOT NULL,
  `sheduled_at` datetime DEFAULT NULL,
  `sheduled_for` datetime DEFAULT NULL,
  `is_auto` tinyint(1) NOT NULL,
  `user_id` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

我最终得到了UNION查询。 是否可以缩短或优化此查询,因为messages_count的唯一区别是newsletter应始终为1?

SELECT
CONCAT('sms_', IF(is_auto = 0, 'user' , 'auto')) AS subtype,
SUM(messages_count * (customers_count + recipients_count)) AS count
FROM small_text_message WHERE status <> 'pending' AND user_id = 1
GROUP BY is_auto
UNION
SELECT
CONCAT('newsletter_', IF(is_auto = 0, 'user' , 'auto')) AS subtype,
SUM(customers_count + recipients_count) AS count
FROM newsletter WHERE status <> 'pending' AND user_id = 1
GROUP BY is_auto

3 个答案:

答案 0 :(得分:5)

我没有看到任何简单的方法来避免UNION(或UNION ALL)操作,这将返回指定的结果集。

我建议您使用UNION ALL运算符代替UNION运算符。然后执行计划将不包括消除重复行的步骤。 (您已经对每个查询执行了GROUP BY操作,并且这两个查询无法生成相同的行。)

否则,您的查询在编写时看起来很好。

(考虑这个问题总是一件好事,可能有更好的方法吗?要获得你要求的结果集,从你的架构中,你的查询看起来和它一样好#&# 39;准备好了。)

答案 1 :(得分:2)

如果您正在寻找更一般的数据库建议,我建议重组表格,将公共元素分解为一个表,可能称为outbound_communication或其他东西,包含所有公共字段,然后可能有“子表“用于承载该类型唯一的字段的特定类型。它确实意味着需要一个简单的JOIN来选择你想要的所有字段,但是再次,它被规范化并且实际上使这样的情况更容易(一个表包含所有感兴趣的实体)。此外,您可以选择将JOIN只写一次作为“视图”,然后您现有的代码甚至不需要更改以查看这两个表,就好像它们从未更改过一样。

CREATE TABLE IF NOT EXISTS `outbound_communicaton` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `content` longtext,
  `sent_at` datetime DEFAULT NULL,
  `status` varchar(255) NOT NULL,
  `recipients_count` int(11) NOT NULL,
  `customers_count` int(11) NOT NULL,
  `sheduled_at` datetime DEFAULT NULL,
  `sheduled_for` datetime DEFAULT NULL,
  `is_auto` tinyint(1) NOT NULL,
  `user_id` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

CREATE TABLE `small_text_message` (
  `oubound_communication_id` int(11) NOT NULL,
  `messages_count` int(11) NOT NULL,
  `username` varchar(255) NOT NULL,
  `method` varchar(255) NOT NULL,
  PRIMARY KEY (`outbound_communication_id`),
  FOREIGN KEY (outbound_communication_id) 
    REFERENCES outbound_communicaton(id)
) ENGINE=InnoDB;

CREATE TABLE `newsletter` (
  `oubound_communication_id` int(11) NOT NULL,
  `subject` varchar(78) DEFAULT NULL,
  PRIMARY KEY (`outbound_communication_id`),
  FOREIGN KEY (outbound_communication_id) 
    REFERENCES outbound_communicaton(id)
) ENGINE=InnoDB;

然后选择文本消息是这样的:

SELECT * 
FROM outbound_communication AS parent
  JOIN small_text_message 
    ON parent.id = small_text_message.outbound_communication_id
WHERE parent.id = 1234;

答案 2 :(得分:1)

查询的本质本质上是来自小文本消息和新闻稿表的数据的并集,因此UNION查询是唯一真实的公式。例如,两个表之间没有相关性的连接。

所以,我认为你的查询与你的问题非常相符。

你为什么担心UNION?