簡體   English   中英

如何創建一個顯示一組公寓可用性日歷的數據庫?

[英]How to create a database that shows a calendar of availability for a set of apartments?

語境:

最好的例子是AirBnB。 假設我有5套公寓。 每間公寓都有一個代表其可用性的日歷。 當度假者前往我的城市並使用給定的開始日期和結束日期搜索公寓時,如果我的任何公寓的日歷上顯示該時間段,我希望這些公寓顯示在搜索結果中游客。

一次一點:

顯然上面有很多。 這個問題的范圍是我應該如何為包含其可用性的公寓列表設置數據庫。 在構建數據庫之前,我花了一些時間在Excel中手動協調,以便更清楚地了解一切應該是什么樣子。 在Excel中,我發現的工作是表的列標題是:

  • apartment_name
  • owner_id
  • apartment_description
  • 日歷

現在的日歷就是我遇到的麻煩。 從字面上看,我的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支持范圍類型和相關的重疊測試

您可以索引范圍,甚至使用約束來防止公寓在同一天被預訂兩次。

使用范圍,您可以根據需要存儲預訂或可用性。

問題出在PostgreSQL中,但我想分享我在MySQL中所做的事情及其性能結果。 我希望這適用於其他數據庫。

途徑

我創建了一個listingslisting_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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM