Frage

Eine Abfrage, die durch 17 Millionen Datensätze in einer Schleife verwendet wird, um Duplikate zu entfernen wurde für den Betrieb nun über 16 Stunden , und ich wollte wissen, ob die Abfrage direkt gestoppt wird jetzt, wenn es die delete-Anweisungen abschließen oder wenn es zu löschen ist, während diese Abfrage ausgeführt wird? Ja, wenn ich es tun stoppen, nicht zum Abschluss bringt es die Löschungen oder Rollen zurück?

Ich habe festgestellt, dass, wenn ich ein

 select count(*) from myTable

Dass die Zeilen, die es gibt (während dieser Abfrage zu tun) etwa 5 weniger als das, was die Startzeilenanzahl war. Offensichtlich ist die Server-Ressourcen sind extrem schlecht, so bedeutet das, dass dieser Prozess 16 Stunden genommen hat 5 Duplikate zu finden (wenn es tatsächlich Tausende), und dies für Tage laufen könnte?

Diese Abfrage dauerte 6 Sekunden auf 2000 Zeilen von Testdaten, und es funktioniert super auf diesem Satz von Daten, so dass ich dachte es 15 Stunden für den kompletten Satz nehmen würde.

Irgendwelche Ideen?

Im Folgenden finden Sie die Abfrage:

--Declare the looping variable
DECLARE @LoopVar char(10)


    DECLARE
     --Set private variables that will be used throughout
      @long DECIMAL,
      @lat DECIMAL,
      @phoneNumber char(10),
      @businessname varchar(64),
      @winner char(10)

    SET @LoopVar = (SELECT MIN(RecordID) FROM MyTable)

    WHILE @LoopVar is not null
    BEGIN

      --initialize the private variables (essentially this is a .ctor)
      SELECT 
        @long = null,
        @lat = null,
        @businessname = null,
        @phoneNumber = null,
        @winner = null

      -- load data from the row declared when setting @LoopVar  
      SELECT
        @long = longitude,
        @lat = latitude,
        @businessname = BusinessName,
        @phoneNumber = Phone
      FROM MyTable
      WHERE RecordID = @LoopVar

      --find the winning row with that data. The winning row means 
      SELECT top 1 @Winner = RecordID
      FROM MyTable
      WHERE @long = longitude
        AND @lat = latitude
        AND @businessname = BusinessName
        AND @phoneNumber = Phone
      ORDER BY
        CASE WHEN webAddress is not null THEN 1 ELSE 2 END,
        CASE WHEN caption1 is not null THEN 1 ELSE 2 END,
        CASE WHEN caption2 is not null THEN 1 ELSE 2 END,
        RecordID

      --delete any losers.
      DELETE FROM MyTable
      WHERE @long = longitude
        AND @lat = latitude
        AND @businessname = BusinessName
        AND @phoneNumber = Phone
        AND @winner != RecordID

      -- prep the next loop value to go ahead and perform the next duplicate query.
      SET @LoopVar = (SELECT MIN(RecordID) 
    FROM MyTable
    WHERE @LoopVar < RecordID)
    END
War es hilfreich?

Lösung

Nein, SQL Server wird nicht zurückrollen das löscht sie bereits durchgeführt hat, wenn Sie Abfrage-Ausführung stoppen. Oracle erfordert eine explizite Einlieferung von Aktionsabfragen oder die Daten werden zurückgerollt, aber nicht mssql.

mit SQL Server nicht zurück rollen, wenn Sie speziell im Rahmen einer Transaktion ausführen und Rollback die Transaktion oder die Verbindung schließt, ohne die Transaktion begangen zu haben war. aber ich habe keinen Transaktionskontext in Ihrer obigen Abfrage sehen.

könnten Sie auch wieder Strukturierung Ihre Anfrage versuchen, die Löschungen ein wenig effizienter zu machen, aber im Wesentlichen, wenn die Spezifikationen der Box sind bis nicht Schnupftabak dann könnten Sie sie stecken wartet aus.

geht nach vorn, sollten Sie einen eindeutigen Index für die Tabelle erstellen, halten Sie sich aus durch diese wieder zu gehen.

Andere Tipps

Ihre Anfrage wird in einer Transaktion nicht umgebrochen, so wird es die Änderungen bereits von den einzelnen löschen Aussagen nicht Rollback.

ich das selbst speziell auf meinem eigenen SQL Server getestet, um die folgende Abfrage verwenden, und der ApplicationLog Tisch war leer, obwohl ich die Abfrage abgebrochen:

declare @count int
select @count = 5
WHILE @count > 0
BEGIN
  print @count
  delete from applicationlog;
  waitfor time '20:00';
  select @count = @count -1
END

Allerdings Ihre Abfrage ist wahrscheinlich viele Tage oder Wochen dauern, viel länger als 15 Stunden. Ihre Schätzung, die Sie 2000 verarbeiten kann erfasst alle 6 Sekunden falsch ist, weil jede Iteration in der while-Schleife wesentlich länger mit 17 Millionen Zeilen nehmen sie dann mit 2000 Zeilen tut. Also, wenn Sie Ihre Abfrage deutlich weniger dauert dann eine zweite für 2000 Zeilen, wird es Tage dauern, für alle 17 Millionen.

Sie sollten eine neue Frage auf Fragen, wie Sie effizient doppelte Zeilen löschen können.

Wenn Sie etwas tun nicht explizit über Transaktionen dann wird die Verbindung sein in autocommit Transaktionen Modus. In diesem Modus wird jede SQL-Anweisung eine Transaktion betrachtet.

Die Frage ist, ob dies bedeutet, dass die einzelnen SQL-Anweisungen Transaktionen sind und daher verpflichtet werden, wie Sie gehen, oder ob die äußere While-Schleife gilt als einer Transaktion.

Es scheint nicht jede Diskussion über diesen in der Beschreibung der Zeit lang zu sein Konstrukt auf MSDN . Da jedoch eine Aussage zwar nicht direkt auf die Datenbank ändern wäre es logisch, dass es nicht startet eine Auto-Commit-Transaktion.

Implizite Transaktionen

Wenn keine 'Implizite Transaktionen' gesetzt wurde, dann wird jede Iteration in der Schleife, um die Änderungen festgeschrieben.

Es ist möglich, für jeden SQL Server mit ‚Implizite Transaktionen‘ festgelegt werden. Dies ist eine Datenbank-Einstellung (Standardeinstellung ist OFF). Sie können auch implizite Transaktionen in den Eigenschaften einer bestimmten Abfrage innerhalb von Management Studio (Rechtsklick im Abfragebereich> Optionen), von Standardeinstellung in dem Client oder eine SET-Anweisung haben.

SET IMPLICIT_TRANSACTIONS ON;

So oder so, wenn dies der Fall ist, müssen Sie noch einen expliziten COMMIT / ROLLBACK ungeachtet der Unterbrechung der Ausführung der Abfrage auszuführen.


Implizite Transaktionen verweisen:

http://msdn.microsoft.com/en-us/library /ms188317.aspx

http://msdn.microsoft.com/en-us/library /ms190230.aspx

erbte ich ein System, das in SQL implementierte Logik etwas wie das Ihre hatte. In unserem Fall haben wir versucht, zusammen Reihen zu verbinden Fuzzy Matching, die ähnliche Namen / Adressen hatte, usw., und diese Logik wurde rein in SQL getan. Damals habe ich es geerbt hatten wir über 300.000 Zeilen in der Tabelle und nach den Zeiten, wir berechnet, es würde ein Jahr dauern, sie alle entsprechen.

Als ein Experiment, um zu sehen, wie viel schneller ich es außerhalb von SQL tun konnte, schrieb ich ein Programm, um die Tabelle db in flache Dateien zu entleeren, lesen Sie das Flat-Files in eine C ++ Programm, meine eigene Indizes bauen, und tut, um die Fuzzy Logik gibt, dann die flachen Dateien in die Datenbank importieren. Was dauerte ein Jahr, in SQL dauerte etwa 30 Sekunden in der C ++ App.

Also, mein Rat ist, versuchen Sie nicht einmal, was Sie in SQL tun. Export, Prozess, Re-Import.

DELETES, die bis zu diesem Zeitpunkt durchgeführt worden sind wird nicht rückgängig gemacht werden.


Als ursprünglicher Autor des Code in Frage und ausgestellt hat, den Vorbehalt, dass die Leistung auf Indizes abhängig sein wird, wie ich die folgenden Punkte vorschlagen würde dies zu beschleunigen.

recordID besser PRIMARY KEY sein. Ich meine nicht, IDENTITY, ich meine Primärschlüssel. Bestätigen Sie dies mit sp_help

Einige Index sollte bei der Bewertung dieser Abfrage verwendet werden. Herauszufinden, welche diese vier Säulen hat die wenigstenen Wiederholungen und Index, ...

SELECT *
FROM MyTable
WHERE @long = longitude
  AND @lat = latitude
  AND @businessname = BusinessName
  AND @phoneNumber = Phone

Vor und nach diesem Index hinzufügen, überprüfen Sie den Abfrage-Plan zu sehen, ob Index Scan wird hinzugefügt.

Als Schleife Abfrage kämpfen wird gut zu skalieren, auch mit entsprechendem Indizes. Die Abfrage sollte auf eine einzige Anweisung neu geschrieben werden, gemäß den Vorschlägen in zu diesem Thema.

Wenn Sie nicht explizit innerhalb einer Transaktion ausgeführt wird nur die Ausführung von Anweisung rückgängig zu machen.

Ich denke, diese Abfrage viel effizienter wäre, wenn es neu geschrieben wurde mit einem Cursor eines Single-Pass-Algorithmus. Sie würden Sie Cursor-Tabelle nach Länge, Breite, Businessname UND @phoneNumber bestellen. Sie würden die Zeilen einer nach dem anderen Schritt durch. Wenn eine Zeile die gleiche Länge hat, Breite, BUSINESS und Telefonnummer als die vorherige Zeile, dann löschen.

Ich glaube, Sie müssen ernsthaft Ihre methodolology betrachten.  Sie müssen beginnen in Sets zu denken (obwohl für Leistung, die Sie Batch-Verarbeitung benötigen, aber für Reihe gegen eine 17 Millionen Rekord Tabelle nicht rudern.)

Zuerst tun alle Ihre Aufzeichnungen Duplikate haben? Ich vermute nicht, so das erste, was Sie tun wan ist beschränken Sie Ihre Verarbeitung nur auf jene Datensätze, die Duplikate haben. Da dies ein großer Tisch ist und Sie können die Löschungen in den Reihen im Laufe der Zeit tun müssen, je nachdem, was andere Verarbeitung geht, ziehen Sie zuerst die Datensätze, die Sie mit in eine Tabelle ihrer eigenen beschäftigen möchten, dass Sie dann Index. Sie können auch eine temporäre Tabelle verwenden, wenn Sie dies, ohne jemals zur gleichen Zeit alle werden in der Lage sein zu tun, zu stoppen sie andere wiesen eine Tabelle in der Datenbank erstellen und am Ende fallen.

So etwas wie (Anmerkung Ich habe nicht den Index statments erstellen schreiben, ich meine, Sie, dass nachschauen selbst):

SELECT min(m.RecordID), m.longitude, m.latitude, m.businessname, m.phone  
     into  #RecordsToKeep    
FROM MyTable   m
join 
(select longitude, latitude, businessname, phone
from MyTable
group by longitude, latitude, businessname, phone
having count(*) >1) a 
on a.longitude = m.longitude and a.latitude = m.latitude and
a.businessname = b.businessname and a.phone = b.phone 
group by  m.longitude, m.latitude, m.businessname, m.phone   
ORDER BY CASE WHEN m.webAddress is not null THEN 1 ELSE 2 END,        
    CASE WHEN m.caption1 is not null THEN 1 ELSE 2 END,        
    CASE WHEN m.caption2 is not null THEN 1 ELSE 2 END



while (select count(*) from #RecordsToKeep) > 0
begin
select top 1000 * 
into #Batch
from #RecordsToKeep

Delete m
from mytable m
join #Batch b 
        on b.longitude = m.longitude and b.latitude = m.latitude and
        b.businessname = b.businessname and b.phone = b.phone 
where r.recordid <> b.recordID

Delete r
from  #RecordsToKeep r
join #Batch b on r.recordid = b.recordid

end

Delete m
from mytable m
join #RecordsToKeep r 
        on r.longitude = m.longitude and r.latitude = m.latitude and
        r.businessname = b.businessname and r.phone = b.phone 
where r.recordid <> m.recordID

Versuchen Sie auch eine andere Methode zu denken doppelte Zeilen zu entfernen:

delete t1 from table1 as t1 where exists (
    select * from table1 as t2 where
        t1.column1=t2.column1 and
        t1.column2=t2.column2 and
        t1.column3=t2.column3 and
        --add other colums if any
        t1.id>t2.id
)

Ich nehme an, dass Sie eine ganze Spalte id in der Tabelle haben.

Wenn das Gerät nicht sehr weit fortgeschritten Hardware hat, dann kann es SQL Server eine sehr lange Zeit, diesen Befehl zu vervollständigen. Ich weiß nicht genau, wie dieser Vorgang unter der Haube durchgeführt wird, aber auf Grund meiner Erfahrung diese effizienter gemacht werden könnte, indem die Datensätze aus der Datenbank zu bringen und in den Speicher für ein Programm, das eine Baumstruktur mit einem Entfernen doppelte Regel verwendet zum Einsetzen. Versuchen Sie, die Gesamtheit der Tabelle in chuncks Lesen (sagen 10000 Zeilen gleichzeitig) in ein C ++ Programm mit ODBC. Einmal in dem C ++ Programm verwenden und std :: map, wo Schlüssel ist der eindeutige Schlüssel und Struktur ist eine Struktur, die den Rest der Daten in Variablen hält. Schleife über alle Datensätze und führen Einsetzen in die Karte. Die Karte Insert-Funktion wird behandelt die Duplikate zu entfernen. Da Suche innerhalb einer Karte lg (n) Zeit viel weniger Zeit Duplikate zu finden, als Ihre while-Schleife verwenden. Sie können dann die gesamte Tabelle löschen und die Tupel zurück in die Datenbank aus der Karte hinzufügen, indem Insert-Abfragen und deren Ausführung über ODBC oder den Aufbau eine Textdatei Skript und es läuft in Management Studio.

Ich bin mir ziemlich sicher, dass ein negatory ist. Ansonsten, was der Punkt der Transaktionen wäre?

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top