Wie kann eine normalisierte SQL-Datenbank effektiv abgefragt und aggregiert werden?

7

In Bezug auf die effizienteste Methode, um mehrere Aggregate in einem einzigen gespeicherten Prozess zurückzugeben?

Ich habe eine E-Mail-Anwendung und möchte alle Nachrichten (Posteingang) für einen Benutzer auswählen. Das Problem ist, dass ich den Header-Teil der E-Mails in der Datenbank so normalisiere, dass die Flat-Daten in eine Nachrichtentabelle gelangen und das BCC von, bis, CC und in einer anderen Tabelle gespeichert wird.

Wie können Nachrichten am besten ausgewählt werden (dh die vollständige Nachricht wird denormalisiert), sodass jeder Datensatz alle für die Nachricht relevanten Felder enthält, einschließlich aller Felder der Nachrichtentabelle und aller zugehörigen Datensätze aus der Empfängertabelle, die sich auf die Nachricht beziehen, gemäß PK / FK Beziehung.

Eine Sache, auf die ich großen Wert lege, ist die Effizienz der SQL-Lösung, da dies Code ist, der viele Male ausgeführt wird und wahrscheinlich die am häufigsten ausgeführte SQL der gesamten Datenbank ist

Zum Kontext hier eine Ansicht meines DB-Schemas.

DB SCHEMA

Kakalapy
quelle
Wie viele Daten erwarten Sie in der Nachrichtentabelle?
Gbn
@gbn: Millionen von Datensätzen. Daher sind mindestens zwei Datensätze für jeden Nachrichtendatensatz mindestens in der Empfängertabelle (von, bis) enthalten.
Kakalapy
Möchten Sie, dass Ihre Ausgabe eine einzelne Textzeichenfolge wie die ursprüngliche E-Mail ist? Wenn ja, benötigen Sie eine Art String-Verkettungsaggregat wie die in dieser SO-Frage beschriebenen
Jack sagt, versuchen Sie es mit topanswers.xyz
4
ist es nur ich oder sieht dieses Schema viel zu normalisiert aus?
Marian
@Marian: Die Normierung beruht darauf, dass die Empfängertabelle Nachrichten aus Outlook erfassen kann, wobei eine einzelne Nachricht mehrere TO-, CC- und BCC-Einträge enthalten kann.
Kakalapy

Antworten:

6

So würde ich es machen. Ich verwende Coalesce regelmäßig, um Zeilen in getrennte Felder zu platzieren, und es funktioniert immer gut und skaliert gut (solange Sie erkennen, dass eine Unterabfrage IMMER einen Leistungseinbruch verursachen wird).

Wenn Sie es nicht als gespeicherte Prozedur ausführen möchten, können Sie es auch einfach als Tabellenwertfunktion umschreiben.

Ein anderer Ansatz wäre vermutlich ein CTE, aber ich bin mit diesem Ansatz zum Schreiben von Grund auf nicht so vertraut.

CREATE PROCEDURE GetMessageById
    @pMessageID int
AS
BEGIN

SET NOCOUNT ON;

Declare @pTo varchar(max)
Declare @pCC varchar(max)
Declare @pBC varchar(max)

SELECT @pTo = COALESCE(@pTo + ', ', '') + [EmailAddress]
FROM MessageRecipient
WHERE MessageID = @pMessageID AND RecipientTypeID = 1 /** or whatever the id of TO field is */

SELECT @pCC = COALESCE(@pCC + ', ', '') + [EmailAddress]
FROM MessageRecipient
WHERE MessageID = @pMessageID AND RecipientTypeID = 2 /** or whatever the id of CC field is */

SELECT @pBC = COALESCE(@pBC + ', ', '') + [EmailAddress]
FROM MessageRecipient
WHERE MessageID = @pMessageID AND RecipientTypeID = 3 /** or whatever the id of BCC field is */

SELECT Message.*, @pTo as [ToField], @pCC as [CCField], @pBC as [BCCField], (SELECT TOP 1 [EmailAddress] FROM MessageRecipient Where RecipientTypeID = 0 /**<sender id>*/ AND MessageID = @pmessageID) AS [FromField] FROM Message Where Message.ID = @pMessageID

END
GO

Sie könnten sich fragen, wie Coalesce auf diese Weise funktioniert (ich habe es getan, als ich es zum ersten Mal gesehen habe). Grundsätzlich wird eine rekursive Abfrage erstellt, die jeden nachfolgenden Nicht-Null-Wert in der Menge bis zum Ende der Rückgabemenge zurückgibt. Wenn Sie am anderen Ende herauskommen, erhalten Sie eine durch Komas getrennte Liste aller Ergebnisse als einzelne Zeichenfolge.

RThomas
quelle
Ich hatte gerade die Gelegenheit, diesen Code zu Hause auszuprobieren, und erkannte, dass ich ein wenig debuggen musste.
RThomas
1
Das ist ein ziemlich ordentlicher Trick mit COALESCE. Wo hast du das gelernt?
Michael Riley - AKA Gunny
Ich wünschte, ich könnte sagen, ich hätte es selbst herausgefunden, aber leider ... Ich habe vor einiger Zeit einen Artikel über Alternativen zu Cursorn gefunden. Dies war einer der beschriebenen Tricks. Ich denke, es war ein SqlServerCentral-Artikel.
RThomas
5

Ich würde eine Ansicht namens viewInbox erstellen , die von allen Eins-zu-Eins- Beziehungstabellen erstellt wird. Dies wäre meine Hauptabfrageansicht. Ich würde diese Ansicht (viewInbox) verwenden, um eine Liste aller Posteingangselemente anzuzeigen.

Wenn der Benutzer einen Drilldown in die Nachricht durchführt, bringe ich alle Informationen zurück, einschließlich der Eins-zu-Viele- Beziehungen aus den mehreren To's, CC's und BCC's.

Michael Riley - AKA Gunny
quelle
Sicher performanter, und wahrscheinlich auch, wie ich es machen würde, aber die Anfrage nach "Denormalisierung" wird nicht erfüllt.
RThomas
2
@LazyDBA - Ich würde den viewInbox als "denormalisiert" betrachten.
Michael Riley - AKA Gunny
"normalisiert" und "denormalisiert" beziehen sich auf die Gestaltung von Tabellen. Ein Schema zu denormalisieren bedeutet eine Änderung des Schemas. Wenn also eine Anforderung zum "Denormalisieren" der Daten mithilfe einer select-Anweisung vorliegt, wird das Wort vermutlich verwendet, um im Wesentlichen "redundante Daten einschließen" zu bedeuten und Tabellen zu einer einzigen Ergebnismenge zusammenzufügen.
Michael J Swart