在数据库的列中存储多对多关系对象

时间:2013-06-09 04:25:48

标签: sql database database-design

我有一个名为listing的对象,并且列表可以有许多便利设施(将其与post,tags model进行比较)。这是一种有/没有关系的。 所以我有一个amenities表来存储系统中可以有的设施,有listings表存储列表,listing_amenities存储设施。

问题是根据设施搜索列表

我可以根据列表的任何参数(租金,地点等)搜索列表,但不能搜索设施,因为这需要2个表加入。我真的没有能够提出一个有效的解决方案来做到这一点。 所以,我想对如何在此基础上进行搜索提供一些帮助。

我能想到的一个解决方案是注入一个额外的字段,其中包含一系列设施

 SELECT * 
 FROM(SELECT listings.*, GROUP_CONCAT(CONCAT("(",Concat(amenity_id,")"))) as amenities 
      FROM `listings`,`listing_amenities`  
      WHERE listings.id=listing_amenities.listing_id 
      GROUP BY listings.id) as rs  
 WHERE amenities like "%(1)%"

即连接并添加包含在()中的设施ID并搜索它们

让我思考:设施仅用于列表和搜索的上下文中,所以为什么不以类似的格式存储它们(1),(2)以在列表表格的单独列中指示可用的设施并保存在一张桌子和额外的查询成本? 这种方法有哪些缺点,如果有的话?

为了更容易理解表格

 Listing Table
 --------
 id title description rent

 Amenities
 -------------
 id name description

 Listing_Amenities
 ------------------
 id listing_id amenity_id

我想我可以删除第三张表

并修改列表

 Listing Table
 --------
 id title description rent amenties(the amenities available in the appartment wrapped in bracket)

喜欢

 1 "House in Downtown Discworld" "Nice house, running water, teleporter" 2000 "(1)(5)(7)"

这种允许我在不需要加入的情况下按设施搜索。我可以运行相同的查询,我选择租金

并使用where子句进行搜索: where amenities like "%(1)%" AND amenities like "%(2)%"

我在这种方法中看到的唯一问题是删除异常,即如果我从系统中删除舒适性,它将不会自动从列表中删除舒适性

但我认为这是一个不太可能发生的事情,因为设施由管理员决定,并且在网站发布之前几乎被冻结,其次,虽然我们可能会添加新的amenties,但我们不太可能删除任何设施。

还有其他缺点吗?能否请您强调具体方案。我认为这比连接更有效。坦率地说,更容易缠头。我错了

3 个答案:

答案 0 :(得分:4)

无法从你的帖子中掌握桌面模型......但总的来说,我认为你应该使用这样的多对多关系。一个单独的表,只是为了保持关系。

ListingsAmenities
----------
ListingID
AmenitiesID    

设施表应该只保留每个设施一次。然后你不需要连接。

有了这个,您应该能够根据设施ID搜索列表..

当您将关系存储在表格的列中时,每个设施只能存储一个列表......或者您应该开始连接...相反,如果您这样存储,相同的列表可以包含许多具有各种设施的行,这使查询变得容易。

喜欢这个。

ListingAmenitiesTable:

ListingID   AmenityID
---------   ----------
L1      A1
L1      A2
L2      A2
L2      A3      

答案 1 :(得分:1)

在事务性(与报告相对)系统中,预先优化性能通常是个坏主意。基于第三范式(3NF)构建模型,然后测试系统看它是否有不可接受的表现。如果是,请仔细检查索引和查询计划,看看是否有优化性能的方法。作为最后的手段,对数据进行非规范化。

您提出的0NF设计灵活性较低且功能较弱,并且几乎肯定会比规范化设计的更差假设连接效率低是错误的。 RDBMS专门用于加入连接。

您可以使用WHERE EXISTS语法轻松地在查询中包含所需的便利设施。

SELECT L.*
FROM listings L
WHERE L.rent < 1000
  AND L.location = 'desirable-location-x'
  AND EXISTS (SELECT A.id 
              FROM amenities A
                inner join listing_amenities LA
                  on A.id = LA.amenities_id
              WHERE A.name like '%walk-in%'
                AND LA.listing_id = L.id)

请注意,使用EXISTS的相关子查询可让您找到所需内容而无需违反任何常规表单。如果交叉表和listingsamenities表上有适当的索引,那么性能会很好。

答案 2 :(得分:1)

传统的数据库设计建议您描述的模型,将“多对多”关系提取到一个单独的表中。

有很多理由这样做 - 不仅仅是“删除”异常,还有性能 - 解析varchar列以提取模式“(*)”明显慢于整数之间的连接。想象一下,您需要查询所有列表,其中包括“Ankh Morpork市中心的景色”和“步行到Unseen大学”,但没有“直接访问傻瓜公会”。