【问题标题】:SQL : How to SELECT a record where certain attributes are nullSQL:如何选择某些属性为空的记录
【发布时间】:2022-01-19 10:58:28
【问题描述】:

我正在使用 SQL Server 2014 数据库和 SQL Server Management Studio 来创建和运行查询。

表格是:

Persons

| ID | personName |
+----+------------+
| 1  |    Hamish  |
| 2  |    Morag   |
| 3  |    Ewan    |

Cars

| ID | CarName |
+----+---------+
| 1  |  Humber |
| 2  |  Austen |
| 3  |  Morris |

Gadgets

| ID | GadgetName |
+----+------------+
| 1  |  Cassette  |
| 2  |     CD     |
| 3  |   Radio    |

CarToPersonMap

| ID | CarID | PersonID |
+----+-------+----------+
| 1  |   1   |    1     |
| 2  |   2   |    1     |
| 3  |   3   |    2     |

CarToGadgetMap

| ID | CarID | GadgetID |
+----+-------+----------+
| 1  |   2   |    2     |

映射表具有适当的外键。

我想找到拥有 Cars 但所有 Cars 都没有小工具的人。所以在上面的例子中,我想找到有一辆没有小工具的汽车的莫拉格。 Hamish 有 2 辆汽车,但 1 辆汽车有一个小工具,所以我不希望 ResultSet 包含 Hamish。

【问题讨论】:

  • 样本数据很好,但您也应该指定预期结果。
  • 看起来你真正想要的是NOT EXISTS

标签: sql sql-server


【解决方案1】:

您可以在EXISTS 中进行条件聚合

SELECT personName
FROM Persons p 
WHERE EXISTS (SELECT 1
    FROM CarToPersonMap cpm
    LEFT JOIN CarToGadgetMap cgm ON cpm.carId = cgm.carID
    WHERE cpm.personId = p.ID
    HAVING COUNT(cgm.carID) = 0
);

这句话是:

  • 获取所有在子查询中有行的Persons
  • 那个子查询是他们所有的CarToPersonMap,左连接了相关的CarToGadgetMap
  • 不得有与CarToGadgetMap 值匹配的行(因为COUNT 只计算非空值)。

db<>fiddle

【讨论】:

    【解决方案2】:

    您也可以使用EXISTS 来获得预期的输出。查询将类似于,

    SELECT DISTINCT personName
    FROM Persons p 
    WHERE 
        EXISTS ( SELECT 1 FROM CarToPersonMap WHERE  personId = p.ID )
        AND NOT EXISTS ( SELECT 1 
                FROM CarToGadgetMap cgm 
                JOIN CarToPersonMap cpm ON cpm.personId = p.ID AND cpm.carId = cgm.carId)
    

    here 是小提琴

    【讨论】:

      【解决方案3】:

      我会使用几个 CTE:

      With CarGadgetCount AS (
               SELECT c.ID as CarID, 
                      SUM(CASE cg.ID IS NOT NULL THEN 1 ELSE 0 END) AS GadgetCount
               FROM Car c 
                    LEFT JOIN CarToGadgetMap cg
                              ON cg.CarID = c.ID
               GROUP BY c.ID
          ),
          PersonCarCount AS (
               SELECT p.ID as PersonID, 
                      SUM(CASE cp.ID IS NOT NULL THEN 1 ELSE 0 END) AS CarCount
               FROM Person p 
                    LEFT JOIN CarToPersonMap cp
                              ON cp.PersonID = p.ID
          )
          PersonGadgetCount AS (
               SELECT p.ID AS PersonID,
                      SUM(CASE WHEN cg.GadgetCount IS NULL THEN 0 ELSE cg.GadgetCount END) as GadgetCount
               FROM Person p
                    LEFT JOIN CarToPersonMap cp
                              ON cp.PersonID = p.ID
                    LEFT JOIN CarGadgetCount cg 
                               ON cg.CarID = cp.CarID
               GROUP BY p.ID
          )
      SELECT p.personName 
      FROM Person p
           INNER JOIN PersonGadgetCount pg
                      ON pg.PersonID = p.ID
           INNER JOIN PersonCarCount pc 
                      ON pc.PersonID = p.ID
      WHERE pc.CarCount > 0
            AND pg.GadgetCount = 0;
      

      这样,如果你想真正看到谁有车,谁有小工具,那么你可以在select中切换WHERE子句和指定列

      【讨论】:

        【解决方案4】:

        将 CarToGadgetMap 左连接到 CarToPersonMap。

        然后按人分组。

        这些人将拥有超过 0 辆汽车,但拥有 0 个小工具。

        select PersonName
        from Persons as Person
        left join CarToPersonMap as CarPerson
          on CarPerson.PersonId = Person.ID
        left join CarToGadgetMap as CarGadget
          on CarGadget.carId = CarPerson.CarId
        group by Person.ID, PersonName
        having count(CarPerson.CarID) > 0    -- has car(s)
           and count(CarGadget.GadgetID) = 0 -- no gadgets
        
        PersonName
        Morag

        dbfiddle here

        上的演示

        【讨论】:

          猜你喜欢
          • 2019-08-08
          • 1970-01-01
          • 2017-05-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-11-08
          • 2014-09-21
          • 1970-01-01
          相关资源
          最近更新 更多