帮助解决这个问题

dub*_*ech 6 sql-server-2008 sql-server count

我有一张人桌。我还有一个汽车桌和书桌。车表和书表都有外键回人。

我正在尝试计算每个人拥有的书籍和汽车的数量。我试过:

SELECT 
    Person.Name, 
    COUNT(BooK.bookid) books, 
    COUNT(Car.CarId) cars 
FROM Person, Book, Car 
WHERE Person.ID = Book.PersonID = Car.PersonID 
Run Code Online (Sandbox Code Playgroud)

但我得到:

Incorrect syntax near '='
Run Code Online (Sandbox Code Playgroud)

ype*_*eᵀᴹ 16

几点

  1. 正如@swasheck 提到的,你不能像WHERE a = b = cSQL那样有条件,它是无效的(与其他语言不同)。你需要做到
    WHERE a = b AND b = c

  2. WHERE自 SQL-92 标准采用JOIN语法 ( a JOIN b ON <condition>) 25 年以来,使用隐式连接 with不再是一种好习惯,它有几个优点,应该是首选。一个原因是有几种类型的连接可用(除了那些只使用INNER连接的查询之外的所有查询都很难使用WHERE语法编写):

    • INNER JOIN 或者 这就是最常见的连接类型,当两个连接表中的行符合 条件时将它们组合起来。JOIN
      ON
    • LEFT OUTER JOIN 或者也 很常见:获取表的所有不匹配行的所有组合。LEFT JOIN
      INNER JOIN
    • RIGHT OUTER JOIN或者只是 (不太常见)join的相反:获取表的所有不匹配行的所有组合。 RIGHT JOIN
      LEFTINNER JOIN
    • FULL OUTER JOIN 或者只是 这就是和加入,结合起来。 FULL JOIN
      LEFTRIGHT
    • CROSS JOIN
    • NATURAL JOIN 和变体(SQL-Server 不支持)

    Web 上有许多关于联接的参考资料和教程。您可以从MSDN 在线文档开始。

  3. 您还需要研究COUNT()工作原理:

    • COUNT(*) 计算行数(一组)。

    • COUNT(column/expression)计算columnorexpression不为空的(组的)行数。如果column不能NULL,这与COUNT(*)

    • COUNT(DISTINCT column/expression)计算columnor的不同值的数量expression(在一个组内)。

以下是编写查询的几种方法:

选项 1 - 内联子查询:

SELECT  
    Person.Name,  

    ( SELECT COUNT(*) 
      FROM Book
      WHERE PersonID = Person.ID 
    ) AS BookCount,

    ( SELECT COUNT(*) 
      FROM Car
      WHERE PersonID = Person.ID 
    ) AS CarCount 
FROM 
    Person ;
Run Code Online (Sandbox Code Playgroud)

选项 2 - 两个LEFT加入,然后GROUP BY和使用COUNT(DISTINCT)

这与您的方法非常相似,但与 WHERE转换为显式连接。

GROUP BY p.ID, p.Name加入,也因此每人查询可以组行。

我们必须COUNT(DISTINCT)在这个版本中使用 ,因为两个连接可能会为每个人生成多行。(如果一个人有 2 辆汽车和 500 本书,则将产生 1000 行,然后通过分组折叠为 1。您可以尝试使用COUNT(*)那里查看产生的(错误)结果。)

SELECT  
    p.Name,  
    COUNT(DISTINCT b.BookID)  AS BookCount,
    COUNT(DISTINCT c.CarID)  AS CarCount 
FROM 
    Person AS p
  LEFT JOIN
    Book AS b 
        ON b.PersonID = p.ID 
  LEFT JOIN
    Car AS c 
        ON c.PersonID = p.ID
GROUP BY
    p.ID, p.Name ;
Run Code Online (Sandbox Code Playgroud)

选项 3(我的偏好) - 两个LEFT连接到(派生)GROUP BY子查询:

SELECT  
    p.Name,  
    COALESCE(BookCount, 0) AS BookCount,   --- using COALESCE() so the NULLs produced
    COALESCE(CarCount, 0)  AS CarCount     --- by the (LEFT) outer joins for persons
                                           --- that have no car or no book (shame!)
                                           --- are turned into 0
FROM 
    Person AS p
  LEFT JOIN
    ( SELECT PersonID
           , COUNT(*) AS BookCount,
      FROM Book 
      GROUP BY PersonID
    ) AS b
        ON b.PersonID = p.ID 
  LEFT JOIN
    ( SELECT PersonID
           , COUNT(*) AS CarCount,
      FROM Car 
      GROUP BY PersonID
    ) AS c
        ON c.PersonID = p.ID ;
Run Code Online (Sandbox Code Playgroud)

选项 4 - 在 SQL Server 中,还有使用OUTER APPLYto(派生)GROUP BY子查询的选项。这类似于LEFT连接,但具有更大的灵活性,这在更复杂的情况下非常有用。(在 PostgreSQL 和 DB2 等其他 DBMS 中,也存在相同的功能,带有LATERAL连接)。
请注意如何将ON选项 3 中的条件移动到WHERE, 在外部应用子查询中:

SELECT  
    p.Name,  
    COALESCE(BookCount, 0) AS BookCount,   --- using COALESCE() so the NULLs produced
    COALESCE(CarCount, 0)  AS CarCount     --- by the (OUTER APLY) joins for persons
                                           --- that have no car or no book (shame!)
                                           --- are turned into 0
FROM 
    Person AS p
  OUTER APPLY
    ( SELECT PersonID
           , COUNT(*) AS BookCount,
      FROM Book
      WHERE PersonID = p.ID  
      GROUP BY PersonID
    ) AS b
  OUTER APPLY
    ( SELECT PersonID
           , COUNT(*) AS CarCount,
      FROM Car 
      WHERE PersonID = p.ID  
      GROUP BY PersonID
    ) AS c ;
Run Code Online (Sandbox Code Playgroud)

所有 4 个查询都将给出相同的结果 - 所有人员及其书籍和汽车的数量,即使他们没有书或没有汽车。如果您只想显示拥有至少一本书或至少一辆汽车(或两者)的 Person,则可以轻松修改选项 2、3 和 4:只需将各自LEFT OUTER JOIN(或两者)更改为INNER JOIN- 和OUTER APPLYtoCROSS APPLY

如果问题涉及计算其他聚合,如MAX()SUM()等,则选项基本相同 - 但请注意,选项 2 可以与MIN和一起使用,MAX但不能与SUM或 一起使用AVG。选项 1 可以与任何聚合函数一起使用,但每个聚合都需要一个单独的子查询,因此如果需要来自同一个表的多个聚合,则选项 3 和 4 更可取


Tho*_*ger 3

我的下面的查询所做的只是隔离不同的聚合。换句话说,我获取书籍的数量,然后对汽车的数量进行 FULL JOIN(以防任一子查询没有相关的 Person)。

select
    PersonBook.Name,
    book_count,
    car_count
from
(
    select
        p.ID,
        p.Name,
        count(b.BookID) as book_count
    from Person p
    left join Book b
    on p.ID = b.PersonID
    group by p.ID, p.Name
) PersonBook
full join
(
    select
        p.ID,
        p.Name,
        count(c.CarID) as car_count
    from Person p
    left join Car c
    on p.ID = c.PersonID
    group by p.ID, p.Name
) PersonCar
on PersonBook.ID = PersonCar.ID
Run Code Online (Sandbox Code Playgroud)