2012-03-30 8 views
9

Próbuję użyć Dapper do obsługi dostępu do danych dla mojej aplikacji serwera.Prawidłowa metoda usuwania ponad 2100 wierszy (według identyfikatora) za pomocą narzędzia Dapper

Moja aplikacja serwerowa ma inną aplikację, która zapisuje rekordy do mojej bazy danych z szybkością 400 na minutę.

Moja aplikacja pobiera je partiami, przetwarza je, a następnie usuwa z bazy danych.

Ponieważ dane nadal są przesyłane do bazy danych podczas przetwarzania, nie mam dobrego sposobu, aby powiedzieć delete from myTable where allProcessed = true.

Jednak znam wartość PK wierszy do usunięcia. Tak więc chcę zrobić delete from myTable where Id in @listToDelete . Problem polega na tym, że jeśli mój serwer nie działa nawet przez 6 minut, to mam ponad 2100 wierszy do usunięcia.

Ponieważ Dapper pobiera moją @listToDelete i zamienia każdą z nich w parametr, moje wywołanie do usunięcia nie powiedzie się. (Powoduje, że moje dane są usuwane, aby uzyskać jeszcze więcej.)

Jaki jest najlepszy sposób radzenia sobie z tym w Dapper?

UWAGI: Przyjrzałem się oznaczonym wartościom parametrów, ale z tego, co widzę, nie są one zbyt liczne: performant. Ta część mojej architektury to szyjka butelki mojego systemu i muszę bardzo szybko uzyskać bardzo.

+0

@marc_s - Nie potrzebuję **, aby przekazać tyle parametrów ... Ale muszę usunąć wiele wierszy według ID PK. Jednak robię to w porządku przeze mnie. Mówię bystrzej, żeby usunąć każdy wiersz z mojej "@ listy". To Dapper tworzy parametry każdego elementu na mojej liście. – Vaccano

+0

Trudno powiedzieć z tej małej informacji, ale dlaczego nie można użyć tych kryteriów selekcji dla partii jako kryteriów usuwania. Lub przetestuj flagę w MyTable, ustaw ją w "przetwarzaniu", a następnie użyj. To nie jest świetne, ale będzie znacznie szybsze niż usuwanie jeden po drugim. To nie będzie szokująco złe, nawet z 10.000 płyt. –

+0

Moje kryteria partii nie są bardzo wydajne. Więc wolałbym tego nie używać. Mogłem dodać przetworzoną flagę, ale w tym celu potrzebowałbym sposobu na wywołanie wierszy, aby dodać przetworzoną flagę do. Jeśli mogę je wezwać, równie dobrze mogę je usunąć. (Mogę dodać flagę do oznaczenia "batched" w wybranym czasie, ale wolałbym nie.) – Vaccano

Odpowiedz

12

Jedną z opcji jest utworzenie tabeli tymczasowej na serwerze, a następnie użycie funkcji ładowania zbiorczego w celu przesłania wszystkich identyfikatorów do tej tabeli jednocześnie. Następnie użyj klauzuli join, EXISTS lub IN, aby usunąć tylko rekordy przesłane do tabeli tymczasowej.

Ładunki masowe są dobrze zoptymalizowaną ścieżką w SQL Server i powinny być bardzo szybkie.

Na przykład:

  1. wykonać instrukcję CREATE TABLE #RowsToDelete(ID INT PRIMARY KEY)
  2. użyć Bulk Load do wstawienia klucze do #RowsToDelete
  3. Execute DELETE FROM myTable where Id IN (SELECT ID FROM #RowsToDelete)
  4. Execute DROP TABLE #RowsToDelte (tabela zostanie również automatycznie usuwane po zamknięciu sesja)

(Zakładając Wytworny) przykładowy kod:

conn.Open(); 

var columnName = "ID"; 

conn.Execute(string.Format("CREATE TABLE #{0}s({0} INT PRIMARY KEY)", columnName)); 

using (var bulkCopy = new SqlBulkCopy(conn)) 
{ 
    bulkCopy.BatchSize = ids.Count; 
    bulkCopy.DestinationTableName = string.Format("#{0}s", columnName); 

    var table = new DataTable();      
    table.Columns.Add(columnName, typeof (int)); 
    bulkCopy.ColumnMappings.Add(columnName, columnName); 

    foreach (var id in ids) 
    { 
     table.Rows.Add(id); 
    } 

    bulkCopy.WriteToServer(table); 
} 

//or do other things with your table instead of deleting here 
conn.Execute(string.Format(@"DELETE FROM myTable where Id IN 
            (SELECT {0} FROM #{0}s", columnName)); 

conn.Execute(string.Format("DROP TABLE #{0}s", columnName)); 
+0

Dodano przykładowy kod, który faktycznie używa SqlBulkCopy, może być dostosowany do szerszych tabel przez dodanie więcej kolumn do temp. tabela i tabela danych. –

5

Aby ten kod działał, zrobiłem się ciemny.

Ponieważ Dapper czyni moją listę parametrami. I SQL Server nie może obsłużyć wielu parametrów. (Nigdy wcześniej nie potrzebowałem nawet dwucyfrowych parametrów). Musiałem przejść z Dynamic SQL.

Tak tu było moje rozwiązanie:

string listOfIdsJoined = "("+String.Join(",", listOfIds.ToArray())+")"; 
connection.Execute("delete from myTable where Id in " + listOfIdsJoined); 

Przed każdy chwyta swoje pochodnie i widły, pozwól mi wyjaśnić.

  • Ten kod jest uruchamiany na serwerze, którego jedynym źródłem danych jest kanał danych z systemu Mainframe.
  • Lista, którą dynamicznie tworzę, jest listą longs/bigints.
  • Długie/duże znaki pochodzą z kolumny Tożsamość.

Wiem, że konstruowanie dynamicznego SQL jest złe juju, ale w tym przypadku po prostu nie widzę, jak prowadzi to do zagrożenia bezpieczeństwa.

+2

[Little Bobby Tables] (https://xkcd.com/327/) say 'G'Day! –

+3

@ Pure.Krome prawdopodobnie jego 'listOfIds' jest typu' List 'dlatego nie ma potrzeby dezynfekcji wejść – Zac

+0

Prawdopodobnie ... ale założenia są źródłem wszelkiego zła. Po drugie, jest to ogólna sugestia => tworzenie skryptu sql, w locie i na łańcuch ... może się bardzo źle skończyć :) –

2

Wytworny wniosek na Liście parametru obiektu mający jako właściwość więc w powyższym przypadku wykaz obiektu posiadającego Id jako właściwość będzie działać.

connection.Execute("delete from myTable where Id in (@Id)", listOfIds.AsEnumerable().Select(i=> new { Id = i }).ToList()); 

To zadziała.

Powiązane problemy