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
几点
正如@swasheck 提到的,你不能像WHERE a = b = c
SQL那样有条件,它是无效的(与其他语言不同)。你需要做到
WHERE a = b AND b = c
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
LEFT
INNER JOIN
FULL OUTER JOIN
或者只是
这就是和加入,结合起来。 FULL JOIN
LEFT
RIGHT
CROSS JOIN
NATURAL JOIN
和变体(SQL-Server 不支持) Web 上有许多关于联接的参考资料和教程。您可以从MSDN 在线文档开始。
您还需要研究COUNT()
工作原理:
COUNT(*)
计算行数(一组)。
COUNT(column/expression)
计算column
orexpression
不为空的(组的)行数。如果column
不能NULL
,这与COUNT(*)
COUNT(DISTINCT column/expression)
计算column
or的不同值的数量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 APPLY
to(派生)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 APPLY
toCROSS APPLY
。
如果问题涉及计算其他聚合,如MAX()
、SUM()
等,则选项基本相同 - 但请注意,选项 2 可以与MIN
和一起使用,MAX
但不能与SUM
或 一起使用AVG
。选项 1 可以与任何聚合函数一起使用,但每个聚合都需要一个单独的子查询,因此如果需要来自同一个表的多个聚合,则选项 3 和 4 更可取。
我的下面的查询所做的只是隔离不同的聚合。换句话说,我获取书籍的数量,然后对汽车的数量进行 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)