如何选择多个 COUNT(*) 列,每个 GROUP BY 都有不同的 WHERE 子句?

cm0*_*007 6 join sql-server-2008-r2 group-by

我有以下架构:

CREATE TABLE Person (
  PersonId int PRIMARY KEY
)

CREATE TABLE Action (
  ActionId int PRIMARY KEY,
  PersonId int NOT NULL FOREIGN KEY REFERENCES Person(PersonId),
  ActionTime datetime NOT NULL
)
Run Code Online (Sandbox Code Playgroud)

以及以下数据:

INSERT INTO Person (PersonId) VALUES
(1),
(2),
(3),
(4)

INSERT INTO Action (ActionId, PersonId, ActionTime) VALUES
(1, 1, '2014-02-01'),
(2, 1, '2014-02-02'),
(3, 2, '2014-02-02'),
(4, 3, '2014-03-05')
Run Code Online (Sandbox Code Playgroud)

我想运行一个查询,显示每个人在每个月的 15 日之间执行的操作数。例如,我正在尝试以下操作:

SELECT
    Person.PersonId,
    COALESCE(GroupA_Actions_Made, 0) AS GroupA_Actions_Made,
    COALESCE(GroupB_Actions_Made, 0) AS GroupB_Actions_Made
FROM
    Person
    RIGHT OUTER JOIN (
        SELECT
            PersonId,
            COUNT(*) AS GroupA_Actions_Made
        FROM
            Action
        WHERE
            ActionTime BETWEEN '2014-01-15 00:00:00' AND '2014-02-14 23:59:59'
        GROUP BY
            PersonId
    ) GroupA ON GroupA.PersonId = Person.PersonId
    RIGHT OUTER JOIN (
        SELECT
            PersonId,
            COUNT(*) AS GroupB_Actions_Made
        FROM
            Action
        WHERE
            ActionTime BETWEEN '2014-02-15 00:00:00' AND '2014-03-14 23:59:59'
        GROUP BY
            PersonId
    ) GroupB ON GroupB.PersonId = Person.PersonId
Run Code Online (Sandbox Code Playgroud)

但是,我正在尝试的查询返回以下内容:

PersonId | GroupA_Actions_Made | GroupB_Actions_Made
(null)     0                     1
Run Code Online (Sandbox Code Playgroud)

但我想

PersonId | GroupA_Actions_Made | GroupB_Actions_Made
1          2                     0
2          1                     0
3          0                     1
Run Code Online (Sandbox Code Playgroud)

(我不希望结果为没有采取行动的人返回任何东西。)

如何获得所需格式的结果?

更新

除了我必须按如下方式包装它们之外,每个答案都有效:

SELECT
    PersonId,
    GroupA_Actions_Made,
    GroupB_Actions_Made
FROM (
    -- (answer)
) t
WHERE
    GroupA_Actions_Made > 0
    OR GroupB_Actions_Made > 0
Run Code Online (Sandbox Code Playgroud)

使用 SQL Server Profiler,接受的答案似乎在大型数据集上具有最快的查询时间。

Jam*_*son 6

这会给你你想要的结果,但我不确定它是否是最灵活的一段代码。

SELECT p.PersonId,
    SUM(CASE 
          WHEN a.ActionTime >= '2014-01-15 00:00:00' 
            AND a.ActionTime < '2014-02-15 00:00:00'
          THEN 1 
          ELSE 0 
        END) AS GroupA_Actions_Made,
    SUM(CASE 
          WHEN a.ActionTime >= '2014-02-15 00:00:00' 
            AND a.ActionTime < '2014-03-15 00:00:00'
          THEN 1 
          ELSE 0 
        END) AS GroupB_Actions_Made
FROM
    Person p
JOIN
    Action a on p.PersonId = a.PersonId
GROUP BY p.PersonId
Run Code Online (Sandbox Code Playgroud)