次のスキーマがあります:
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
)
および次のデータ:
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')
各人が毎月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
しかし、私が試しているクエリは次を返しています:
PersonId | GroupA_Actions_Made | GroupB_Actions_Made
(null) 0 1
でも欲しい
PersonId | GroupA_Actions_Made | GroupB_Actions_Made
1 2 0
2 1 0
3 0 1
(アクションを実行していない人には結果が何も返さないようにしたい)。
希望する形式で結果を取得するにはどうすればよいですか?
[〜#〜]更新[〜#〜]
次のようにラップする必要があることを除いて、それぞれの回答は機能します。
SELECT
PersonId,
GroupA_Actions_Made,
GroupB_Actions_Made
FROM (
-- (answer)
) t
WHERE
GroupA_Actions_Made > 0
OR GroupB_Actions_Made > 0
SQL Serverプロファイラを使用すると、受け入れられた回答は、大規模なデータセットでのクエリ時間が最も速いようです。
これであなたが望んだ結果が得られますが、それが最も柔軟なコードであるかどうかはわかりません。
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
少し異なるアプローチ:
select
PersonId
,GroupA_Actions_Made
,GroupB_Actions_Made
from (
select
p.PersonId
,[groupName] = 'Group' + case
when a.ActionTime >= '2014-01-15' and a.ActionTime < '2014-02-15'
then 'A'
when a.ActionTime >= '2014-02-15' and a.ActionTime < '2014-03-15'
then 'B'
end + '_Actions_Made'
from dbo.Person p
inner join dbo.Action a
on p.PersonId = a.PersonId
) pdata
pivot(count([groupName]) for [groupName] in ([GroupA_Actions_Made],[GroupB_Actions_Made]))pvt
また、 間に注意 。
これはやや柔軟なはずです。
SELECT PersonId, [2014.02] AS GroupA_Actions_Made, [2014.03] AS GroupB_Actions_Made
FROM (
SELECT ActionId, PersonId,
CONVERT(char(7), DATEADD(month,1,DATEADD(dd,-14,ActionTime)), 102) Grouping
FROM Action
) p
PIVOT (COUNT(ActionId)
FOR Grouping IN ([2014.02],[2014.03])
) AS pvt