Wydajne czytanie dużego pliku tekstowego i pobieranie danych z bazy

0

Używam netcore. Potrzebuję w aplikacji wczytywać wiersze z długiego pliku tekstowego. Struktura wiersza wygląda na tą chwile tak:
id_maszyny,id_polprodukt,id_operator,data_start,data_koniec

Za każdym razem może to być po kilka/kilkanaście tysięcy wierszy do przetworzenia (mozliwe ze jeszcze o czymś nie wiem i wierszy będzie kilkadziesiąt tysięcy). Dla każdego wczytanego z pliku tekstowego wiersza musze pobrac dodatkowe dane z bazy. Jak już sobie to dopasuję, to muszę otrzymane dane wrzucić do tabeli w bazie.
W jaki sposób zrobić to wydajnie? Czy wczytać od razu cały plik tekstowy do pamieci i w pętli sobie dopasować dane z bazy? Proszę o jakieś nakierowanie.

PS: czy jest jakiś sprytny sposób walidacji pliku tekstowego który mógłbym tu wykorzystać? Czyli jakies sprawdzanie zanim zaimportuję

0

Jeśli ten plik tekstowy ma taki format jak napisałeś to ja bym najpierw zrobił deserializację do listy za pomocą CsvHelper, w którym delimiterem byłby przecinek, a później w pętli wrzucał do bazy.

EDIT
Jeśli pętla będzie mało wydajna to można skorzystać SqlBulkCopy. Wszystko zależy od ORMa jakiego używasz, o ile w ogóle używasz...

0

@AdamWox: czyli masz na myśli:

  1. wczytuje wszystkie wiersze pliku do listy
  2. dla kazdego wiersza z listy robię selecty do bazy (może być kilka selectó lub joinów zeby pobrać to co potrzebuje) zeby pobrac brakujace dane
  3. znowy robie przejscie po kompletnej już liscie i dla kazdego elementu robie insert do mojej tabeli

Tak chce uzyc entityframeworkcore

0

Nie bardzo rozumiem co znaczy pobrać brakujące dane. Lecąc po liście rób pełne, potrzebne obiekty i insert. Po co ci ta druga pętla?

0

@AdamWox: "pobrać brakujące dane" - chodzi mi o to ze w pliku tekstowym mam tylko czesc potrzebnych danych, ktore maja wyladowac w mojej tabeli. Dla kazdego wiersza z pliku tekstowego, muszę dociągnąć troche danych z bazy danych i dopiero to jest dla mnie kompletny rekord który mogę wrzucic do mojej tabeli.

0

Wczytujesz cały plik -> w pętli zaciągasz z bazy to co potrzebujesz dodatkowo + insert

0

@kalimata: Jeśli chcesz to zrobić naprawdę wydajnie to nie ładuj całego pliku do pamięci na raz.

Jeśli danych jest naprawdę dużo użyj producer-consumer pattern. Wczytuj do takiej producer-consumer kolekcji plik linia po linii z jakimś limitem pojemności, na przykład 100 linii. Z drugiej strony równolegle wyciągaj te linijki, parsuj i wykonuj zapytania do bazy danych.

FileStream --> [producer-consumer collection (max 100 lines)] --> Parse  --> Process

Jesli się nie mylę to można do tego użyć (chyba niedawno dodanej) klasy Channel.

0

Polecam użycie IAsyncEnumerable
Otwierasz stream z pliku i czytasz po linii.
Przy odczycie z bazy możesz zrobić batche po ile tam potrzebujesz. Wszystko asynchronicznie i nie zajmuje dużo pamięci

1
  1. jaka baza, jak robisz inserty
  2. najważniejsze! nie pojedynczy insert dla każdej linii a wcześniej jeszcze select bo to nigdy nie będzie wydajne
  3. szukaj pod hasłem (jak już @AdamWox pisał) bulk copy albo bulk insert dla Twojej bazy i sposobu w jaki dodajesz dane i rób inserty grupowe po 1000 na raz
  4. do odczytu z pliku linia po linii nawet nie ma sensu szukać czegoś szybszego niż
System.IO.StreamReader file = new System.IO.StreamReader(@"c:\test.txt");  
while((line = file.ReadLine()) != null)  
{  
  //tu robisz coś z linią
}  
file.Close();  

4. ostatnie ale równie ważne jak 1 - najpierw robisz insert "surowych" danych z pliku do tabeli tymczasowej czy dodatkowej a dopiero potem robisz za jednym zamachem insert do tabeli docelowej na zasadzie `insert into docelowa_tabela select x.*, y.* from tymczasowa x join pozostala y`

Natomiast jeśli baza to mssql i jeśli masz fizyczny dostęp do maszyny na której on stoi to możesz pominąć kroki 1-3 i zaczytać taki plik od razu do bazy - przykład https://stackoverflow.com/questions/15242757/import-csv-file-into-sql-server i opis https://docs.microsoft.com/en-us/sql/t-sql/statements/bulk-insert-transact-sql?view=sql-server-ver15
0

Jeśli to jest bardzo duży plik to trzeba pamiętać o tym, że już sam DbContext tracking potrafi zabić wydajność. Najlepiej jest dla każdego batcha (np 1000) rekordów użyć nowej instancji kontekstu. Można też kombinować z czyszczeniem change trackera.

1 użytkowników online, w tym zalogowanych: 0, gości: 1