SQL count(*)=1 时的聚合函数,所以只能有一个值

SQL aggregate function when count(*)=1 so there can be only one value

有时您会编写一个分组查询,其中每个组都是一行,如 having count(*) = 1。这意味着像 minmaxsum 等常用的聚合函数有点毫无意义:最小值等于最大值、等于总和、等于平均值​​。因为只有一个值要聚合。

我通常会随意选择 min。如果我们采用 table 将一本书映射到其作者的熟悉示例,我可能只想查询只有一个作者的书:

-- For books that have a single author, pull back that author's id.
select book_id,
       min(author_id) as author_id
       -- I could equally well use max(author_id) or even sum(author_id)...
from book_authors
group by book_id
having count(*) = 1

行得通,但似乎可以更好地表达。我实际上对 'minimum' 本身并不感兴趣,只是为了获得我知道存在的单一值。某些列类型(例如 Microsoft SQL Server 中的 bit)不支持 min 聚合函数,因此您必须使用 convert(bit, min(convert(int, mycol))).

等变通方法

所以,我希望答案是否定的,但是有没有更好的方法来说明我的意图?

select book_id,
       there_must_be_one_value_so_just_return_it(author_id) as author_id
from book_author
group by book_id
having count(*) = 1

显然,如果您不需要 count(*)=1,那么您将不再保证单个值,并且无法使用特殊的聚合函数。编译 SQL 时可能会发现该错误。

所需的结果将等同于上面的 min 查询。

我使用的是 Microsoft SQL Server (2016),但由于这是一个相当“蓝天”的问题,我也对其他 SQL 方言的回复感兴趣。

您可以改为使用窗口化 COUNT,然后基于它进行过滤:

WITH CTE AS(
    SELECT ba.book_id,
           ba.author_id,
           COUNT(ba.book_id) OVER (PARTITION BY ba.book_id) AS Authors
    FROM dbo.book_authors ba)
SELECT c.book_id,
       c.author_id
FROM CTE c
WHERE c.Authors = 1;

另一种方法是使用相关子查询:

SELECT ba.book_id,
       ba.author_id
FROM dbo.book_authors ba
WHERE EXISTS (SELECT 1
              FROM dbo.book_authors e
              WHERE e.book_id = ba.book_id
              GROUP BY e.book_id
              HAVING COUNT(*) = 1);

我还没有用相当数量的数据测试性能,但是,我 希望 对于具有良好索引 table 的相关子查询,你应该会看到更好的性能。