如何创建两个JOIN表,以便我可以比较其中的属性?

时间:2019-04-29 15:53:00

标签: sql

我参加了一个数据库课程,其中包含AirBnB的列表,并且需要能够在我们根据数据制作的关系模型中执行一些SQL查询,但是我尤其要特别努力:

我有两个我们感兴趣的表,BillingAmenities。第一个具有列表的idprice,第二个具有idwifi(为简单起见,如果有Wifi,则等于1,0除此以外)。两者都有我们在这里不太在乎的其他属性。

所以查询的是,“有和没有Wifi的商家信息的平均价格有何不同?”

我的想法是建立JOIN表,其中一个包含具有wifi列表的列表,另一个不包含该列表,然后轻松比较它们:

SELECT avg(B.price - A.price) as averagePrice
FROM (
    SELECT Billing.price, Billing.id
    FROM Billing
    INNER JOIN Amenities
    ON Billing.id = Amenities.id
    WHERE Amenities.wifi = 0
) A, ( 
    SELECT Billing.price, Billing.id
    FROM Billing
    INNER JOIN Amenities
    ON Billing.id = Amenities.id
    WHERE Amenities.wifi = 1) B
WHERE A.id = B.id;

显然这是行不通的...我很确定有一个更简单的解决方案,我想念什么?

(顺便说一下,有没有一种方法可以计算价格差之间的绝对值?)

我希望我足够清楚,谢谢您的时间!

编辑:如评论中所述,忘记了这一点,但是两个表都以id作为主键,因此每个列表只有一行。

2 个答案:

答案 0 :(得分:2)

只需使用条件聚合:

!=

如果您想要差异而不是特定值,可以使用allEqual

答案 1 :(得分:1)

假设我们正在处理以下数据(下面将说明您的数据模型存在的问题):

Billing
+------------+---------+
| listing_id |  price  |
+------------+---------+
|          1 | 1500.00 |
|          2 | 1700.00 |
|          3 | 1800.00 |
|          4 | 1900.00 |
+------------+---------+

Amenities
+------------+------+
| listing_id | wifi |
+------------+------+
|          1 |    1 |
|          2 |    1 |
|          3 |    0 |
+------------+------+

请注意,我已将“ id”更改为“ listing_id”以使其清晰可见(无论如何,使用“ id”作为属性名称都是有问题的)。另外,请注意,“便利设施”表中没有一个列表。根据您的数据,可能会或可能不会担心(再次,请参阅底部的有关数据模型的讨论)。

基于此数据,您的平均值应为:

  • 带有wifi的列表平均$ 1600(列表1和2)
  • 不带wifi的列表(只有3个)平均1800)。

所以差额是200美元。

要在SQL中获得此结果,首先获取每次使用便利设施的平均费用(是否提供wifi)可能会有所帮助。这可以通过以下查询获得:

SELECT 
    Amenities.wifi AS has_wifi, 
    AVG(Billing.price) AS avg_cost
FROM Billing
    INNER JOIN Amenities ON
        Amenities.listing_id = Billing.listing_id
GROUP BY Amenities.wifi 

为您提供以下结果:

+----------+-----------------------+
| has_wifi |       avg_cost        |
+----------+-----------------------+
|        0 | 1800.0000000000000000 |
|        1 | 1600.0000000000000000 |
+----------+-----------------------+

到目前为止,一切都很好。因此,现在我们需要计算这两行之间的差。有许多不同的方法可以执行此操作,但是一种方法是使用CASE表达式将其中一个值设为负,然后简单地将结果的SUM用作结果(请注意,使用CTE,但您也可以使用子查询):

WITH 
    avg_by_wifi(has_wifi, avg_cost) AS
    (
        SELECT Amenities.wifi, AVG(Billing.price)
        FROM Billing
        INNER JOIN Amenities ON
            Amenities.listing_id = Billing.listing_id
        GROUP BY Amenities.wifi 
    )
SELECT 
    ABS(SUM
    (
        CASE
            WHEN has_wifi = 1 THEN avg_cost 
            ELSE -1 * avg_cost
        END
    ))
FROM avg_by_wifi

这为我们提供了200的期望值。


现在有关您的数据模型:

  • 如果您的BillingAmenities表的每个列表仅具有1行,则将它们合并为1个表是有意义的。例如:Listings(listing_id, price, wifi)
  • 但是,这仍然是有问题的,因为您可能还有许多其他要建模的便利设施(游泳池,桑拿浴室等),因此,您可能想使用清单来模拟列表和便利设施之间的多对多关系。中间表:
Listings(listing_id, price)
Amenities(amenity_id, amenity_name)
ListingsAmenities(listing_id, amenity_id)

这样,您可以为给定列表列出多个便利设施,而不必添加其他列。存储有关便利设施的其他信息也变得很容易:什么是wifi密码?游泳池有多深?等

当然,使用此模型会使您的原始查询(按wifi列出的平均房源费用有所不同)有些棘手,但绝对可以。