2009-06-18 5 views
1

Ich habe eine Tabelle, Schema ist sehr einfach, eine ID-Spalte als eindeutiger Primärschlüssel (uniqueidentifier Typ) und einige andere Nvarchar-Spalten. Mein aktuelles Ziel ist es, für 5000 Eingaben zu berechnen, welche bereits in der Tabelle enthalten sind und welche nicht. Tht-Eingaben sind Zeichenfolge und ich habe eine C# -Funktion, die Zeichenfolge in Uniqueidentifier (GUID) konvertiert. Meine Logik ist, wenn es eine vorhandene ID gibt, behandle ich die Zeichenfolge als bereits in der Tabelle enthalten.wie SQL-Abfrage-Leistung in meinem Fall zu verbessern

Meine Frage ist, wenn ich herausfinden muss, welche aus den 5000 Eingabezeichenfolgen bereits in DB enthalten sind, und was nicht, was ist der effizienteste Weg?

BTW: Meine aktuelle Implementierung ist, konvertieren Zeichenfolge in GUID mit C# -Code, dann aufrufen/implementieren eine Speicherprozedur, die Abfrage, ob eine ID in der Datenbank vorhanden ist und zurück zu C# -Code.

Meine Arbeitsumgebung: VSTS 2008 + SQL Server 2008 + C# 3.5.

Antwort

3

Mein erster Instinkt wäre Ihre 5000 Eingänge in eine einspaltig temporäre Tabelle X zu pumpen, möglicherweise Index, und dann verwenden:

SELECT X.thecol 
FROM X 
JOIN ExistingTable USING (thecol) 

auf diejenigen zu bekommen, die vorhanden sind, und (wenn beide Sätze werden benötigt)

SELECT X.thecol 
FROM X 
LEFT JOIN ExistingTable USING (thecol) 
WHERE ExistingTable.thecol IS NULL 

um diejenigen zu bekommen, die abwesend sind. Mindestens Benchmarking wert.

Edit: wie angefordert, hier sind einige gute docs & Tutorials auf temporäre Tabellen in SQL Server. Bill Graziano hat eine einfache Einführung, die temporäre Tabellen, Tabellenvariablen und globale temporäre Tabellen abdeckt. Randy Dyess und SQL Master diskutieren Leistungsproblem für und gegen sie (aber denken Sie daran, dass wenn Sie Performance-Probleme haben Sie wollen, Benchmark-Alternativen, nicht gehen Sie einfach auf theoretische Überlegungen! -).

MSDN hat Artikel auf tempdb (wo temporäre Tabellen gespeichert sind) und optimizing seine Leistung.

+0

"einspaltige temporäre Tabelle X" - zu schätzen, wenn Sie dies klären könnten. Ich denke, dieser Punkt ist wichtig. temporäre Tabelle Sie meinen, eine physische Tabelle zu erstellen oder? – George2

+1

CREATE TABLE #X (thecol VARCHAR (30)) macht die temporäre Tabelle (die führende # im Namen ist, was es temporär macht) - es dauert nur so lange wie die Prozedur oder Sitzung, die es erstellt. –

+0

In meinem Szenario, (1) warum eine temporäre Tabelle besser ist? Verglichen mit dem Erstellen einer anderen physischen Tabelle? (2) Für den temporären Tisch, was ist Ihr Rat, wenn wir es erstellen/wenn wir es fallen lassen. d. h., es wird jedes Mal eine neue temporäre Tabelle erstellt, wenn 5000 Batch-Abfragen vorhanden sind und nach dem Join gelöscht werden, oder wenn die temporäre Tabelle nur einmal erstellt wird? – George2

1

Versuchen Sie sicherzustellen, dass Sie nur eine Abfrage ausführen. Wenn Ihre Lösung beispielsweise 5000 Abfragen für die Datenbank ausführt, ist dies wahrscheinlich der größte Ressourcenverbraucher für die Operation.

Wenn Sie die 5000 IDs in eine temporäre Tabelle einfügen können, können Sie eine einzelne Abfrage schreiben, um diejenigen zu finden, die nicht in der Datenbank vorhanden sind.

+0

"Versuchen Sie sicherzustellen, dass Sie am Ende nur eine Abfrage ausführen" - meinen Sie, ich habe 5000 Mal in meinem C# -Code eine Abfrage nach der anderen durchgeführt, um aufzuzeichnen, ob sie in der Datenbank angekommen sind oder nicht? Wenn es das ist, was du meinst, dann ist es das, was ich tue. – George2

+0

"Wenn Sie die 5000 IDs in eine temporäre Tabelle einfügen können, können Sie eine einzelne Abfrage schreiben, um diejenigen zu finden, die nicht in der Datenbank vorhanden sind." - Könnten Sie mir Pseudo-Code zeigen, wie man mit einer einzigen Abfrage diejenigen findet, die nicht in der Datenbank existieren? – George2

+1

Siehe Martellis Antwort für ein gutes Beispiel. –

2

Was müssen Sie mit den Einträgen tun, die in Ihrer Tabelle vorhanden sind oder nicht?

Je nachdem, was Sie benötigen, möglicherweise die neue MERGE Anweisung in SQL Server 2008 könnte Ihre Rechnung passen - zu aktualisieren, was bereits vorhanden ist, fügen Sie neue Sachen, alle ordentlich in eine einzige SQL-Anweisung. Hör zu!

Ihre Aussage würde wie folgt aussehen:

MERGE INTO 
    (your target table) AS t 
USING 
    (your source table, e.g. a temporary table) AS s 
ON t.ID = s.ID 
WHEN NOT MATCHED THEN -- new rows does not exist in base table 
    ....(do whatever you need to do) 
WHEN MATCHED THEN  -- row exists in base table 
    ... (do whatever else you need to do) 
; 

dies wirklich schnell zu machen, würde ich die "neue" Datensätze laden von zB Datei eine TXT oder CSV in eine temporäre Tabelle in SQL Server unter Verwendung von Bulk:

BULK INSERT YourTemporaryTable 
FROM 'c:\temp\yourimportfile.csv' 
WITH 
(
    FIELDTERMINATOR =',', 
    ROWTERMINATOR =' |\n' 
) 

INSERT BULK mit MERGE kombiniert sollten Sie die beste Leistung geben Sie auf diesem Planeten :-)

Marc

bekommen

PS: hier ist eine Notiz von TechNet auf MERGE Leistung und warum es schneller ist als Einzelaussage:

In SQL Server 2008 können Sie mehrere Datenbearbeitungssprache (DML) durchzuführen, in einer einzigen Anweisung mit der MERGE-Anweisung. Beispielsweise müssen Sie möglicherweise zwei Tabellen synchronisieren, indem Sie Zeilen in einer Tabelle basierend auf den in der anderen Tabelle gefundenen Unterschieden einfügen, aktualisieren oder löschen. In der Regel erfolgt dies durch Ausführen einer gespeicherten Prozedur oder eines Stapels, der einzelne INSERT-, UPDATE- und DELETE-Anweisungen enthält. Dies bedeutet jedoch, dass die Daten sowohl in der Quell- als auch in der Zieltabelle mehrfach ausgewertet und verarbeitet werden. mindestens einmal für jede Aussage. Mithilfe der MERGE-Anweisung können Sie die einzelnen DML-Anweisungen durch eine einzelne Anweisung ersetzen. Dies kann die Abfrageleistung verbessern, da die Vorgänge in einer einzigen Anweisung ausgeführt werden und somit die Anzahl der Verarbeitungsvorgänge der Daten in den Quell- und Zieltabellen minimiert wird. Leistungssteigerungen hängen jedoch von korrekten Indizes, Joins und anderen Überlegungen ab. Dieses Thema enthält Best-Practice-Empfehlungen, mit denen Sie eine optimale Leistung bei Verwendung der MERGE-Anweisung erzielen können.

+0

Die Verwendung Senario ist, ich habe eine große Job/Auftrag-Datenbank, die bereits verarbeiteten Job/Auftrag enthält, für neue 5000 Batch-Auftrag/Job-Anfragen, werde ich zuerst nachsehen, ob die Bestellung/Aufträge bereits verarbeitet werden, wenn nicht ich verarbeitet nicht verarbeiteten Auftrag/Auftrag. Denkst du, Merge ist für mein Szenario geeignet? – George2

+1

Ja, absolut! Das ist DAS perfekte Szenario für MERGE. Sie haben eine Tabelle mit Ihren neu verarbeiteten Aufträgen, und Sie aktualisieren dann die Basistabelle und z. Setzen Sie eine Flagge, oder fügen Sie eine Zeile hinzu oder was immer Sie tun müssen. –

+0

Haben Sie die Antwort von Alex über Outer Join gelesen? Was sind die Vor- und Nachteile von Merge im Vergleich zu Links Join? – George2

3

Schritt 1. Stellen Sie sicher, dass Sie ein Problem zu lösen haben. Fünftausend Inserts ist nicht viel, um in vielen Kontexten eins nach dem anderen einzufügen.

Sind Sie sicher, dass der einfachste Weg nicht ausreicht? Welche Leistungsprobleme haben Sie bisher gemessen?

+0

Die Anzahl der Abfragen ist konfigurierbar und ich möchte, dass meine Lösung für große Zahlen wie 1M funktioniert. – George2

+1

Sie erhalten verschiedene Antworten für verschiedene Probleme. Sie müssen dann 1MM in Ihrer Frage sagen. Sie müssen nach Bulk-Insert-Strategien fragen. Die Antwort ist auch anders, wenn Sie sehr wahrscheinlich oder sehr unwahrscheinlich sind, dass Sie Spiele erhalten. – dkretz

+0

1M ist nicht in naher Zukunft, aktuelle Szenario ist für alle 15 Minuten, gibt es 5000 Batch-Abfragen. Irgendein Rat? – George2

1

Wenn Sie Einfachheit wollen, da 5000 Datensätze nicht sehr viele sind, dann verwenden Sie aus C# einfach eine Schleife, um eine Insert-Anweisung für jede der Zeichenfolgen zu generieren, die Sie der Tabelle hinzufügen möchten. Wickeln Sie die Einfügung in einen TRY CATCH-Block. Senden Sie sie alle in einem Schuss wie diese auf den Server hoch:

BEGIN TRY 
INSERT INTO table (theCol, field2, field3) 
SELECT theGuid, value2, value3 
END TRY BEGIN CATCH END CATCH 

BEGIN TRY 
INSERT INTO table (theCol, field2, field3) 
SELECT theGuid, value2, value3 
END TRY BEGIN CATCH END CATCH 

BEGIN TRY 
INSERT INTO table (theCol, field2, field3) 
SELECT theGuid, value2, value3 
END TRY BEGIN CATCH END CATCH 

wenn Sie einen eindeutigen Index oder Primärschlüssel auf dem String GUID definiert haben, dann werden die doppelten Einsätze scheitern. Wenn Sie im Voraus überprüfen, ob der Datensatz nicht existiert, werden nur Duplikate ausgeführt, die SQL ohnehin ausführen wird.

Wenn die Leistung wirklich wichtig ist, dann sollten Sie die 5000 GUIDS auf Ihre lokale Station herunterladen und alle Analysen durchführen. Das Lesen von 5000 GUIDs sollte viel weniger als 1 Sekunde dauern. Dies ist einfacher als der Massenimport in eine temporäre Tabelle (dies ist die einzige Möglichkeit, Leistung von einer temporären Tabelle zu erhalten) und eine Aktualisierung mithilfe einer Verknüpfung zur temporären Tabelle.

+0

Ich denke, Ihre Lösung, die Duplikate überprüft, indem Sie verwenden, ob SQL Server Fehler zurückgibt, ist nicht sehr zuverlässig, wie Sie sehen können, wenn wir Fehler einfügen gibt es viele Gründe, einschließlich doppelten Wert. – George2

+0

"Wenn die Leistung wirklich wichtig ist, dann sollten Sie die 5000 GUIDS zu Ihrer lokalen Station herunterladen und alle Analyse-Local-Vorgänge durchführen."- Was meinst du Download? Mein Szenario ist ich habe eine große Tabelle mit mehreren M verarbeiteten Bestellung und jedes Mal gibt es Batch-Eingang 5000 Aufträge zu überprüfen, againts die mehrere M verarbeitete Reihenfolge Tabelle zu finden unprocssed. – George2

+0

Es war nicht klar, dass die Tabelle in der DB mehrere M-Datensätze hatte. Fügen Sie diese Informationen zu Ihrer Frage hinzu! Sie haben Recht, es könnte andere Fehler bei der Ausführung der Insert-Anweisung geben. Sie können meine Lösung ändern, um nur einen Schlüsselverletzungsfehler zu absorbieren und zu werfen andere Fehler Können Sie mir das sagen, 1) wie lange dauert es, um die 5000 Datensätze zu importieren, und 2) wie viele der Datensätze sind bereits in der Tabelle? Ich frage, weil, wenn die Anzahl der doppelten Datensätze klein ist, sagen 10-100, dann sparen Sie nicht viel, indem Sie die 10-100 Einfügungen nicht senden .. – johnnycrash

1

Auf keinen Fall eins nach dem anderen.

Meine bevorzugte Lösung ist eine gespeicherte Prozedur mit einem Parameter zu erstellen, die in folgendem Format nehmen und XML können:

<ROOT> 
    <MyObject ID="60EAD98F-8A6C-4C22-AF75-000000000000"> 
    <MyObject ID="60EAD98F-8A6C-4C22-AF75-000000000001"> 
    .... 
</ROOT> 

Dann in dem Verfahren mit dem Argument vom Typ NCHAR (MAX) Sie wandeln es in XML, nach was Sie es als Tabelle mit einer einzelnen Spalte verwenden (nennen wir es @FilterTable). Der Speichervorgang wie folgt aussieht:

CREATE PROCEDURE dbo.sp_MultipleParams(@FilterXML NVARCHAR(MAX)) 
AS BEGIN 
    SET NOCOUNT ON 

    DECLARE @x XML 
    SELECT @x = CONVERT(XML, @FilterXML) 

    -- temporary table (must have it, because cannot join on XML statement) 
    DECLARE @FilterTable TABLE (
     "ID" UNIQUEIDENTIFIER 
    ) 

    -- insert into temporary table 
    -- @important: XML iS CaSe-SenSiTiv 
    INSERT  @FilterTable 
    SELECT  x.value('@ID', 'UNIQUEIDENTIFIER') 
    FROM  @x.nodes('/ROOT/MyObject') AS R(x) 

    SELECT  o.ID, 
       SIGN(SUM(CASE WHEN t.ID IS NULL THEN 0 ELSE 1 END)) AS FoundInDB 
    FROM  @FilterTable o 
    LEFT JOIN dbo.MyTable t 
      ON o.ID = t.ID 
    GROUP BY o.ID 

END 
GO 

Sie führen es als:

EXEC sp_MultipleParams '<ROOT><MyObject ID="60EAD98F-8A6C-4C22-AF75-000000000000"/><MyObject ID="60EAD98F-8A6C-4C22-AF75-000000000002"/></ROOT>' 

Und Ihre Ergebnisse wie folgt aussehen:

ID         FoundInDB 
------------------------------------ ----------- 
60EAD98F-8A6C-4C22-AF75-000000000000 1 
60EAD98F-8A6C-4C22-AF75-000000000002 0 
+0

Verwirrt über Ihre SQL-Anweisung, 1. Ich bin verwirrt darüber, wie Sie ID-Stribute aus jeder Zeile der XML-Datei extrahieren? Ich habe nicht gefunden eine solche Aussage in Ihrer Antwort 2. Eine andere Frage ist, was d Das heißt genau - "SIGN (SUM (FALL WENN t.ID NULL DANN 0 ELSE 1 ENDE))"? – George2

+0

Danke für Ihren Rat, meine Eingabe ist ein String-Array und ich möchte es nicht in XML einbinden, um zusätzlichen Overhead hinzuzufügen. Denken Sie, dass die Lösung der Masseneinfügung in eine temporäre Tabelle und dann der temporären Tabelle mit der wirklich großen verarbeiteten Auftragstabelle Sinn macht? – George2

+1

SIGN (...) gibt grundsätzlich 0 zurück, wenn 0 Zeilen gefunden wurden, und 1, wenn mehr als 1 Zeilen gefunden wurden. In Ihrem Fall ist der Filter UNIQUE, also ist dies nicht wirklich erforderlich, also können Sie das SIGN fallen lassen und nur SUM (...) lassen. – van

1

Da Sie SQL Server 2008 verwenden, können Sie Tabelle verwenden könnte -bewertete Parameter. Es ist eine Möglichkeit, eine Tabelle als Parameter für eine gespeicherte Prozedur bereitzustellen.

Mit ADO.NET können Sie einfach eine DataTable vorbelegen und als SqlParameter übergeben. Schritte müssen Sie ausführen:

Erstellen einer benutzerdefinierten SQL-Typ

CREATE TYPE MyType AS TABLE 
(
UniqueId INT NOT NULL, 
Column NVARCHAR(255) NOT NULL 
) 

eine gespeicherte Prozedur erstellen, die den Typ

CREATE PROCEDURE spInsertMyType 
@Data MyType READONLY 
AS 
xxxx 

Anruf mit C#

SqlCommand insertCommand = new SqlCommand(
    "spInsertMyType", connection); 
insertCommand.CommandType = CommandType.StoredProcedure; 
SqlParameter tvpParam = 
    insertCommand.Parameters.AddWithValue(
    "@Data", dataReader); 
tvpParam.SqlDbType = SqlDbType.Structured; 

Verbindungen akzeptiert: Table-valued Parameters in Sql 2008

+0

Passing Parameter ist der erste Schritt, dann nächsten Schritt, den Sie vorschlagen, dass ich links beitreten? – George2

+0

Sie könnten definitiv einen linken Join verwenden. Darüber hinaus erwähnten Sie die Verwendung einer C# -Funktion, um Strings in IDs zu konvertieren. Da der Tabellenwert-Parameter mehrere Felder enthalten kann, ist es möglicherweise nicht erforderlich, die Daten vorab zu verarbeiten und nur Daten für alle Felder zu senden, die Sie abfragen möchten. –

Verwandte Themen