[英]How to create a database that shows a calendar of availability for a set of apartments?
語境:
最好的例子是AirBnB。 假設我有5套公寓。 每間公寓都有一個代表其可用性的日歷。 當度假者前往我的城市並使用給定的開始日期和結束日期搜索公寓時,如果我的任何公寓的日歷上顯示該時間段,我希望這些公寓顯示在搜索結果中游客。
一次一點:
顯然上面有很多。 這個問題的范圍是我應該如何為包含其可用性的公寓列表設置數據庫。 在構建數據庫之前,我花了一些時間在Excel中手動協調,以便更清楚地了解一切應該是什么樣子。 在Excel中,我發現的工作是表的列標題是:
現在的日歷就是我遇到的麻煩。 從字面上看,我的Excel中的列只是永恆的日期。 每當度假者提交請求時,我會找到每個日期單元格為空的所有公寓(例如,可用)。 然后我把度假者寄給這些公寓。 當他/她預訂時,我會回到Excel並在每個日期單元格中標記所選擇的特定公寓。
我想獲得更多意見......這是我想象PostGreSQL數據庫的正確方法嗎? 如果是這樣的話......我可以進行下面的遷移嗎?
class CreateApartments < ActiveRecord::Migration
def change
create_table :apartments do |t|
t.string :apt_name
t.integer :apt_owner
t.text :apt_description
Date.today..Date.new(2034, 12, 31)).each do |date|
t.date :date
end
t.timestamps
end
end
end
您不應該存儲可用性,而是相反(公寓在特定日期預訂)。 沒有任何更深入的分析,我會做一些簡單的事情:
owner
owner_id
owner_name
apartment
apartment_id
apartment_name
apartment_description
owner_id
customer
customer_id
customer_name
booking
booking_id
customer_id
apartment_id
booking_start
booking_end
如果可以預訂不相交的日子:
booking
booking_id
customer_id
apartment_id
booking_calendar
booking_id
booking_date
無論如何,您將能夠輕松返回可用公寓列表。
select
*
from
apartments a
where not exists
(select
1
from
bookings b
where
a.apartment_id = b.apartment_id
and (
<<required_start>> between booking_start and booking_end
or
<<required_end>> between booking_start and booking_end
)
問題出在PostgreSQL中,但我想分享我在MySQL中所做的事情及其性能結果。 我希望這適用於其他數據庫。
我創建了一個listings
和listing_availabilities
,用於存儲列表可用的可用時間范圍。 如果我創建可用性或“不可用性”並不重要,因為可用性的補充是不可用的,因此,它唯一改變的不是數據集的大小而是查詢條件。
CREATE TABLE `listings` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(64) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
CREATE TABLE `listing_availabilities` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`listing_id` int(11) NOT NULL,
`start_time` int(11) NOT NULL,
`end_time` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `start_time` (`start_time`),
KEY `end_time` (`end_time`),
KEY `listing_id` (`listing_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
有人擔心,如果您要將該列表提供一年,那么您需要實現365行數據。 假設您有100萬個列表,那么您將擁有最少3.65億行的列表可用性。 對,那是正確的。 它將具有那么多行或者具有難以過濾的大數據包的一行。 所以,唯一知道的方法就是實際測試。
我已經添加一個百萬行到listings
中的365萬行listing_availabilities
,所以一排具有可用性范圍的365天。
以下是一些實際查詢及其性能。
獲取所有列表的特定列表的可用性
> SELECT * FROM `listing_availabilities` WHERE `listing_id` = 716384;
> 365 rows in set (0.01 sec)
查看此商家信息的具體時間是否可用。 1如果可用/ 0如果不可用
> SELECT count(*) as exists FROM `listing_availabilities`
> WHERE `listing_id` = 1234
> AND 1481527584 BETWEEN `start_time` AND `end_time`
> LIMIT 1;
> 1 row in set (0.00 sec)
在第一個可用時段加入前50個列表
> SELECT *
> FROM listings AS l
> LEFT JOIN listing_availabilities a
> ON a.listing_id = l.id
> AND a.start_time = (SELECT start_time FROM listing_availabilities WHERE l.id = listing_id ORDER BY start_time ASC LIMIT 1)
> LIMIT 50;
> 50 rows in set (0.05 sec)
獲取我的特定時間戳可用的前50個列表(假設條件符合)
> SELECT *
> FROM listings AS l
> LEFT JOIN listing_availabilities a
> ON a.listing_id = l.id
> AND a.start_time =
> (SELECT start_time FROM listing_availabilities WHERE l.id = listing_id AND start_time < 1481536932 AND end_time > 1481536932 LIMIT 1)
> WHERE a.start_time IS NOT NULL
> LIMIT 50;
> 50 rows in set (0.05 sec)
獲取我的特定時間戳不可用的前50個列表(當沒有可用時間時)
> SELECT *
> FROM `listings` AS l
> LEFT JOIN `listing_availabilities` a
> ON a.listing_id = l.id
> AND a.start_time =
> (SELECT `start_time` FROM `listing_availabilities` WHERE l.id = listing_id AND `start_time` < 1481530494 AND `end_time` > 1481530494 LIMIT 1)
> WHERE a.start_time IS NOT NULL
> LIMIT 50;
> Empty set (3.00 sec)
這個花了更長的時間,並且在EXPLAIN EXTENDED的幫助下,當在子查詢中使用多列不等式時,似乎掃描整個表。 我仍然不能100%確定為什么會這樣......有人可以在這里啟發我。
替代方法是將所有可用性連接到每個列表,然后從該集合中過濾:
> SELECT l.id, a.start_time, a.end_time
> FROM listings as l
> LEFT JOIN listing_availabilities a
> ON l.id = a.listing_id
> WHERE 1481530494
> BETWEEN a.start_time
> AND a.end_time limit 50;
> Empty set (0.01 sec)
自動插入listing_availabilities 365天
> INSERT INTO `listing_availabilities`
> (listing_id, start_time, end_time)
> VALUES
> (8, 1481689555, 1481689556) ...
> Query OK, 500 rows affected (0.01 sec)
> Records: 500 Duplicates: 0 Warnings: 0
刪除舊的可用性
> DELETE FROM `listing_availabilities`
> WHERE `end_time` < 1481671237
> LIMIT 100000;
> Query OK, 100000 rows affected (10.43 sec)
這似乎需要一段時間並鎖定數據庫,所以我決定在較小的塊中這樣做:
> DELETE FROM `listing_availabilities`
> WHERE end_time < 1481671237
> LIMIT 500;
> Query OK, 500 rows affected (0.09 sec)
並執行此操作直到受影響的行變為零。
顯然,您需要一個腳本來刪除舊的可用性並在未來幾個月內添加新的可用性。
這似乎適用於我的應用程序,它應該滿足OP想要的要求。 我希望這有助於其他數據庫模式。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.