在我的大多数查询中,我的表现非常糟糕。我已经阅读了很多关于stackoverflow的内容,但仍然有一些问题,也许有人可以提供帮助或给我任何提示?
基本上,我正在预订网站上工作,其中包括以下表格:
物体
+----+---------+--------+---------+------------+-------------+----------+----------+-------------+------------+-------+-------------+------+-----------+----------+-----+-----+
| id | user_id | status | type_id | privacy_id | location_id | address1 | address2 | object_name | short_name | price | currency_id | size | no_people | min_stay | lat | lng |
+----+---------+--------+---------+------------+-------------+----------+----------+-------------+------------+-------+-------------+------+-----------+----------+-----+-----+
在MySQL中的OR:
CREATE TABLE IF NOT EXISTS `objects` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 'object_id',
`user_id` int(11) unsigned DEFAULT NULL,
`status` tinyint(2) unsigned NOT NULL,
`type_id` tinyint(3) unsigned DEFAULT NULL COMMENT 'type of object, from object_type id',
`privacy_id` tinyint(11) unsigned NOT NULL COMMENT 'id from privacy',
`location_id` int(11) unsigned DEFAULT NULL,
`address1` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
`address2` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
`object_name` varchar(35) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'given name by user',
`short_name` varchar(12) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT 'short name, selected by user',
`price` int(6) unsigned DEFAULT NULL,
`currency_id` tinyint(3) unsigned DEFAULT NULL,
`size` int(4) unsigned DEFAULT NULL COMMENT 'size rounded and in m2',
`no_people` tinyint(3) unsigned DEFAULT NULL COMMENT 'number of people',
`min_stay` tinyint(2) unsigned DEFAULT NULL COMMENT '0=no min stay;else # nights',
`lat` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL,
`lng` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1451046 ;
的保留
+----+------------+-----------+-----------+---------+--------+
| id | by_user_id | object_id | from_date | to_date | status |
+----+------------+-----------+-----------+---------+--------+
在MySQL中的OR:
CREATE TABLE IF NOT EXISTS `reservations` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`by_user_id` int(11) NOT NULL COMMENT 'user_id of guest',
`object_id` int(11) NOT NULL COMMENT 'id of object',
`from_date` date NOT NULL COMMENT 'start date of reservation',
`to_date` date NOT NULL COMMENT 'end date of reservation',
`status` int(1) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=890729 ;
有几个问题:
1 - 我没有设置任何额外的钥匙(主要除外) - 我应该在哪里设置以及我应该设置哪个钥匙?
2 - 我读过关于MyISAM和InnoDB的内容,我得出的结论是MyISAM在只读时更快,而InnoDB是为更频繁更新或插入的表而设计的。因此,目前对象使用MyISAM和预订InnoDB。这是一个好主意混合?还有更好的选择吗?
3 - 我需要查询某段时间内可用的对象(from_date和end_date之间)。我已经在stackoverflow上阅读了这篇文章({等等}:MySQL select rows where date not between date
但是,当我使用建议的解决方案时,查询会在返回任何结果之前超时(因此它非常慢):
SELECT DISTINCT o.id FROM objects o LEFT JOIN reservations r ON(r.object_id=o.id) WHERE
COALESCE('2012-04-05' NOT BETWEEN r.from_date AND r.to_date, TRUE)
AND COALESCE('2012-04-08' NOT BETWEEN r.from_date AND r.to_date, TRUE)
AND o.location_id=201
LIMIT 20
我做错了什么?进行此类查询的最佳解决方案是什么?其他网站如何做到这一点?我的数据库结构不是最好的,还是仅查询?
我会有更多问题,但我真的很感激能得到任何帮助!非常感谢您提前提出任何暗示或建议!
答案 0 :(得分:6)
看来你正在寻找任何"对象"根据提供的自/至日期没有预订冲突。做coalesce()总是包括那些在预订中找不到的东西是一个不错的选择,但是,作为一个左连接,我会尝试离开加入IS找到日期的地方,并忽略任何对象FOUND。像
这样的东西SELECT DISTINCT
o.id
FROM
objects o
LEFT JOIN reservations r
ON o.id = r.object_id
AND ( r.from_date between '2012-04-05' and '2012-04-08'
OR r.to_date between '2012-04-05' and '2012-04-08' )
WHERE
o.location_id = 201
AND r.object_id IS NULL
LIMIT 20
我将通过(object_id,from_date)和另一个(object_id,to_date)确保预留表上的索引。通过明确使用范围之间的from_date(以及迄今为止),您将专门寻找占用此时间段的预留。如果找到它们,则不允许,因此WHERE子句寻找" r.object_id IS NULL" (即:在您提供的日期范围内没有发现任何冲突)
从我之前的答案扩展,并且通过在(id,from date)和(id,to date)上有两个不同的索引,你可以通过分别加入每个索引的预留并在BOTH预留集中期望NULL来获得更好的性能...
SELECT DISTINCT
o.id
FROM
objects o
LEFT JOIN reservations r
ON o.id = r.object_id
AND r.from_date between '2012-04-05' and '2012-04-08'
LEFT JOIN reservations r2
ON o.id = r2.object_id
AND r2.to_date between '2012-04-05' and '2012-04-08'
WHERE
o.location_id = 201
AND r.object_id IS NULL
AND r2.object_id IS NULL
LIMIT 20
答案 1 :(得分:2)
我不会混用InnoDB和MyISAM表,但我会将所有表定义为InnoDB(用于支持键)。通常所有带有_id后缀的列都应该是外键,引用适当的表(object_id =>对象等)。
您不必在外键上定义索引,因为它是自动定义的(自MySQL 4.1.2起),但您可以在reservations.from_date和reservations.to_date列上定义其他索引,以便更快地进行比较。
答案 2 :(得分:1)
我知道这已经有一年了,但是如果您已经尝试过上述解决方案,那么逻辑就不完整了。它错过了在查询开始之前开始并在查询结束之后结束的预留。此外between
也无法应对同时开始和结束的预订。
这对我来说效果更好:
SELECT venues.id
FROM venues LEFT JOIN reservations r
ON venues.id = r.venue_id && (r.date_end >':start' and r.date_start <':end')
WHERE r.venue_id IS NULL
ORDER BY venues.id