【问题标题】:SQL Query to find Records in Table1 that has one relation to Table2 but should have other relation to the same Table2SQL 查询以查找 Table1 中与 Table2 有一个关系但应该与同一个 Table2 有其他关系的记录
【发布时间】:2021-12-12 19:09:34
【问题描述】:

我们有两个表用于“物理位置”和“商店”。一个物理位置可以有多个商店。每个商店都可以是活动的或非活动的。

我们如何编写查询来查找那些没有与之关联的活动商店的“物理位置”?

物理位置

LocationId    LocationAddress
100           Address1
101           Address2
102           Address3

商店

StoreID    LocationId        Status
100A           100           Active
100B           100           Inactive
101C           101           Inactive
102D           101           Inactive

我尝试过类似下面的方法。

Select * from PhysicalLocation where LocationId IN 
 (Select LocationId from Store where Status <> 'Active')

我的预期结果是

LocationId    LocationAddress
    101           Address2

因为这个位置只有不活跃的商店

【问题讨论】:

    标签: sql sql-server


    【解决方案1】:

    这是最简单的解决方案:

    select LocationId, LocationAddress from PhysicalLocation
    where LocationId in 
    (
        select Distinct (LocationId) from Store 
        where LocationId not in (select LocationId from Store where Status = 'Active')
    )
    

    【讨论】:

    • 为什么DISTINCT,它在半连接中的作用是什么?而且它不是一个函数,所以为什么(LocationId)
    • Distinct 将减少我们想要比较的列表。如果没有 Distinct,我们将两次获得相同的 LocationId
    • 您不会,因为您正在使用该 IN 子句进行半联接,它所说的只是:子查询中是否有任何匹配的 LocationId,您不需要到DISTINCT子查询
    • 这不是因为我们在 'IN' 子句中的数据查询,而是因为我们在 Store 表中拥有的数据
    • 再次where LocationId in (select Distinct LocationId from Storewhere LocationId in (select LocationId from Store同一件事 因为它所说的只是:子查询中是否有 any 匹配项,它使没有区别多少匹配所以你不需要“减少它”,它不是一个连接,它是一个半连接,另见stackoverflow.com/questions/42249690/…
    【解决方案2】:

    您需要选择哪些位置有活跃的商店,并在 WHERE 子句中过滤掉这些位置。

    SELECT S.LocationId
        ,LocationAddress
    FROM #Store S
        INNER JOIN #PhysicalLocation PL
            ON S.StoreID = PL.LocationId
    WHERE S.LocationId NOT IN (SELECT LocationId FROM #Store WHERE [status] = 'active')
    

    输出:

    这与您指定的所需输出略有不同,但我怀疑您没有考虑额外的地址。仍然只有 1 个地点没有活跃的商店。

    【讨论】:

      【解决方案3】:

      您可以使用以下内容,它在 count 中使用带有 case 表达式的 have 子句来过滤掉具有活动商店的位置。该联接用于检索其他位置详细信息。

      SELECT
          P.*
      FROM
         PhysicalLocation P
      INNER JOIN (
         SELECT LocationId
         FROM Store
         GROUP BY LocationId
         HAVING COUNT(CASE WHEN Status='Active' THEN 1 END) =0
      ) SA ON p.LocationId=SA.LocationId
      
      LocationId LocationAddress
      101 Address2

      如果您对所有没有活动商店的位置感兴趣,无论是否已创建商店条目,那么您可以尝试以下使用左连接和 where 子句来应用过滤器而不是如上所述的内连接。

      SELECT
          P.*
      FROM
         PhysicalLocation P
      LEFT JOIN (
         SELECT 
             LocationId,
             COUNT(CASE WHEN Status='Active' THEN 1 END) as no_active_locations
         FROM Store
         GROUP BY LocationId
      ) SA ON p.LocationId=SA.LocationId
      WHERE SA.no_active_locations=0 OR SA.no_active_locations IS NULL
      
      LocationId LocationAddress
      101 Address2
      102 Address3

      View working demo db fiddle

      【讨论】:

      • 谢谢@ggordon,这与我想要的很接近。我今天学到了一些东西!
      【解决方案4】:

      查询会是这样的。

      SELECT * FROM PhysicalLocation AS Ph INNER JOIN Store AS St ON Ph.LocationId = St.LocationId WHERE St.Status = 'Inactive';
      

      使用 Join 在两个表之间建立关系。这将为您提供您正在寻找的结果值。

      输出:

      LocationId    LocationAddress    StoreID        LocationId    Status
      100           Address1           100B           100           Inactive
      101           Address2           101C           101           Inactive
      101           Address2           102D           101           Inactive
      

      【讨论】:

        【解决方案5】:

        使用Joins

        SELECT * FROM PhysicalLocation 
        INNER JOIN Store ON Store.LocationId = PhysicalLocation.LocationId
        WHERE Store.Status = 'Inactive'
        

        我使用INNER JOIN 并假设没有商店位置,例如102 Address3,与不活动商店的位置一样好。

        如果假设不正确,并且您希望所有位置都处于非活动状态以及没有商店的位置,那么请选择 LEFT OUTER JOIN

        【讨论】:

        • 感谢您的回复。我考虑过这一点。但这将提供物理位置,其中一个商店也可能是活跃的。可能不在此结果集中。
        • @sqloverflow - 所以你需要一个查询,只有当其中的所有商店都处于非活动状态时才应该给出一个位置?
        • 是的,没错。
        • 检查@ggordon 的答案。它更接近你想要的。
        【解决方案6】:
        select l.LocationId,l.LocationAddress
        from locations as l
        where not exists
        (
          select 1 from stores as s
            where l.LocationId=s.LocationId
             and s.Status='Active'
        )
        

        如果它适合你,请你试试这个。 顺便说一句,位置 102 也不包含活动商店

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-07-18
          • 1970-01-01
          • 2015-01-26
          • 2016-01-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多