W jaki sposób uporządkować filtrowanie listy z bazy w funkcji?

0

Witam,

Chciałbym Was praktyków zapytać, co jest bardziej ergonomiczne. Tworzę funkcję. I teraz co będzie bardziej wydajne. Przekazać do funkcji listę warunków do zapytania, w funkcji odpytać bazę danych, iterować po wyniku i wyrzucić odpowiednio sformatowaną listę? Czy też do funkcji przekazać pojedynczą daną, (kod produktu, id grupy itp.), i w funkcji odpytać o jeden rekord i tylko ten rekord zwrócić?
Zastanawiam się, które rozwiązanie będzie lepsze.

0

@Buster: Pod jakim względem wydajniejsze? Czasu, pamięci?
Pod względem pamięci raczej ta druga opcja, co do czasu to zmierz czas wykonania w obu przypadkach i będziesz wiedział, ale tu również
stawiałbym na drugą opcję.

0

Pytam ogólnie. Nie o jakiś konkretny parametr wydajnościowy. Pytam raczej, jak to praktycy robią.

0

@Buster: Jeżeli nie jesteś w stanie przeanalizować kodu pod względem wydajności - kod wysyła zapytania do baz danych, jakichś zewnętrznych serwisów itp. i po prostu nie jesteś w stanie dokonać takiej analizy - to skorzystaj z profilera, albo używaj funkcji do mierzenia czasu wewnątrz kodu - to dla prostszych przypadków.
Ewentualnie pod linuxem masz taką komendę jak "time", która przyjmuje jako argument program, wykonuje go i wypluwa czas jego wykonania.
Z tego korzystam, może ktoś będzie miał jeszcze jakieś inne propozycje.

5

jeśli potrzebujesz mieć listę obiektów z bazy to pobieranie ich pojedynczo to najgorsze co możesz zrobić

2

Pytanie jest w ogóle źle zadane.

Czemu implementacja filtrowania danych z bazy jest postawiona z punktu widzenia jakiejś funkcji?

Jeśli się zastanawiasz w jaki sposób do tego podejść, to jest jasny sygnał że powinieneś najpierw całość napisać imperatywnie "od góry do dołu", można powiedzieć "na pałę" i potem to odpowiednio zrefaktorować, nadać odpowiednie nazwy, wydzielić odpowiednie funkcje. Moim zdaniem nie potrzebnie skupisz uwagę na tej funkcji, zamiast na perspektywie całościowej.

3

Pytanie jest zadane w pokrętny rozgałęziony sposób, ale ogólnie zakłada się że im mniej odczytasz z bazy danych tym lepiej i im więcej filtrowania przerzucisz na bazę danych tym lepiej wiec:

  • napisac zapytanie które wyciąga tylko potrzebne rekordy -> dobrze
  • napisac zapytanie które wyciąga wszystkie rekordy i filtruje w aplikacji -> źle
3

Po to masz SQL, żeby na niego zrzucić obowiązek przefiltrowania danych.

Jeśli jesteś w stanie stworzyć tak zapytanie, żeby z bazy dostać tylko to, co Cię interesuje, to staraj się iść w tym kierunku. Oczywiście, nie zawsze się tak da - ale jeśli masz możliwość, to stwórz SELECT xxx WHERE yyy AND yyy2 AND yyy3 i niech baza się męczy. Bo idąc w kierunku przez Ciebie proponowanym - zamiast SQL, możesz sobie trzymać dane w pliku tekstowym, potem go wczytać i w pętli sprawdzić, czy każdy element jest tym, którego szukasz. Da się, ale to zupełnie bez sensu. I nie dotyczy to tylko Delphi, ale ogólnie jest to kwestia architektury - po to masz bazę, żeby ona odwaliła czarną robotę.

A co do samej wydajności - pamiętaj, ze aplikację możesz odpalać na różnych maszynach i np. u Ciebie na super kompie (ale kiepskim SQL) może nawet (małe szanse, ale jest taka opcja) szybciej chodzić filtrowanie po stronie apki. Ale ktoś inny to odpali na jakimś C2D, które będzie podłączone do bardzo wydajnego serwera, na którym stoi SQL i wtedy wyniki mogą być zupełnie odmienne. Zresztą - jak SQL zacznie zamulać, to się zmienia serwer/dokłada RAM, możesz stworzyć indeksy lub w inny sposób bazę zoptymalizować.

2

Jedno , dobrze zrobione query działające na dobrze zbudowanej bazie i zwracające "n" wyników, będzie lepsze wydajnościowo od "n" query zwracających po jednym wyniku

0

Właśnie tak mi się wydawało, że lepiej, żeby SQL filtrował. No ale może zdradzę trochę szczegółów. Dokładnie tworzę aplikację, która będzie pobierać z bazy jednego systemu ERP informacje (towary, kontrahenci, dokumenty) i na podstawie tych danych tworzyć plik do zaimportowania w drugim systemie ERP. Borykam się z częścią migracji kartotek towarowych. Migracja może pobrać wszystkie kartoteki, albo o zadanych kodach (wpisanych w pole TMemo), albo o wybranych grupach (grupy zaznaczone w TCheckBoxList). I teraz zastanawiam się czy przekazać do funkcji odpowiednią listę parametrów (tablica kodów towarów, tablica wybranych grup) i wtedy całą operację SQL i iterację wyników zrobić w funkcji i zwrócić TStringList (akurat w tym przypadku). Czy też do funkcji przekazać pojedynczy kod towaru, lub pojedynczą grupę, a samo zapytanie SQL i jego wynik iterować w procedurze głównej. Z tymże, że przy przekazaniu grupy, to kurczę i tak w wyniku zapytania, nie otrzymam pojedynczego produktu, tylko listę produktów. A co za tym idzie, najpierw musiałbym wykonać zapytanie w głównej procedurze, gdzie otrzymałbym listę odpowiednich kodów i dopiero wtedy przekazywać je do funkcji, która znowu pobierałaby dane o pojedynczym produkcie. To już wydaje mi się lepiej pierwsze rozwiązanie. Jak sądzicie?

0

Aby to było jakoś spójne ja bym to zrobił mniej więcej tak:
W systemie eksportującym dane:

  1. Tworzę listę rekordów które będę chciał wyeksportować. Będzie to jedno query, bez znaczenia czy będzie to jeden rekord, wiele, grupa. To już musisz tylko ustalić jakieś warunki w zapytaniu aby takie rekordy otrzymać jak chcesz. Podsumowując: jedno query które utworzy jednego dataseta. No dobra, powiedzmy że będzie to kilka, kilkanaście rekordów plus jakaś grupa dodatkowo, gdyby zapytanie sql miało się jakoś bardzo mocno rozbudować a pojedyncze miałyby być bardzo proste to można przecież zrobić więcej takich zapytań a dane wyjściowe zapisać w czymś na kształt listy.
  2. W tej mojej liście zapamiętałbym tylko id rekordu (towaru, kontrahenta, dokumentu) bo na teraz tylko to jest potrzebne i na koniec przeniósł do jakiegoś dataseta ale to niekoniecznie.

Mała uwaga: jeśli zapytania mają być bardzo podobne można dla optymalizacji utworzyć widoki w bazie danych. Jeśli nie korzystałeś to poczytaj sobie o nich.

Gdy mam już te dane w datasetcie to należy w pierwszej kolejności zastanowić się w jaki sposób będziesz kojarzył rekordy w dwóch niezależnych systemach.
Jest kilka opcji, omówię je bardzo pokrótce:

  1. najprostsza, w systemie importującym ten sam towar / kontrahent / dokument ma te same id w bazie lub w ostateczności jest pole z id z naszej bazy (nazwijmy je polem z obcym Id).
  2. średnio skomplikowana, nie ma w bazie docelowej pola z Id naszego rekordu ale możemy towary rozpoznać np. po unikatowym kodzie EAN, kontrahentów po NIP-ie a dokumenty po numerze. Jest to do przyjęcia ale na pewno będą problemy (nie wszystkie towary mają kod EAN, kontrahenci mogą mieć różnie zapisany NIP, chociaż można to wyeliminować poprzez usuwanie wszystkiego poza cyframi (pewien problem przy nipie EU), numery dokumentów nie powinny sprawić problemów).
  3. nie ma żadnego powiązania, towary nie mają kodów EAN lub innych unikatowych, kontrahenci są z unikatowym nipem ale są też i detaliści i jest jedenastu Janów Kowalskich, dokumenty powinny mieć jednak unikatowe numery. Tutaj będzie trudno, można pokusić się o jakieś filtrowanie aby znaleźć odpowiednik naszego rekordu ale bez mechanizmu do ręcznego kojarzenia nierozpoznanych rekordów się nie obędzie.

I teraz w zależności od scenariusza:

  1. po prostu eksportujemy dane do pliku, rekord po rekordzie,
  with qryTowary, SQL do
  begin
    Clear;
    Close;
    Add('SELECT Id FROM Towary WHERE Warunek = :Warunek');
    ParamByName('Warunek').AsString := warunek;
    Open;
    First;
  end;

  while not qryTowary.Eof do
  begin
    mmoEksport.Items.Add(qryTowary.FieldByName('Id').AsString); // dla przykładu zapis danych do memo
    qryTowary.Next;
  end;
  1. tutaj także może być prosto jeśli zrzucimy odpowiedzialność za weryfikację danych oraz zarządzanie duplikatami na aplikację importującą. W zasadzie eksport może wglądać jak powyżej, tylko zamiast Id eksportujemy w pliku EAN, NIP lub numer dokumentu. Jeśli jednak system importujący w aplikacji docelowej nie ma mechanizmów weryfikujących to rozwiązanie trzecie jest także dla ciebie.
  2. ten przypadek wymagać będzie:
    a. dostęp do bazy danych systemu importującego, zerknijmy na przykład z towarami:
  with qryTowary, SQL do // na początek utworzymy sobie listę towarów do eksportu w naszej aplikacji
  begin
    Clear;
    Close;
    Add('SELECT Id, Kod, Nazwa, EAN, Opis FROM Towary WHERE Warunek = :Warunek');
    ParamByName('Warunek').AsString := warunek;
    Open;
    First;
  end;

  // sam ustalisz po jakich polach chcesz poszukiwać, powiedzmy jako przykład tak (bez sensu):
  while not qryTowary.Eof do // no i teraz przejedziemy się przez nie do końca
  begin
    with qryEksport, SQL do
    begin
      Clear;
      Close;
      Add('SELECT Id, Kod, Nazwa, EAN, Opis FROM Towary WHERE Id = :Id or Kod = :Kod or Nazwa = :Nazwa or EAN = :EAN or Opis like :Opis');
      ParamByName('Id').AsInteger := qryTowary.FieldByName('Id').AsInteger;
      ParamByName('Kod').AsInteger := qryTowary.FieldByName('Kod').AsInteger;
      ParamByName('Nazwa').AsString := qryTowary.FieldByName('Nazwa').AsString;
      ParamByName('Opis').AsString := '%' + qryTowary.FieldByName('Opis').AsString + '%'; // tą linijką zabijesz wydajność ale co zrobić
      Open;
      First;
    end;


  // teraz opis
  jeśli zapytanie powyżej znajdzie jakieś rekordy to
    - jeśli jest to jeden rekord to dodaj go do pliku eksportowanego
  jeśli jest ich więcej
    - to należy utworzyć formę na której wyświetlisz parametry twojego eksportowanego towaru (aby użytkownik jak najwięcej o nim wiedział) oraz tabelę z rekordami znalezionymi
  użytkownik poprzez kliknięcie na odpowiedni rekord powinien oznaczyć ten odpowiedni
  (wypadałoby albo automatycznie zapamiętać takie powiązanie albo chociaż zapytać czy to zapamiętać na przyszłość)
  po skojarzeniu dodać rekord do pliku eksportowanego
  w przypadku braku skojarzenia określić co dalej:
    1. zapamiętać i na koniec wyświetlić z tego raport,
    2. jeśli to możliwe to od razu utworzyć rekord w systemie docelowym i wykonać eksport.
    

    qryTowary.Next;
  end;

Jak widać, nie będzie to tak bardzo proste i zapewne mogą być ograniczenia które nie pozwolą na rozwiązanie problemu (np. brak dostępu do systemu docelowego).
Może coś więcej można by napisać gdybyś podał więcej informacji.

0

@robertz68: Oooo i bardzo fajnie wyjaśniłeś. Aczkolwiek wynikiem ma nie być migracja online, tylko plik z danymi do zaimportowania w drugim systemie (np. InsERT epp, czy jakiś inny XML). Ale to już mi się rozjaśniło. Heh.

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