it-swarm.com.de

Wie wähle ich mehrere COUNT (*) - Spalten aus, wobei jede GROUP BY eine andere WHERE-Klausel hat?

Ich habe das folgende Schema:

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
)

und die folgenden Daten:

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')

Ich möchte eine Abfrage ausführen, die die Anzahl der Aktionen anzeigt, die jede Person zwischen dem 15. eines jeden Monats ausführt. Zum Beispiel versuche ich Folgendes:

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

Die Abfrage, die ich versuche, gibt jedoch Folgendes zurück:

PersonId | GroupA_Actions_Made | GroupB_Actions_Made
(null)     0                     1

aber ich möchte

PersonId | GroupA_Actions_Made | GroupB_Actions_Made
1          2                     0
2          1                     0
3          0                     1

(Ich möchte nicht, dass die Ergebnisse etwas für Personen zurückgeben, die keine Maßnahmen ergriffen haben.)

Wie kann ich die Ergebnisse im gewünschten Format erhalten?

UPDATE

Jede der Antworten funktioniert, außer dass ich sie wie folgt einpacken musste:

SELECT
    PersonId,
    GroupA_Actions_Made,
    GroupB_Actions_Made
FROM (
    -- (answer)
) t
WHERE
    GroupA_Actions_Made > 0
    OR GroupB_Actions_Made > 0

Mit SQL Server Profiler scheint die akzeptierte Antwort die schnellste Abfragezeit für große Datenmengen zu haben.

6
cm007

Dies gibt Ihnen das gewünschte Ergebnis, aber ich bin mir nicht sicher, ob es der flexibelste Code ist.

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
6
James Anderson

Ein etwas anderer Ansatz:

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

Auch Vorsicht zwischen .

1
SQLFox

Dies sollte etwas flexibler sein.

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
1
Kenneth Fisher