Der effizienteste Weg, um mehrere Aggregate in einem einzigen gespeicherten Prozess zurückzugeben?

7

Was ist der beste oder effizienteste Weg, um mehrere aggregierte Ergebniswerte zu erhalten?

Grundsätzlich habe ich eine E-Mail-App und wollte alle Nachrichten für jeden Ordnertyp (Posteingang, gesendet, archiviert, markiert ...) wie unten definiert abrufen.

Hier ist eine Auswahl von Dingen, die ich in einem gespeicherten Proc-Aufruf ausführen möchte. (wobei [uid] die Benutzer-ID ist)

SELECT * FROM Message
Inbox    WHERE [to]    = [uid] 
Unread    WHERE [to]    = [uid] and isread = 0
Flagged    WHERE [to]    = [uid] and isFlagged = 1
Drafts    WHERE [to]    = [uid] and isDraft = 1
Sent Messages    WHERE [from] = [uid]
Archived Messages    WHERE [to]     = [uid] and isArchived = 1
Kakalapy
quelle

Antworten:

7

COUNT..CASE ist der übliche Weg. Für "Gesendet" ist es allerdings etwas schwieriger, da es sich um einen anderen UID-Filter handelt, für den zwei Abfragen in der Nachrichtentabelle erforderlich sind

Ich habe dieses Konstrukt verwendet, um eine Nullzeile für von / nach zuzulassen. Es vermeidet ein ODER und fügt jedem CASE einen UID-Filter hinzu.

Jede abgeleitete Tabelle (kann als CTE geschrieben werden) gibt nur eine Zeile zurück, und ich habe ein MAX für uid verwendet, um auch GROUP BY zu vermeiden, falls sich jemand wundert

SELECT
    ISNULL(M.Inbox, 0) AS Inbox,
    ISNULL(M.Unread, 0) AS Unread,
    ISNULL(M.Flagged, 0) AS Flagged,
    ISNULL(M.Drafts, 0) AS Drafts,
    ISNULL(S.Sent, 0) AS Sent,
    ISNULL(M.Archived, 0) AS Archived
FROM
    (
    SELECT
        COUNT(*) AS InBox,
        COUNT(CASE WHEN isread = 0 THEN 1 ELSE NULL END) Unread,
        COUNT(CASE WHEN isFlagged = 1 THEN 1 ELSE NULL END) Flagged,
        COUNT(CASE WHEN isDraft = 1 THEN 1 ELSE NULL END) Drafts,
        S.Sent,
        COUNT(CASE WHEN isArchived = 0 THEN 1 ELSE NULL END) Archived
    FROM
        Message
    WHERE
        [to] = [uid] 
    ) M
    FULL OUTER JOIN
    (
    SELECT COUNT(*) AS Sent
    FROM Message
    WHERE [from] = [uid]
    ) S ON 1=1

Bearbeiten:

Ein Aggregat ohne GROUP BY gibt immer ein Ergebnis zurück (siehe SO hier ). Ich habe es vergessen.

Ich habe mein SQL aktualisiert

gbn
quelle
Ja, ich denke, das Gesendete ist ein Stock in den Speichen ... Ich habe mehr nach dem gedacht, was @Cape Cod Gunny gepostet hat. Aber ich bin mir nicht sicher, ob die Mehrfachauswahl besser oder schlechter ist als die vollständige äußere Verknüpfung, die Sie gepostet haben. Gedanken?
Kakalapy
2
@kacalapy: Dies sind 2 Abfragen der Tabelle und eine Anweisung. Die andere Antwort sind 6 Anfragen und mehrere Aussagen. Selbst mit der Indizierung ist es 66% weniger Arbeit in einfachen Worten
gbn
@gbn - warum nicht das joinuidZeug fallen lassen und ein verwenden cross join?
Jack sagt, versuchen Sie es mit topanswers.xyz
@ JackPDouglas: Das habe ich mir überlegt. Wenn jedoch keine gesendete Nachricht oder keine Posteingangsnachrichten vorhanden sind, erhalten Sie keine Ergebnisse. Der MAX dient nur dazu, ein GROUP BY zu vermeiden (was für viele UID-Werte nützlich wäre). (Ich habe diesen Kommentar
übrigens
1
@ JackPDouglas: Ich habe angenommen, dass es irgendwo mindestens eine Nachricht geben wird. Wie wir wissen, ist die Annahme die Mutter aller Probleme :-). Ich hatte 1 = 1, wurde aber entfernt, weil ich zu undurchsichtig war.
Na ja
2
DECLARE @Inbox  bigint
DECLARE @Unread bigint
DECLARE @Flagged bigint
/*
MORE DECLARE STATEMENTS GO HERE
*/

SET @Inbox   = SELECT Count(*) FROM MESSAGE WHERE [to] = [uid] 
SET @Unread  = SELECT Count(*) FROM MESSAGE WHERE [to] = [uid] and isread = 0
SET @Flagged = SELECT Count(*) FROM MESSAGE WHERE [to] = [uid] and isFlagged = 1

/*
MORE SELECT STATEMENTS GO HERE
*/

SELECT
@Inbox   as Inbox,
@Unread  as Unread,
@Flagged as Flagged

/*
MORE @Variables GO HERE
*/
Michael Riley - AKA Gunny
quelle