使用子选择和IF条件慢的MySQL查询

时间:2013-02-22 10:40:27

标签: mysql sql subquery

我有一点问题,想要一些建议 我想要做的是从tableA中选择所有表格中的条目,其中tableB中的条目是表格中的第一个条目,该条目在特定的日期范围内。

因此,开始日期为2013-01-01 00:00:00,结束日期为2013-01-31 23:59:59我需要做这样的事情

SELECT * FROM tblsites WHERE ID IN(SELECT 
IF(DateRequired >= '2013-01-01 00:00:00' AND DateRequired <= '2013-01-31 23:59:59', SiteID, '' ) AS SiteID FROM `tblmovements` WHERE TicketStatus IN ( 0, 1 ) GROUP BY SiteID) AND LENGTH(SiteName)>0 ORDER BY SiteName ASC

基本上它应该返回一个站点数据列表,其中该站点在所选日期范围内进行了第一次移动。 DateRequired列是我在tblmovements中的移动日期,tblmovements将SiteID存储为列。票证状态您可以忽略它只是一个标志,表示票证处于活动状态,它不应该对查询产生结果。

所以我的理论是,如果我在tblmovements中选择它们处于活动状态的所有票证,并将它们分组到siteid上,那么如果DateRequired在我的输入日期之间,那么我可以让子查询仅返回站点ID,这将允许我在站点表上做一个简单的SELECT WHERE IN()。 LENGTH子句只是因为if在else子句上返回零长度,而不是在脚本中过滤掉它我想在查询时过滤它们。

查询运行时没有任何错误但是绝对缓慢(30分钟+仍在运行)我不能为我的生活想到一个更好的方式来写它可能只是周五早上的蓝调但任何建议唤醒我的回复将非常感激 如果你能理解我上面的漫无边际,那么更好的写作方式也没关系。

修改 假表。

--
-- Table structure for table `tblmovements`
--

CREATE TABLE IF NOT EXISTS `tblmovements` (
  `ID` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `SiteID` bigint(20) unsigned NOT NULL,
  `TicketStatus` smallint(3) NOT NULL DEFAULT '1',
  `DateRequired` datetime DEFAULT NULL,
  PRIMARY KEY (`ID`),
  KEY `SiteID` (`SiteID`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=11 ;

--
-- Dumping data for table `tblmovements`
--

INSERT INTO `tblmovements` (`ID`, `SiteID`, `TicketStatus`, `DateRequired`) VALUES
(1, 1, 1, '2013-01-02 00:00:00'),
(2, 1, 1, '2013-01-02 00:00:00'),
(3, 1, 1, '2013-02-02 00:00:00'),
(4, 1, 1, '2013-02-02 00:00:00'),
(5, 1, 1, '2013-02-02 00:00:00'),
(6, 2, 1, '2012-02-02 00:00:00'),
(7, 2, 1, '2012-02-02 00:00:00'),
(8, 2, 1, '2012-01-20 00:00:00'),
(9, 2, 1, '2013-01-02 00:00:00'),
(10, 2, 1, '2013-01-02 00:00:00');

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

--
-- Table structure for table `tblsites`
--

CREATE TABLE IF NOT EXISTS `tblsites` (
  `ID` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `SiteName` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`ID`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;

--
-- Dumping data for table `tblsites`
--

INSERT INTO `tblsites` (`ID`, `SiteName`) VALUES
(1, 'Site 1'),
(2, 'Site 2');

编辑2:

好的史蒂夫建议我已将查询更改为此

SELECT tblmovements.SiteID, tblsites.SiteName FROM tblmovements
INNER JOIN tblsites
 ON tblmovements.SiteID = tblsites.ID
WHERE (tblmovements.TicketStatus = 0 OR tblmovements.TicketStatus = 1)
AND tblmovements.DateRequired>='2013-01-01 00:00:00' AND tblmovements.DateRequired<='2013-01-31 23:59:59'
GROUP BY tblmovements.SiteID

现在是半工作的。我现在遇到的问题是,这只是选择该日期范围之间是否有票证。我现在需要做的只是返回结果集,如果tblmovements中的条目是该表中该站点ID的第一次出现。 如果你回到主查询,你会注意到子选择上有一个IF。

编辑3

对,我认为我已根据Steves的输入和非星期五早晨白痴的突然闪现破解了它。

SELECT tblmovements.SiteID, tblsites.SiteName FROM tblmovements
INNER JOIN tblsites
 ON tblmovements.SiteID = tblsites.ID
WHERE (tblmovements.TicketStatus = 0 OR tblmovements.TicketStatus = 1)
AND tblmovements.DateRequired>='2013-01-01 00:00:00' AND tblmovements.DateRequired<='2013-01-31 23:59:59'
AND (SELECT COUNT(*) FROM tblmovements t3 WHERE t3.DateRequired<'2013-01-01 00:00:00' AND t3.SiteID=tblmovements.SiteID)<=0
GROUP BY tblmovements.SiteID

添加了一个额外的条件,只是在开始日期之前给我一个票据计数,所以理论上如果它返回结果集,其中我的所需日期之间有票,并且在开始日期之前没有票,那么返回的所有siteid应该是我需要的那些。

到目前为止的测试是返回我期待的数据集,这些数据集是好的,并且明显更快查询执行时间0.5711秒奖励。

1 个答案:

答案 0 :(得分:2)

连接而不是子查询和IN列表怎么样?

SELECT t1.SiteID, t2.* FROM tblmovements t1
WHERE (TicketStatus = 0 OR TicketStatus = 1)
AND t1.DateRequired BETWEEN '2013-01-01 00:00:00' AND '2013-01-31 23:59:59'
GROUP BY t1.SiteID)
INNER JOIN tblsites t2
 ON t1.SiteID = t2.ID

编辑1 - 回复评论:

我在这里所做的是将您的IF评估转移到WHERE条件声明中。

原始查询将选择大范围的行,然后评估它们以编写其SiteID或''。为什么选择一个大的范围,当你可以用where条件缩小范围?