Wątek zablokowany 2014-06-18 17:08 przez furious programming.

Tworzenie "koszyka" bazy danych.

0

Witam. Jako, że to mój pierwszy post - Witam wszystkich :D
W delphi jestem dopiero początkujący wiec proszę o wyrozumiałość. Otóż pisze sobie program używający baz danych. Ma on działać tak : Mam tabele z produktami, każdy z produktów ma swój unikalny kod, który też jest odgórnie zdefiniowany w tej tabeli. I chcę zrobić tak, że w polu edit będę sobie wpisywał kod tego produktu oraz ilość sztuk. I po naciśnięciu przycisku ten produkt będzie się dodawał do tak jakby "koszyka" czyli do 2 tabeli która będzie osobno zapisywana. I takim moim największym problem jest to przeniesienie wybranych rekordów z tabeli 1 do 2 czyli tego "koszyka". Bardzo bym prosił o w miarę prosty przekaz gdyż jeszcze nie potrafię wiele :D

1

Nic nie musisz przenosić. Tabela "Koszyk" ma wyglądać następująco: Id_pozycji_koszyka, Id_użytkownika, Id_Produktu, Ilość.

0

Ale chodzi mi o to jak przenieść te dane. Bo tak, w 1 tabeli mam nazwa prod., kod, cena kat., cena dyst. a w 2 tabelce ma być tylko to co wybiore z 1 tabeli a dane mają byc tam takie : nazwa, cena kat, ilość.

0

Zapomniałeś do tabeli 1 dodać pole Id_Produktu.
Select może działać na kilku tabelach jednocześnie.

0

tylko co to jest i jak tego użyć ? Wiem, że to głupie pytanie ale jestem dopiero na poziomie podstawowym i jeszcze nie znam tak dobrze delphi.

0

Może zacznij od pokazania co dotychczas zrobiłeś aby nie wyszło że każdy z nas rozmawia o czymś innym.

0

na razie mam tylko pokazywanie się tabeli która jest już przeze-mnie utworzona w excel i wyeksportowana do *.db. Jest to bardzo mało, bo na razie zbieram informacje i szukam jakiegoś gotowego programu który wykorzystuje podobne działanie. I właśnie po to tutaj pytam, bo jestem w tym zielony. Dotąd pisałem jakieś programy-zabawki, wiec z takim czymś nie mam doświadczenia

1

To jak jesteś taki zielony to idź przeczytaj kurs

0

Czytałem Rozdział 16 i szukałem informacji na ten temat ale nadal nie wiem jak przenieść wybrane rekordy z 1 tabeli do 2.

0

Jeżeli działasz na bazach danych to podstawą jest SQL. Kilka godzin nauki powinno wystarczyć aby opanować podstawy. Do przenoszenia danych wystarczy wywołać INSERT z odpowiednim warunkiem WHERE. Z Delphi skończyłem przygodę wiele lat temu, ale chyba większość obecnych uniwersalnych języków programowania obsługuje w jakiś sposób standard SQL. Zastanów się tylko czy rzeczywiście musisz przenosić dane między tabelami, może wystarczy utworzenie miedzy nimi relacji (jak ktoś wspomniał wcześniej).

1

Moderator! Wszystkie odpowiedzi w tym wątku są nie na temat...

Wiszu, pokaż ten swój projekt i nie zapomnij o bazie danych (co to jest *.db??).
A dodatkowo:

  • jaka wersja Delphi (7, 2007, XE, XE2, itd.)?
  • którego Delphi używasz (Proffesional, Enterprise, Architect)?
    To są istotne informacje, bo nie wszystkie możliwości są we wszystkich wersjach...

Jak podeślesz to Ci pokażę dokładnie co i jak, jak nie podeślesz - to się nie dogadasz.
Twój wybór.

0

Ok. Mam delphi 7 enterprise. *.db to jest rozszerzenie bazy danych na którym pracuje. Nie wiem czy jest sens pokazywać bo sam projekt na razie to jest tylko wyświetlanie gotowej tabelki pokazane właśnie w tym tutorialu.
KOD, próbowałem tutaj zrobić funkcję dodawania nowej tabeli, cala reszta dzieje sie na komponentach

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, DB, Grids, DBGrids, DBTables, StdCtrls;

type
  TForm1 = class(TForm)
    Table: TTable;
    DBGrid1: TDBGrid;
    DataSource1: TDataSource;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
 Table.DatabaseName := 'MyDatabase';
  Table.TableType := ttParadox;
  Table.TableName := 'Koszyk';
  if not Table.Exists then
  begin
    with Table.FieldDefs do
    begin
      with AddFieldDef do
      begin
        Name := 'Nazwa';
        DataType := ftString;
        Required := True;
      end;
      with AddFieldDef do
      begin
        Name := 'KOD';
        DataType := ftString;
        Required := True;
      end;
      with AddFieldDef do
      begin
        Name := 'Cena';
        DataType := ftInteger;
        Required := True;
      end;
      with AddFieldDef do
      begin
        Name := 'ilosc';
        DataType := ftInteger;
        Required := True;
      end;
    end;
    Table.CreateTable;
  end;
end;

end.

dodanie znacznika <code class="delphi"> - furious programming

0

Prosiłem o projekt, a nie o coś takiego...

Mój kod źródłowy + baza danych w załączniku do tego postu.
Przykładowa aplikacja korzysta z ADO i bazy MS Access.
Mam tam problemy z odświeżaniem sumy koszyka, wygląda na to że zdarzenia w ADO działają wcześniej niż sam zapis do bazy danych.
Z ADO pożegnałem się dawno temu, tak więc nie jestem specjalista...
A idea wydawała się słuszna ;-)

Procedura dodawania danych do tabeli koszyk wygląda tak:

procedure TfMain.actAddExecute(Sender: TObject);
var
  lGO  : Boolean;
  lVal : string;
  lQty : Currency;
begin
  if not adqProdukty.IsEmpty then
  begin
    adqKoszyk.DisableControls;
    try
      // pobierz ilość za pomoca InputQuery
      repeat
        lGO := InputQuery('Koszyk', 'Podaj ilość produktów', lVal);
        if not lGO then
          Exit
        else
          lQty := StrToCurrDef(lVal, 0);

        // sprawdź czy podana ilośc jest większa od zera
        lGO := lQty > 0;
        if not lGO then
          Application.MessageBox('Proszę podać ilość większą od zera.', 'Koszyk', MB_OK + MB_ICONWARNING);

      // powtarzaj do momentu podania prawidłowej wartości lub anulowania dialogu
      until lGO;

      if lGO then
      begin
        // jeśli znaleziono produkt w koszyku, to edytuj ten wiersz
        if adqKoszyk.Locate('KodProduktu', adqProdukty.FieldByName('KodProduktu').Value, []) then
          adqKoszyk.Edit
        else
          // w przeciwnym razie dodaj wiersz do koszyka
          adqKoszyk.Append;

        adqKoszyk.FieldByName('KodProduktu').Value := adqProdukty.FieldByName('KodProduktu').Value;
        adqKoszyk.FieldByName('Cena').Value        := adqProdukty.FieldByName('Cena').Value;
        // dodaj ilość do istniejącej wartości
        adqKoszyk.FieldByName('Ilosc').Value       := adqKoszyk.FieldByName('Ilosc').AsCurrency + lQty;
        // zapisz zmiany
        adqKoszyk.Post;
      end;
    finally
      adqKoszyk.EnableControls;
    end;
  end;
end;

Obliczanie wartości wiersza koszyka zrealizowałem za pomocą obsługi zdarzenie BeforePost, a więc jest obliczana automatycznie zawsze przed zapisem. Dzięki czemu można zmienić ilość lub cenę w koszyku i wszystko policzy się automatycznie.
Kod zdarzenia BeforePost jest banalny:

procedure TfMain.adqKoszykBeforePost(DataSet: TDataSet);
begin
  DataSet.FieldByName('Wartosc').Value := DataSet.FieldByName('Cena').AsCurrency * DataSet.FieldByName('Ilosc').AsCurrency;
end;

Resztę można zobaczyć w projekcie.

3

W Delphi piszę, a raczej pisałem programy przez jakieś 15 lat i zawsze byłem przeciwnikiem bezpośredniego używania komponentu TTable.
Dlaczego?
Gdyż ma się bardzo małą kontrolę nad bazą danych!
Oczywiście małe programiki będą działały, DBGrid będzie wyświetlał tabelki i będzie prawie gites, ale .....prawie robi różnice ;-) .

Prosty przykład.
Masz aplikację, która łączy się z bazą będącą częścią ogromnej bazy danych, rozproszonej po kilkunastu serwerach na terenie całej Polski. (Dane między serwerami są wymieniana za pomocą mechanizmów replikacji i procedur składowanych dual commit) Załóżmy, że Twój koszyk wypełniany będzie towarami, których listę aktualizuje równocześnie kilkudziesięciu (kilkuset) użytkowników. Wolisz, by o integralność tej bazy dbała aplikacja klienta, czy serwer(y) bazy danych?
No właśnie!
Serwer, będący nawet częścią systemu rozproszonego lub częścią klastra, zadba o to najlepiej!

Dlatego proponuję Ci zweryfikować swój pogląd dotyczący obsługi baz danych z poziomu aplikacji napisanych w Delphi i to bez względu na to, czy piszesz programy client-server, czy trójwarstwowe (z wykorzystaniem pośrednio serwera aplikacji).

Proponuję byś swoje aplikacje pisał tak:

Komponent TQuery i tutaj piszesz kod SQL.
Komponent ten poprzez TDataSource obsługuj sobie w gridach, polach, comoxach, czy jak tam chcesz.
Gdy przeprowadzasz edycję tabeli, dodajesz, usuwasz rekordy, to najlepiej wywoływać procedurę składowaną z odpowiednimi parametrami i przechwytywać zwracaną wartość. Będzie to najczęściej ID nowo wprowadzonego rekordu lub numer i komunikat błędu serwera. Ponadto, przy takim podejściu możesz na serwerze bazy danych robić cuda-cudeńka np. sprawdzać walidację przesłanych parametrów, wykonać 101 dodatkowych akcji (np. Dla akcji "Kup wódkę, sztuk 2 flaszki, po cenie 19,90zł" możesz sprawdzać, czy zarejestrowany user ma 18-cie lat i jeżeli nie, to wyszukać najbardziej chude mleko w najdalszym magazynie i mu to wstawić do Koszyka ;-)

Przykładowy kawałek kodu do obsługi tabelki Klient

w module typu TDataMod stawiasz komponent typu TQuery i komponenty TStoredProc, które będą wywowływały odpowiednie procedury z bazy danych.

de03080abe.png

unit DataModKlient;

interface            

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  Db, DBGrids, DBTables, StdCtrls, MyUnit;

//--------------------------------------------------------------------------
//  definije zmienne w ktorych przechowuje wartosci o ktore pytam baze danych
//--------------------------------------------------------------------------
type
  TKlientSQLParam = class
    kod_klientaOd, kod_klientaDo, nazwa,
    miejscowosc, ulica, nr_domu, nr_lokalu, kod_pocztowy,


// tutaj sobie zdeklaruj wszystko, po czym chcesz wyszukiwać
    ......

    SQLCaption: String;
    LowerIsUpper: String;
    procedure ClearSQLAllParams;
    procedure LoadSQLBuffer;
    procedure SaveSQLParam;
  end;


  TDMKlient = class(TDataModule)
    qKlient: TQuery;
    qKlientkod_klienta: TIntegerField;
    qKlientnazwa: TStringField;
    qKlientmiejscowosc: TStringField;
    qKlientulica: TStringField;
    qKlientnr_domu: TStringField;
    qKlientnr_lokalu: TStringField;
    qKlientkod_pocztowy: TStringField;

   //.........

    procedure DMKlientCreate(Sender: TObject);
    procedure CachedQueryUpdateErrorHandler(DataSet: TDataSet;
      E: EDatabaseError; UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction);
    procedure qKlientNewRecord(DataSet: TDataSet);
    procedure qKlientBeforeDelete(DataSet: TDataSet);
    procedure qKlientBeforePost(DataSet: TDataSet);
    procedure qKlientBeforeEdit(DataSet: TDataSet);
    procedure qKlientUpdateRecord(DataSet: TDataSet;
      UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction);
    procedure qKlientAfterPost(DataSet: TDataSet);
    procedure qKlientBeforeInsert(DataSet: TDataSet);
  private
    FLastKlientSerial: Integer;
  public
    property LastKlientSerial: Integer read FLastKlientSerial write FLastKlientSerial;

    procedure KlientSQLLoad(var QueryKlient: TQuery);
    procedure KlientWHERELoad(var QueryKlient: TQuery; var QueryBuffer: TKlientSQLParam);
  end;

//--------------------------------------------------------------------------
//  Deklarujesz zmienną, która przechowuje twoje parametry szukanych wartości
//--------------------------------------------------------------------------
var
  KlientSQLParam: TKlientSQLParam;
  DMKlient: TDMKlient;

implementation

uses DataMod, RetMesAnalyzer, Klient_S;

//--------------------------------------------------------------------------
//  RetMesAnalyzer jest formularzem, który analizuje zwrotne komunikaty z serwera bazy danych i
// ewentualnie wyświetla  błędy
// Klient_S jest formularzem na którym zdefiniujesz, czego szukasz w bazie danych



//--------------------------------------------------------------------------


{$R *.DFM}

// inicjowanie "kontenera" do przechowywania naszych zmiennych, gdy "rusza" program
procedure TDMKlient.DMKlientCreate(Sender: TObject);
begin
  KlientSQLParam.ClearSQLAllParams;
end;


/// ta procedura "czyści" zmienne, które definiją parametry naszyego zapytania SQL
procedure TKlientSQLParam.ClearSQLAllParams;
begin
  kod_klientaOd := '';
  kod_klientaDo := '';
  nazwa := '';
  miejscowosc := '';
  ulica := '';
  nr_domu := '';
  nr_lokalu := '';
  kod_pocztowy := '';


  SQLCaption := '';
  LowerIsUpper := 'T';
end;
// SQLCaption bedzie przechowywało komunikat, który możesz sobie gdzieś wyświetlić, o tym, czego użytkownik szuka
// LoverIsUpper - to informacja, czy baza ma rozróżniać małe/duże litery



// ta procedura ładuje na formularza Klient_S wszystkie parametry ostatniego szukania
procedure TKlientSQLParam.LoadSQLBuffer;
var
begin
  with S_Klient do
  begin
  CEkod_klientaOd.Text := kod_klientaOd;
  CEkod_klientaDo.Text := kod_klientaDo;
  Enazwa.Text := nazwa;
  Emiejscowosc.Text := miejscowosc;
  Eulica.Text := ulica;
  Enr_domu.Text := nr_domu;
  Enr_lokalu.Text := nr_lokalu;
  Ekod_pocztowy.Text := kod_pocztowy;


  cbLowerIsUpper.State := StateOfTakNieCheckBox(LowerIsUpper);
  end;
end;
 

d76c89a50f.png


// a ta zapisuje je z forumarza do naszych zmiennych 
procedure TKlientSQLParam.SaveSQLParam;
begin
  with S_Klient do
  begin

  kod_klientaOd := CEkod_klientaOd.Text;
  kod_klientaDo := CEkod_klientaDo.Text;
  nazwa := Enazwa.Text;

  miejscowosc := Emiejscowosc.Text;
  ulica := Eulica.Text;
  nr_domu := Enr_domu.Text;
  nr_lokalu := Enr_lokalu.Text;
  kod_pocztowy := Ekod_pocztowy.Text;

  LowerIsUpper := ValueOfTakNieCheckBox(cbLowerIsUpper.State);
  end;
end;


// procedurka, ktora przygotowuje kod zapytania SQL, które skierujemy do bazy
// Zwróć uwagę, że parametrem jest obiekt klasy TQury, na którym poprzez odwołanie .SQL masz dostęp do tekstu wywołania SQL
procedure TDMKlient.KlientSQLLoad(var QueryKlient: TQuery);
begin
  QueryKlient.Close;
  QueryKlient.SQL.Clear;

  QueryKlient.SQL.ADD('SELECT klient.kod_klienta, ');
  QueryKlient.SQL.ADD('klient.nazwa, ');
  QueryKlient.SQL.ADD('klient.miejscowosc, ');
  QueryKlient.SQL.ADD('klient.ulica, ');
  QueryKlient.SQL.ADD('klient.nr_domu, ');
  QueryKlient.SQL.ADD('klient.nr_lokalu, ');
  QueryKlient.SQL.ADD('klient.kod_pocztowy, ');

  //.......

  QueryKlient.SQL.ADD('FROM klient ');
  QueryKlient.SQL.ADD('WHERE ');
end;


// procedurka, która doda warunki WHERE w klauzuli wyszukiwania
procedure TDMKlient.KlientWHERELoad(var QueryKlient: TQuery; var QueryBuffer: TKlientSQLParam);
var
  x, ValueChecked: Integer;
begin
  QueryBuffer.SQLCaption := '';

  if length(QueryBuffer.kod_klientaOd) > 0 then
    begin
      QueryKlient.SQL.ADD('klient.kod_klienta >= :kod_klientaOd AND ');
      QueryKlient.ParamByName('kod_klientaOd').AsInteger := StrToInt(QueryBuffer.kod_klientaOd);
      QueryBuffer.SQLCaption := QueryBuffer.SQLCaption + ' >= ' + QueryBuffer.kod_klientaOd;
    end;

  if length(QueryBuffer.kod_klientaDo) > 0 then
    begin
      QueryKlient.SQL.ADD('klient.kod_klienta <= :kod_klientaDo AND ');
      QueryKlient.ParamByName('kod_klientaDo').AsInteger := StrToInt(QueryBuffer.kod_klientaDo);
      // opis filtru
      QueryBuffer.SQLCaption := QueryBuffer.SQLCaption + ' <= ' + QueryBuffer.kod_klientaDo;
    end;

  if length(QueryBuffer.nazwa) > 0 then
    begin
      if (QueryBuffer.LowerIsUpper = 'T') then
        begin
          QueryKlient.SQL.ADD('UPPER(klient.nazwa) LIKE :nazwa AND ');
          QueryKlient.ParamByName('nazwa').AsString := AnsiUpperCase(QueryBuffer.nazwa);
        end
      else
        begin
          QueryKlient.SQL.ADD('klient.nazwa LIKE :nazwa AND ');
          QueryKlient.ParamByName('nazwa').AsString := QueryBuffer.nazwa;
        end;
      QueryBuffer.SQLCaption := QueryBuffer.SQLCaption + ' ' + QueryBuffer.nazwa;
    end;


  if length(QueryBuffer.miejscowosc) > 0 then
    begin
      if (QueryBuffer.LowerIsUpper = 'T') then
        begin
          QueryKlient.SQL.ADD('UPPER(klient.miejscowosc) LIKE :miejscowosc AND ');
          QueryKlient.ParamByName('miejscowosc').AsString := AnsiUpperCase(QueryBuffer.miejscowosc);
        end
      else
        begin
          QueryKlient.SQL.ADD('klient.miejscowosc LIKE :miejscowosc AND ');
          QueryKlient.ParamByName('miejscowosc').AsString := QueryBuffer.miejscowosc;
        end;
      QueryBuffer.SQLCaption := QueryBuffer.SQLCaption + ' ' + QueryBuffer.miejscowosc;
    end;

  if length(QueryBuffer.ulica) > 0 then
    begin
      if (QueryBuffer.LowerIsUpper = 'T') then
        begin
          QueryKlient.SQL.ADD('UPPER(klient.ulica) LIKE :ulica AND ');
          QueryKlient.ParamByName('ulica').AsString := AnsiUpperCase(QueryBuffer.ulica);
        end
      else
        begin
          QueryKlient.SQL.ADD('klient.ulica LIKE :ulica AND ');
          QueryKlient.ParamByName('ulica').AsString := QueryBuffer.ulica;
        end;
      QueryBuffer.SQLCaption := QueryBuffer.SQLCaption + ' ' + QueryBuffer.ulica;
    end;

  if length(QueryBuffer.nr_domu) > 0 then
    begin
      if (QueryBuffer.LowerIsUpper = 'T') then
        begin
          QueryKlient.SQL.ADD('UPPER(klient.nr_domu) LIKE :nr_domu AND ');
          QueryKlient.ParamByName('nr_domu').AsString := AnsiUpperCase(QueryBuffer.nr_domu);
        end
      else
        begin
          QueryKlient.SQL.ADD('klient.nr_domu LIKE :nr_domu AND ');
          QueryKlient.ParamByName('nr_domu').AsString := QueryBuffer.nr_domu;
        end;
      QueryBuffer.SQLCaption := QueryBuffer.SQLCaption + ' ' + QueryBuffer.nr_domu;
    end;

  if length(QueryBuffer.nr_lokalu) > 0 then
    begin
      if (QueryBuffer.LowerIsUpper = 'T') then
        begin
          QueryKlient.SQL.ADD('UPPER(klient.nr_lokalu) LIKE :nr_lokalu AND ');
          QueryKlient.ParamByName('nr_lokalu').AsString := AnsiUpperCase(QueryBuffer.nr_lokalu);
        end
      else
        begin
          QueryKlient.SQL.ADD('klient.nr_lokalu LIKE :nr_lokalu AND ');
          QueryKlient.ParamByName('nr_lokalu').AsString := QueryBuffer.nr_lokalu;
        end;
      QueryBuffer.SQLCaption := QueryBuffer.SQLCaption + ' ' + QueryBuffer.nr_lokalu;
    end;

  if length(QueryBuffer.kod_pocztowy) > 0 then
    begin
      QueryKlient.SQL.ADD('klient.kod_pocztowy LIKE :kod_pocztowy AND ');
      QueryKlient.ParamByName('kod_pocztowy').AsString := QueryBuffer.kod_pocztowy;
      QueryBuffer.SQLCaption := QueryBuffer.SQLCaption + ' ' + QueryBuffer.kod_pocztowy;
    end;



  QueryKlient.SQL.ADD('klient.ID > 0 ');

  if QueryBuffer.SQLCaption = '' then
    QueryBuffer.SQLCaption := EmptyQueryFilterCaption;
end;
 

88e5e8019e.png

**
Zwróć proszę uwagę, co zyskałeś dzięki rezygnacji z komponentu TTable na rzecz TQuery!**

Szukasz przykładowo Pana Kowalskiego Jana z Dominikowa.... ale nie jesteś pewnym, czy to może nie był Pan Kowalewski i nie Jan tylko Janusz ... Hmmm A może nie z Dominikowa tylko z Dominikówka ?

Korzystając z mechanizmu zapytań do bazy możesz stosować cały wachlarz składni SQL, czy LIKE, UPPER, itd.

Jak wygląda to od strony formularzy, okienek i akcji?

Powiedzmy, że masz okno na masz komponent TDBGrid i obok Button "szukaj".
Akcja dla niego wygląda tak:

procedure TOknoGlowne.aKlientZnajdzExecute(Sender: TObject);
begin
  DBGridKlient.SetFocus;

  //Tworzysz formularz do szukania danych
  Application.CreateForm(TS_Klient, S_Klient);
  // ładujesz parametry (za pierwszym razem wszystko będzie puste)
  KlientSQLParam.LoadSQLBuffer;
  //wywołujesz okienko i jeżeli ktoś wykonał akcję szukaj
  if S_Klient.ShowModal = mrOk then
  begin
    try
      Screen.Cursor := crSQLWait;
      // ... to z formularza czytasz dane i umieszczasz je w swoich zmiennych
      KlientSQLParam.SaveSQLParam;
      DMKlient.qKlient.DisableControls;
      // ładujesz tekst zapytania do obiektu TQuery
      DMKlient.KlientSQLLoad(DMKlient.qKlient);
      // uzupełniasz o klauzule WHERE
      DMKlient.KlientWHERELoad(DMKlient.qKlient, KlientSQLParam);
      // wyświetlasz użtkownikowi czego szukał
      OknoGlowne.StaticTextKlient.Caption := KlientSQLParam.SQLCaption;
      //OknoGlowne.Update;
      Application.ProcessMessages;
      // Otwierasz TQury  i widzisz efekty swojego zapytania
      DMKlient.qKlient.Open;
    finally
      DMKlient.qKlient.EnableControls;
      Screen.Cursor := crDefault;
    end;
  end;
end;

 

na tym Gridzie możesz teraz wykonywać inne akcje
np. usuwać rekordy posługując się menu Popup lub klawiszem DEL


procedure TOknoGlowne.aKlientUsunExecute(Sender: TObject);
begin
  if not (dsKlient.DataSet.FieldByName('kod_klienta').AsInteger > 0) then
    Abort;

  dsKlient.DataSet.Delete;
end;

**Ważne!
Widzisz powyższy wpis dsKlient.DataSet.Delete?
Wrócimy do tego!
**
a jak wrócisz po zrobieniu kawy i nie wiesz, czy wyświetlane dane na Twoim kompie są aktualne to możesz je oświeżyć uruchamiając F5 lub w Popupie

procedure TOknoGlowne.aKlientOdswiezExecute(Sender: TObject);
var
  FindValue: Integer;
begin
  DBGridKlient.SetFocus;

  if KlientSQLParam.SQLCaption = '' then
    Abort;

  //aKlientFindStop - checkbox na formularzu, który mówi, że po odświeżeniu danych, kursor ma wrócić do poprzedniego rekordu
  if aKlientFindStop.Checked then
    FindValue := dsKlient.DataSet.FieldByName('kod_klienta').AsInteger
  else
    FindValue := 0;
  try
    Screen.Cursor := crSQLWait;
    Application.ProcessMessages;
    DMKlient.qKlient.DisableControls;
    DMKlient.qKlient.Close;
    DMKlient.qKlient.Open;
    if aKlientFindStop.Checked then
      if FindValue <> 0 then
        DMKlient.qKlient.Locate('kod_klienta',FindValue,[]);
  finally
    DMKlient.qKlient.EnableControls;
    Screen.Cursor := crDefault;
  end;
end;
 

Dobra .... Chcesz sobie te dane posortować?
(Przypomnę, że masz zdefinniowane warunki wyszukiwania.)
Chcesz?
Nie ma sprawy! :)
Kliknij w nagłówek kolumny na TDBGridzie!

procedure TOknoGlowne.DBGridKlientTitleClick(Column: TColumn);
var
  FindValue: Integer;
begin
  FindValue := DMKlient.qKlientkod_klienta.Value;

  if not (FindValue > 0) then
    Abort;

  if KlientSQLParam.SQLCaption = EmptyQueryFilterCaption then
    if Application.MessageBox(PChar(RunEmptyQueryOrderQuestion), 'Uwaga:', MB_ICONQUESTION + MB_YESNO + MB_DEFBUTTON2) <> IDYES then
      Abort;

  try
    Screen.Cursor := crSQLWait;
    Application.ProcessMessages;
    DMKlient.qKlient.DisableControls;
    DMKlient.KlientSQLLoad(DMKlient.qKlient);
    DMKlient.KlientWHERELoad(DMKlient.qKlient, KlientSQLParam);
    OknoGlowne.StaticTextKlient.Caption := KlientSQLParam.SQLCaption;    
    DBGridUnMarkORDER(OknoGlowne.DBGridKlient);
    //OknoGlowne.Update;
    Application.ProcessMessages;
    Column.Font.Style := [fsBold];
    Column.Title.Font.Style := [fsBold];
    DBGridORDERLoad(DMKlient.qKlient, OknoGlowne.DBGridKlient);
    DMKlient.qKlient.Open;
    if aKlientFindStop.Checked then
      DMKlient.qKlient.Locate('kod_klienta',FindValue,[]);
  finally
    DMKlient.qKlient.EnableControls;
    Screen.Cursor := crDefault;
  end;
end;


procedure DBGridUnMarkORDER(var DisplayDBGrid: TDBGrid);
var
  vCol: Integer;
begin
  for vCol := 0 to (DisplayDBGrid.Columns.Count - 1) do
    begin
      DisplayDBGrid.Columns.Items[vCol].Title.Font.Style := [];
      DisplayDBGrid.Columns.Items[vCol].Font.Style := [];
    end;
  DisplayDBGrid.Repaint;
end;


function DBGridORDERLoad(var DisplayQuery: TQuery; var DisplayDBGrid: TDBGrid): String;
var
  vCol, vPos_C, vPos_L: Integer;
  vField: String;
begin
  for vCol := 0 to (DisplayDBGrid.Columns.Count - 1) do
    if DisplayDBGrid.Columns.Items[vCol].Font.Style = [fsBold] then
      vField := DisplayDBGrid.Columns.Items[vCol].FieldName;

  if vField <> '' then
    begin
      DisplayQuery.SQL.ADD('ORDER BY ');
      DisplayQuery.SQL.ADD(GetOrderByFromList(vField));
    end;

  DisplayDBGrid.Repaint;
  Result := vField;
end;

 

Dane najlepiej z DBgrida najlepiej wyświetlać na formularzu czyli np tak:


procedure TOknoGlowne.aKlientOtworzExecute(Sender: TObject);
begin
  if not (dsKlient.DataSet.FieldByName('kod_klienta').AsInteger > 0) then
    Abort;

  KlientView;
end;
 
procedure KlientView(const ReadOnlyMode: Boolean = False);
begin
  try
    Application.CreateForm(TFDD_Klient, FDD_Klient);
    FDD_Klient.dsDetale.DataSet := DMKlient.qKlient;
    FDD_Klient.KeyForDokumenty := DMKlient.qKlient.FieldByName('kod_klienta').AsInteger;
    FDD_Klient.TabSheetDokumenty.TabVisible := True;
  finally
    FDD_Klient.Free;
    FDD_Klient := nil;
  end;
end;
 

Na formularzu z danymi masz obiekt typu DataSet i do niego przypisałeś swoje qKlient.
Teraz sobie możesz na tym walczyć jak chcesz. wejść w tryb edycji wywołując metodę Edit, pokazać na zakładkach (w przykładzie TabSheetDokumenty) dokumenty skojarzone z klientem, historię wpisów, czyli pobrać z bazy wyświetli dokumenty skojarzone z tym klientem. itd. Możesz dodać nową pozycję Klient, itd.

Najważniejsze, byś pamiętał, że pracujesz na obiekcie DataSet!

Dobra .... wróćmy teraz do tego, co napisałem na początku, czyli obsługi akcji edycja, usuń, dodaj z wykorzystaniem procedur składowanych bazy danych i naszego obiektu TQuery .... opakowanego w TDataSet.

Na formularzu wywołujemy TDataSet.Post, a w naszym module module mamy akcje dla takiego wywołania
Before i After .... After wykona się, gdy Before nie zwróci błędów


procedure TDMKlient.qKlientBeforePost(DataSet: TDataSet);
begin
  Screen.Cursor := crSQLWait;

  if not (DataSet.FieldByName('kod_klienta').AsInteger > 0) then
    if (DataSet.State = dsEdit) then
      DataSet.FieldByName('kod_klienta').AsInteger := LastKlientSerial;

  DataSet.FieldByName('change_user').AsInteger  := DM.KodOperatora;

  try
    spKlient_BeforePost.Params[ 0].Value  := DataSet.FieldByName('kod_klienta').NewValue;
    spKlient_BeforePost.Params[ 1].Value  := DataSet.FieldByName('nazwa').NewValue;
    spKlient_BeforePost.Params[ 5].Value  := DataSet.FieldByName('miejscowosc').NewValue;
    spKlient_BeforePost.Params[ 6].Value  := DataSet.FieldByName('ulica').NewValue;
    spKlient_BeforePost.Params[ 7].Value  := DataSet.FieldByName('nr_domu').NewValue;
    spKlient_BeforePost.Params[ 8].Value  := DataSet.FieldByName('nr_lokalu').NewValue;
    spKlient_BeforePost.Params[ 9].Value  := DataSet.FieldByName('kod_pocztowy').NewValue;

    //....

    // tutaj wywołaliśmy procedurę zapisującą do bazy
    spKlient_BeforePost.ExecProc;
    // a tutaj pobrała ona komunikaty z serwera
    spKlient_BeforePost.Open;

   // jeżeli nie ma tych komunikatów, to wszytko poszło OK :) w przeciwnym wypadku pokaż je i przetwórz
    if spKlient_BeforePost.RecordCount > 0 then
      begin
        Screen.Cursor := crDefault;
        ReturnMessageAnalyzer.dsResultMessages.DataSet := spKlient_BeforePost;
        if ReturnMessageAnalyzer.ShowModal <> mrOk then
          Abort;
      end;
  finally
    Screen.Cursor := crDefault;
    spKlient_BeforePost.Close;
  end;

end;

procedure TDMKlient.qKlientAfterPost(DataSet: TDataSet);
begin
  if DM.MojaBaza.InTransaction then
    begin
      (DataSet As TQuery).ApplyUpdates;
      (DataSet As TQuery).CommitUpdates;
    end
  else
    begin
      DM.MojaBaza.StartTransaction;
      try
        (DataSet As TQuery).ApplyUpdates;
        DM.MojaBaza.Commit;
      except
        DM.MojaBaza.Rollback;
        (DataSet As TQuery).CancelUpdates;
        raise;
      end;
      (DataSet As TQuery).CommitUpdates;
    end;
end;



// a tutaj bezpośrednio wywołujemy odpowiednie procedury składowane i przekazujemy im parametry.
procedure TDMKlient.qKlientUpdateRecord(DataSet: TDataSet;
  UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction);
begin
  case UpdateKind of
    ukInsert:
      begin
        { - stosowane tylko w kreatorze klienta !!!  }
        try          
          spKlient_Ins.Params[ 0].Value  := DataSet.FieldByName('nazwa').NewValue;
          spKlient_Ins.Params[ 4].Value  := DataSet.FieldByName('miejscowosc').NewValue;
          spKlient_Ins.Params[ 5].Value  := DataSet.FieldByName('ulica').NewValue;
          spKlient_Ins.Params[ 6].Value  := DataSet.FieldByName('nr_domu').NewValue;
          spKlient_Ins.Params[ 7].Value  := DataSet.FieldByName('nr_lokalu').NewValue;
          spKlient_Ins.Params[ 8].Value  := DataSet.FieldByName('kod_pocztowy').NewValue;
          spKlient_Ins.ExecProc;
          spKlient_Ins.Open;
          LastKlientSerial := spKlient_Insexpression.Value;
        finally
          spKlient_Ins.Close;
        end;
      end;

    ukModify:
      begin
        spKlient_Upd.Params[ 0].Value  := DataSet.FieldByName('kod_klienta').OldValue;
        spKlient_Upd.Params[ 1].Value  := DataSet.FieldByName('nazwa').NewValue;
        spKlient_Upd.Params[ 5].Value  := DataSet.FieldByName('miejscowosc').NewValue;
        spKlient_Upd.Params[ 6].Value  := DataSet.FieldByName('ulica').NewValue;
        spKlient_Upd.Params[ 7].Value  := DataSet.FieldByName('nr_domu').NewValue;
        spKlient_Upd.Params[ 8].Value  := DataSet.FieldByName('nr_lokalu').NewValue;
        spKlient_Upd.Params[ 9].Value  := DataSet.FieldByName('kod_pocztowy').NewValue;
        spKlient_Upd.ExecProc;
      end;

    ukDelete:
      begin
        spKlient_Del.Params[ 0].Value  := DataSet.FieldByName('kod_klienta').OldValue;
        spKlient_Del.Params[ 1].Value  := DM.KodOperatora;
        spKlient_Del.ExecProc;
      end;
  end;
  UpdateAction := uaApplied;
end;



 

Czy teraz już wiesz, co zrobić ze swoim koszykiem? :)
Podpowiedź:
Zdefiniuj Koszyk jako DataSet i Drugi DataSet Towary.
Wybierając określony towar przekaż go formularza i uzupełnij dane o ilość.
Przycisk "Zapisz" zrób jako wywołanie procedury z odpowiednimi parametrami z formularza:
ID_TOWARU,
ILOSC

W procedurze składowanej pobierz CENĘ, przemnóż przez ilość i zapisz do bazy

Jest to mój pierwszy post.
Więcej! Dzisiaj się zarejestrowałem dopiero na tym forum, chociaż czytam okazjonalnie od dłuższego czasu.
Dlaczego postanowiłem napisać ten post?
Dlatego, gdyż ..... w dłuższej perspektywie opłaca się uczyć od początku właściwego pisania programów.
Oczywiście można stosować komponent TTable, gdy LOKALNIE coś tam sobie małego grzebiemy, ale dojdziemy kiedyś do ściany, od której się odbijemy. Jak chcesz obsługiwać bazę, która ma milion towarów? Komponentem TTable?
Lepiej od samego początku pisać kod, mając na myśli bazę, do której dobiera się jednocześnie setki użytkowników.
Lepiej na samym początku przyjąć do wiadomości, że to, co mamy na ekranie było aktualne przed chwileczką i w chwili Post, aktualne już być nie musi. Lepiej przyjąć założenie, że twórcy baz danych dobrze wykonali swoją robotę i to na bazę danych zrzucić odpowiedzialność za wybieranie, sortowanie, spójność danych, a nie samemu rzeźbić lokalnie jakieś procedury validacji, etc.

Mam nadzieję, że przekonałem Was do stosowania TQuery zamiast TTable.
.... i mam nadzieję, że od tematu "aktualizowania danych w bazie" nie odskoczyłem za bardzo.

0

Już widzę, że numery parametrów pomyliłem (winny być kolejne), ale tak to jest jak się coś wycinka z programu. :(

0

Wow, wielkie dzięki za ten post. Spróbuje to ogarnąć. Mi tutaj głownie chodziło, że jak już dodam to do tego "koszyka" to będę to mógł drukować. Z tym sobie poradzę jakoś. I dzięki, że pokazałeś, że lepiej użyć innego komponentu bo to się przyda, ponieważ w bazie będzie ponad 1500 produktów, i co jakiś czas będą aktualizowane i dodawane. Jak będę miał chwile czasu to przysiądę nad tym i przeanalizuje. Jeszcze raz dzięki :D

0

No to teraz się zacznie ;-)
Uwaga – będzie mnóstwo czepiania się.
Ale jeśli ktokolwiek będzie miał do mnie zarzuty o czepialstwo, to proszę bardzo – niech napisze dlaczego nie mam racji.

BSorbus napisał(a):

W Delphi piszę, a raczej pisałem programy przez jakieś 15 lat i zawsze byłem przeciwnikiem bezpośredniego używania komponentu TTable.
Dlaczego?
Gdyż ma się bardzo małą kontrolę nad bazą danych!
Oczywiście małe programiki będą działały, DBGrid będzie wyświetlał tabelki i będzie prawie gites, ale .....prawie robi różnice ;-) .

Absolutnie nie zgadzam się w tym konkretnym przypadku; zauważ, że Wiszu pisał program w oparciu o bazę PARADOX. To nie jest baza SQL, to jest baza plikowa, do której masz dostęp przez BDE. Dostęp do PARADOXA za pomocą TTable jest jak najbardziej prawidłowy i zalecany.
Natomiast korzystanie z TQuery dla baz ISAMowych, chociaż wygodne, jest protezą - wolną protezą... Ponieważ to BDE (a dokładnie jego podsystem zwany LocalSQL) tłumaczy kod SQLa na filtry, zakresy (Range)., które de-facto są wykonywane za pomocą TTable. Skoro tak, to po co zatem używać TQuery (przypominam - chodzi o Paradoxa) skoro to proteza?

Prosty przykład.
Masz aplikację, która łączy się z bazą będącą częścią ogromnej bazy danych, rozproszonej po kilkunastu serwerach na terenie całej Polski. (Dane między serwerami są wymieniana za pomocą mechanizmów replikacji i procedur składowanych dual commit) Załóżmy, że Twój koszyk wypełniany będzie towarami, których listę aktualizuje równocześnie kilkudziesięciu (kilkuset) użytkowników. Wolisz, by o integralność tej bazy dbała aplikacja klienta, czy serwer(y) bazy danych?
No właśnie!
Serwer, będący nawet częścią systemu rozproszonego lub częścią klastra, zadba o to najlepiej!

Ja tu nie widzę żadnego przykładu. Co więcej, co ma piernik do wiatraka - chcesz powiedzieć że używając TTable nie ma się kontroli nad bazą danych? Przecież to nieprawda...
Masz cały zestaw mechanizmów, które w pełni pozwolą Ci kontrolować komunikację z bazą danych. Od transakcji, przez wyjątki (również te generowane przez serwer) do CachedUpdates.

Dlatego proponuję Ci zweryfikować swój pogląd dotyczący obsługi baz danych z poziomu aplikacji napisanych w Delphi i to bez względu na to, czy piszesz programy client-server, czy trójwarstwowe (z wykorzystaniem pośrednio serwera aplikacji).

Proponuję byś swoje aplikacje pisał tak:

Komponent TQuery i tutaj piszesz kod SQL.

Pełna zgoda, ale nie dla Paradoxa!

Komponent ten poprzez TDataSource obsługuj sobie w gridach, polach, comoxach, czy jak tam chcesz.
Gdy przeprowadzasz edycję tabeli, dodajesz, usuwasz rekordy, to najlepiej wywoływać procedurę składowaną z odpowiednimi parametrami i przechwytywać zwracaną wartość.

Uuuuaaa.... dwie sprawy.
Po pierwsze - nie da się napisać procedury składowanej dla Paradoxa.
Po drugie - piszesz o CRUD (w wikipedii piszą o tym więcej). Jestem przeciwnikiem takiego podejścia, dlatego że trzeba napisać 4 (cztery!) procedury składowane do obsługi jednej tabeli. Nie dość, że trzeba owe procedury napisać to jeszcze trzeba je utrzymywać.
Zmiana tabeli, np. dodanie jednego pola = poprawka czterech procedur i oczywiście kodu w Delphi.
I po co się tak męczyć, skoro można to zrobić od ręki?

Będzie to najczęściej ID nowo wprowadzonego rekordu lub numer i komunikat błędu serwera. Ponadto, przy takim podejściu możesz na serwerze bazy danych robić cuda-cudeńka np. sprawdzać walidację przesłanych parametrów, wykonać 101 dodatkowych akcji (np. Dla akcji "Kup wódkę, sztuk 2 flaszki, po cenie 19,90zł" możesz sprawdzać, czy zarejestrowany user ma 18-cie lat i jeżeli nie, to wyszukać najbardziej chude mleko w najdalszym magazynie i mu to wstawić do Koszyka ;-)

To samo można zrobić bez pisania czterech procedur do obsługi każdego zestawu danych.

Przykładowy kawałek kodu do obsługi tabelki Klient
w module typu TDataMod stawiasz komponent typu TQuery i komponenty TStoredProc, które będą wywowływały odpowiednie procedury z bazy danych.

Żle!!
Gdybyś połączył to TQuery z TUpdateSQL to wszystko to ca tak pracowicie wklepałeś w przypadku przypisania parametrów do procedur i ich wywołania zrobiłoby się samo.
Ot tak, przy edycji danych w TQuery i wywołaniu Post/Delete wykonana zostałaby odpowiednia procedura... Ale Ty wolisz je ręcznie wywoływać w zdarzeniach DataSetu.
Po co??
/ciach/
Dzizas…
Tyle kodu do wyszukiwania danych tylko w jednej kartotece? I to jeszcze napisanego literalnie pod konkretny zestaw danych – tego nawet nie da się użyć do wyszukiwania danych w dowolnej kartotece.
Wybacz, ale dla mnie to jest niedopuszczalna masakra… Chyba jestem zbyt leniwy na coś takiego i dlatego wolę napisać mechanizm taki jak wyszukiwanie filtrowanie i inne raz a dobrze, a potem używać wszędzie i dla każdego zestawu danych. Bez przepisywania go za każdym razem, kiedy np. dodam pole do tabeli.

**
Zwróć proszę uwagę, co zyskałeś dzięki rezygnacji z komponentu TTable na rzecz TQuery!**

Ano, zyskał 20x więcej kodu. Dzięki za taką optymalizację – innymi słowy, niczego nie zyskał.

Szukasz przykładowo Pana Kowalskiego Jana z Dominikowa.... ale nie jesteś pewnym, czy to może nie był Pan Kowalewski i nie Jan tylko Janusz ... Hmmm A może nie z Dominikowa tylko z Dominikówka ?
Korzystając z mechanizmu zapytań do bazy możesz stosować cały wachlarz składni SQL, czy LIKE,

Oczywiście to prawda dla TTable. Ale już np. takie TFDTable (FireDAC) i TIBTable (Interbase Express) to zupełnie inna bajka. Te TTable filtruje po stronie serwera i wystarczy coś takiego:

Table.Filter := ‘adres like ’ + QuotedStr(‘dominik%’)
Table.Filtered := True; 

TFDTable zamieni coś takiego na:

Select * from tabela where adres like ‘domink%’

Czyli zrobi to samo, bez pisania procedur i ich obsługi. Zauważ, że używanie filtra jest dużo wygodniejsze niż zmiana zapytania i zrobienie Close/Open.
Poza tym TFDTable działa lepiej od TQuery z dużymi zestawami danych (sic!). Brzmi jak herezja, ale to prawda – wystarczy poczytać o Firedac Live Data Window Mode

UPPER, itd.

Zauważyłem, że wszędzie w SQL przy porównywaniu danych tekstowych używasz UPPER. Ale wiesz, że takie podejście to zabójstwo dla Twojego serwera bazy danych? Indeksy nie zostaną użyte i zostanie wykonany skan tabeli?

Jak wygląda to od strony formularzy, okienek i akcji?
Powiedzmy, że masz okno na masz komponent TDBGrid i obok Button "szukaj".
Akcja dla niego wygląda tak:

/ciach/

**Ważne!
Widzisz powyższy wpis dsKlient.DataSet.Delete?

Widzę i nie podoba mi się z kilku powodów, ale najbardziej nie podoba mi się ten Abort.
Po co on tam jest? Dlaczego nie tak:

procedure TOknoGlowne.aKlientUsunExecute(Sender: TObject);
begin
  if not dsKlient.DataSet.IsEmpty then
    dsKlient.DataSet.Delete;
end;

Wrócimy do tego!
**
a jak wrócisz po zrobieniu kawy i nie wiesz, czy wyświetlane dane na Twoim kompie są aktualne to możesz je oświeżyć uruchamiając F5 lub w Popupie

procedure TOknoGlowne.aKlientOdswiezExecute(Sender: TObject);
var
  FindValue: Integer;
begin
  DBGridKlient.SetFocus;

  if KlientSQLParam.SQLCaption = '' then
    Abort;

  //aKlientFindStop - checkbox na formularzu, który mówi, że po odświeżeniu danych, kursor ma wrócić do poprzedniego rekordu
  if aKlientFindStop.Checked then
    FindValue := dsKlient.DataSet.FieldByName('kod_klienta').AsInteger
  else
    FindValue := 0;
  try
    Screen.Cursor := crSQLWait;
    Application.ProcessMessages;
    DMKlient.qKlient.DisableControls;
    DMKlient.qKlient.Close;
    DMKlient.qKlient.Open;
    if aKlientFindStop.Checked then
      if FindValue <> 0 then
        DMKlient.qKlient.Locate('kod_klienta',FindValue,[]);
  finally
    DMKlient.qKlient.EnableControls;
    Screen.Cursor := crDefault;
  end;
end;

Eeee… Close/Open? A dlaczego nie TDataSet.Refresh?
I jeszcze ten checkbox aKlientFindStop – ktoś w ogóle z tego korzysta i rozumie jak to działa? Ciut to przekombinowane…

Dobra .... Chcesz sobie te dane posortować?
(Przypomnę, że masz zdefinniowane warunki wyszukiwania.)
Chcesz?
Nie ma sprawy! :)
Kliknij w nagłówek kolumny na TDBGridzie!

Fajne, ale ja bym napisał to tak:

procedure TOknoGlowne.DBGridKlientTitleClick(Column: TColumn);
var 
  q: TDAdQuery; 
begin 
  q := Column.Field.DataSet as TDAdQuery;
  q.SQLParser
    .OrderBy
    .Clear
    .Add(Column.FieldName);
  q.OpenEx(); 
end;

Widzisz różnicę - najwalniejsza (dla mnie) to taka że ten kod zadziała z dowolnym zapytaniem i dla dowolnej kolumny.
Napisałem to już jakiś czas temu, więcej tu:
http://www.da-soft.com/forums/anydac-general-english/suggestion-property-for-server-side-filtering.html#17820

Dane najlepiej z DBgrida najlepiej wyświetlać na formularzu czyli np tak:


procedure TOknoGlowne.aKlientOtworzExecute(Sender: TObject);
begin
  if not (dsKlient.DataSet.FieldByName('kod_klienta').AsInteger > 0) then
    Abort;

  KlientView;
end;
 

I znowu ten Abort… To naprawde nie służy do ego, zwłaszcza że ten kod można napisać po prostu tak:

procedure TOknoGlowne.aKlientOtworzExecute(Sender: TObject);
begin
  if not dsKlient.DataSet.IsEmpty then
    KlientView;
end;

Zauważ, że kod jest krótszy, prostszy i zadziała z dowolnym DataSetem. Wystarczy przekazać mu tylko jakie View ma otworzyć i możesz to napisać raz – a używać wszędzie.
/ciach/
O, teraz będzie moja ulubiona część ;-)

Dobra .... wróćmy teraz do tego, co napisałem na początku, czyli obsługi akcji edycja, usuń, dodaj z wykorzystaniem procedur składowanych bazy danych i naszego obiektu TQuery .... opakowanego w TDataSet.

Na formularzu wywołujemy TDataSet.Post, a w naszym module module mamy akcje dla takiego wywołania
Before i After .... After wykona się, gdy Before nie zwróci błędów


procedure TDMKlient.qKlientBeforePost(DataSet: TDataSet);
begin
  Screen.Cursor := crSQLWait;

  if not (DataSet.FieldByName('kod_klienta').AsInteger > 0) then
    if (DataSet.State = dsEdit) then
      DataSet.FieldByName('kod_klienta').AsInteger := LastKlientSerial;

  DataSet.FieldByName('change_user').AsInteger  := DM.KodOperatora;

  try
    spKlient_BeforePost.Params[ 0].Value  := DataSet.FieldByName('kod_klienta').NewValue;
    spKlient_BeforePost.Params[ 1].Value  := DataSet.FieldByName('nazwa').NewValue;
    spKlient_BeforePost.Params[ 5].Value  := DataSet.FieldByName('miejscowosc').NewValue;
    spKlient_BeforePost.Params[ 6].Value  := DataSet.FieldByName('ulica').NewValue;
    spKlient_BeforePost.Params[ 7].Value  := DataSet.FieldByName('nr_domu').NewValue;
    spKlient_BeforePost.Params[ 8].Value  := DataSet.FieldByName('nr_lokalu').NewValue;
    spKlient_BeforePost.Params[ 9].Value  := DataSet.FieldByName('kod_pocztowy').NewValue;

    //....

    // tutaj wywołaliśmy procedurę zapisującą do bazy
    spKlient_BeforePost.ExecProc;
    // a tutaj pobrała ona komunikaty z serwera
    spKlient_BeforePost.Open;

   // jeżeli nie ma tych komunikatów, to wszytko poszło OK :) w przeciwnym wypadku pokaż je i przetwórz
    if spKlient_BeforePost.RecordCount > 0 then
      begin
        Screen.Cursor := crDefault;
        ReturnMessageAnalyzer.dsResultMessages.DataSet := spKlient_BeforePost;
        if ReturnMessageAnalyzer.ShowModal <> mrOk then
          Abort;
      end;
  finally
    Screen.Cursor := crDefault;
    spKlient_BeforePost.Close;
  end;

end;

procedure TDMKlient.qKlientAfterPost(DataSet: TDataSet);
begin
  if DM.MojaBaza.InTransaction then
    begin
      (DataSet As TQuery).ApplyUpdates;
      (DataSet As TQuery).CommitUpdates;
    end
  else
    begin
      DM.MojaBaza.StartTransaction;
      try
        (DataSet As TQuery).ApplyUpdates;
        DM.MojaBaza.Commit;
      except
        DM.MojaBaza.Rollback;
        (DataSet As TQuery).CancelUpdates;
        raise;
      end;
      (DataSet As TQuery).CommitUpdates;
    end;
end;

O co chodzi z tymi "komunikatami z serwera" i dlaczego musi pobierać je procedura składowana?
Domniemuję, że to jakieś Twoje patenty (jeśli tak, to nie jest to żadne wyjaśnienie tylko galimatias) - bo przecież wyjątki serwera obsługuje się zupełnie inaczej.
Ale tak naprawdę - po co te kombinacje? Nawet jeśli to Twój patent, to dlaczego to jest napisane w ten sposób? Obsługa w zdarzeniach właściwego TDataSet, ręczne spinanie kontrolek z TDataModule w kodzie z innymi komponentami, itd.
Dla mnie to jest mnóstwo niepotrzebnego kodu, w którym bardzo łatwo się pomylić.

Nigdy, ale to **NIGDY **nie używaj RecordCount (aczkolwiek naprawdę wiele zależy od użytych komponentów) dla baz SQL - gdyż może pojawić się tam wartość -1. Dużo ładniej i bezpieczniej używać TDataSet.IsEmpty, które zawsze działa poprawnie.

Druga sprawa – nie napisałeś tego, ale z kodu jasno wynika, że owo TQuery chodzi w trybie CachedUdpates. Skoro tak, to po jasną cholerę zapisujesz zmiany po każdej zmianie w lokalnym (zdarzenie AfterPost) buforze datasetu? To jest pomieszanie z poplątaniem i to na dodatek źle zrobione. Pewnie, że będzie działać – ale litości, to tak jakby kupować tira żeby jechać po bułkę do spożywczaka…

// a tutaj bezpośrednio wywołujemy odpowiednie procedury składowane i przekazujemy im parametry.

Właśnie o to mi chodzi - cały poniższy kod jest niepotrzebny (zobacz niżej dopisek o TUpdateSQL)!
Rozumiem, że masz kilkaset obiektów bazodanowych i każdy obsługujesz w ten sposób?
To jest jeszcze proste - ale strasznie jestem ciekawy jak wygląda obsługa bardziej skomplikowanych obiektów biznesowych, takich jak np. dokumenty handlowe czy magazynowe.
Coś, co jest opisane za pomocą kilku połączonych ze sobą DataSetów.

procedure TDMKlient.qKlientUpdateRecord(DataSet: TDataSet;
  UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction);
begin
  case UpdateKind of
    ukInsert:
      begin
        { - stosowane tylko w kreatorze klienta !!!  }
        try          
          spKlient_Ins.Params[ 0].Value  := DataSet.FieldByName('nazwa').NewValue;
          spKlient_Ins.Params[ 4].Value  := DataSet.FieldByName('miejscowosc').NewValue;
          spKlient_Ins.Params[ 5].Value  := DataSet.FieldByName('ulica').NewValue;
          spKlient_Ins.Params[ 6].Value  := DataSet.FieldByName('nr_domu').NewValue;
          spKlient_Ins.Params[ 7].Value  := DataSet.FieldByName('nr_lokalu').NewValue;
          spKlient_Ins.Params[ 8].Value  := DataSet.FieldByName('kod_pocztowy').NewValue;
          spKlient_Ins.ExecProc;
          spKlient_Ins.Open;
          LastKlientSerial := spKlient_Insexpression.Value;

O to ciekawe - po co jest ExecProc i Open za chwilę?
Rozumiem, że przez ten Open pobierasz jakieś ID z serwera, czy tak?
Skoro tak i spKlient_Ins jest komponentem typu TStoredProc, to dlaczego nie użyłeś parametru output??
Z parametrem outpu ten kod wyglądałby tak:

 spKlient_Ins.ExecProc;
 LastKlientSerial := spKlient_Ins.Params[9].Value;

A więc nie jest potrzebne żadne Open i zamykanie potem...

        finally
          spKlient_Ins.Close;
        end;
      end;

    ukModify:
      begin
        spKlient_Upd.Params[ 0].Value  := DataSet.FieldByName('kod_klienta').OldValue;
        spKlient_Upd.Params[ 1].Value  := DataSet.FieldByName('nazwa').NewValue;
        spKlient_Upd.Params[ 5].Value  := DataSet.FieldByName('miejscowosc').NewValue;
        spKlient_Upd.Params[ 6].Value  := DataSet.FieldByName('ulica').NewValue;
        spKlient_Upd.Params[ 7].Value  := DataSet.FieldByName('nr_domu').NewValue;
        spKlient_Upd.Params[ 8].Value  := DataSet.FieldByName('nr_lokalu').NewValue;
        spKlient_Upd.Params[ 9].Value  := DataSet.FieldByName('kod_pocztowy').NewValue;
        spKlient_Upd.ExecProc;
      end;

    ukDelete:
      begin
        spKlient_Del.Params[ 0].Value  := DataSet.FieldByName('kod_klienta').OldValue;
        spKlient_Del.Params[ 1].Value  := DM.KodOperatora;
        spKlient_Del.ExecProc;
      end;
  end;
  UpdateAction := uaApplied;
end;

I to jes wg mnie największa masakra całego kodu - no chyba, że masz jakieś wytłumaczenie, którego tu nie widać.
Powiedz mi proszę po co to?
Dlaczego nie użyłeś komponentu TUpdateSQL, który wszystko to co napisałeś powyżej wykona za Ciebie automatycznie bez pisania ani jednej linii kodu?
Poza tym - naprawdę?
Mam pisać ręczne przypisywania wartości pola do procedury dla każdego DataSetu używanego w programie? I kilka razy? A nie lepiej zrobić to (w ogóle bym to zrobił inaczej, ale jeśli już...) za pomocą jednej procedury, która obsłuży dowolny TQuery i dopasowanego do niego TStoredProc.
Taka procedura może wyglądać tak (zakładam, że nzwy pól DataSetu i parametrów procedur posiadają takie same nazwy - co jest absolutnie sensowne):

procedure ExecCRUD(AQuery : TQuery; AUpdateKind: TUpdateKind; ASPIns, ASPUpd, ASPDel : TStoredProc);
var
  lSP : TStoredProc;
    I: Integer;
  lSPParam: TParam;
begin
  case AUpdateKind of
    ukModify : lSP := ASPUpd;
    ukInsert : lSP := ASPIns;
    ukDelete : lSP := ASPDel;
  end;

  for I := 0 to AQuery.FieldCount - 1 do
  begin
    lSPParam := lSP.Params.FindParam(AQuery.Fields[i].FieldName);
    if Assigned(lSPParam) then
    begin
      if AUpdateKind = ukDelete then
        lSPParam.Value := AQuery.Fields[i].OldValue
      else
        lSPParam.Value := AQuery.Fields[i].NewValue;
    end;
  end;

  lSP.ExecProc;
end;

A obsługa tego samego co napisałeś powyżej w zdarzeniu UpdateRecord będzie taka:

procedure TDMKlient.qKlientUpdateRecord(DataSet: TDataSet;
  UpdateKind: TUpdateKind; var UpdateAction: TUpdateAction);
begin
  ExecCRUD(DataSet, UpdateKind, spKlient_Ins, spKlient_Upd, spKlient_Del);
end;

Zobacz - kodu masz mnie tak ze 20x a robi dokładnie to samo.
Czy dalej nie jest godne naśladowania ;-)

Czy teraz już wiesz, co zrobić ze swoim koszykiem? :)
Podpowiedź:
Zdefiniuj Koszyk jako DataSet i Drugi DataSet Towary.
Wybierając określony towar przekaż go formularza i uzupełnij dane o ilość.
Przycisk "Zapisz" zrób jako wywołanie procedury z odpowiednimi parametrami z formularza:
ID_TOWARU,
ILOSC
W procedurze składowanej pobierz CENĘ, przemnóż przez ilość i zapisz do bazy

Prawie by się udało, gdyby to nie był Paradox. Co do reszty – hmm… Nie, dziękuję.

Jest to mój pierwszy post.
Więcej! Dzisiaj się zarejestrowałem dopiero na tym forum, chociaż czytam okazjonalnie od dłuższego czasu.
Dlaczego postanowiłem napisać ten post?
Dlatego, gdyż ..... w dłuższej perspektywie opłaca się uczyć od początku właściwego pisania programów.

To prawda, ale na pewno tak jak wyżej to nie jest właściwe pisanie programu bazodanowego. Wybacz, ale to jest radosne rękodzieło.

Oczywiście można stosować komponent TTable, gdy LOKALNIE coś tam sobie małego grzebiemy, ale dojdziemy kiedyś do ściany, od której się odbijemy. Jak chcesz obsługiwać bazę, która ma milion towarów? Komponentem TTable?

Za pomocą TFDTable – żaden problem.
Twoje TQuery z procedurą odświeżania, którą podałeś ma spore szanse ślicznie się wywalić z komunikatem out of memory właśnie na takiej tabeli, która ma ze 2 mln rekordów. Używasz tam Locate, które działa LOKALNIE, a więc pobiera wszystko do bufora i przeszukuje za pomocą zwykłe, prostej iteracji. Jak będzie szukać czegoś przy końcu takiego zestawu danych – sam wiesz, co się stanie.

Lepiej od samego początku pisać kod, mając na myśli bazę, do której dobiera się jednocześnie setki użytkowników.

Moim zdaniem to bez znaczenia, czy użytkowników są setki czy kilku. Wystarczy świadomość, że jest ich więcej niż jeden i trzeba sobie radzić z blokowaniem rekordów, którego nie wiedziałem u Ciebie w ogóle. Ale to mogę zrozumieć – ciężko w takim poście zawrzeć wszystko.
Większe znaczenie ma ilość danych i szybkość łącza do serwera. Karygodnym zachowaniem jest pisanie aplikacji bazo-danowej z pustą bazą danych. Ile razy słyszałem tłumaczenia „u mnie działa szybko”… Bo masz kilkaset rekordów ;-)

Lepiej na samym początku przyjąć do wiadomości, że to, co mamy na ekranie było aktualne przed chwileczką i w chwili Post, aktualne już być nie musi. Lepiej przyjąć założenie, że twórcy baz danych dobrze wykonali swoją robotę i to na bazę danych zrzucić odpowiedzialność za wybieranie, sortowanie, spójność danych, a nie samemu rzeźbić lokalnie jakieś procedury validacji, etc.

Za wybieranie i spójność danych – pełna zgoda.
Sortowanie – to zależy. Bo po co mam sortować dane przez serwer, jeśli mam je już na kliencie? Zawsze stosuję hybrydę – jak mam wszystko na kliencie (bo jest tego max kilkaset rekordów, no może kilka tyś) sortuję na kliencie, w przeciwnym razie – na serwerze.
Co do walidacji – absolutnie się nie zgadzam. Moim zdaniem znacznie lepiej i taniej (w utrzymani), a przede wszystki elastycznie zrobić walidację na kliencie. Walidacja na serwerze ma zapewniać tylko spójność danych, ale nie to czy – na przykład – dana w polu X musi kończyć się na A.

Mam nadzieję, że przekonałem Was do stosowania TQuery zamiast TTable.
.... i mam nadzieję, że od tematu "aktualizowania danych w bazie" nie odskoczyłem za bardzo.

Hmm… wiesz, gdybyś nie nawalił tu tyle kuriozalnego kodu z dodatkiem „tak się powinno pisać prawidłowo”, to może. Ale w takim przypadku – nie dziękuję.
I teraz możecie mi dać bana :D

0

Rozumiem, że w swoim życiu nie pisałeś NIGDY programu na bazę Informix-Online Dynamix Server i dlatego ciągle zadajesz pytania:
"O to ciekawe - po co jest ExecProc i Open za chwilę?"
Podobnie

"Rozumiem, że przez ten Open pobierasz jakieś ID z serwera, czy tak?
Skoro tak i spKlient_Ins jest komponentem typu TStoredProc, to dlaczego nie użyłeś parametru output??
Z parametrem outpu ten kod wyglądałby tak:

 spKlient_Ins.ExecProc;
 LastKlientSerial := spKlient_Ins.Params[9].Value;

"

Gdybyś przynajmniej jeden taki program napisał, to wiedziałbyś, że bez tego OPEN, procedura składowana w INFORMIX'ie nie zwróci Ci parametrów OUT i że obsługa w Delphi procedur składowanych dla tego systemu bazy danych jest odmienna niż dla np MS SQL.
Gdybyś przynajmniej jeden taki program napisał, to wiedziałbyś, że nic Ci nie da:
"LastKlientSerial := spKlient_Ins.Params[9].Value"

Pięknie piszesz o automatyzowaniu sortowania i podajesz dobrze napisany przykład.
Przyszło Ci jednak do głowy, że w DBGridzie mogą występować pola tylu Lockup do innych tabel i wtedy Twoje "select cośtam From tabela WHERE tutaj_pole_ktore_należy_do_innej_tabeli_bo_pochodzi_od_Lockup" wywali Ci błąd?

Dalej....,
Jak zadziałają Twoje złote pomysły na UPDATE/DELETE/INSERT, jeżeli w DBGridzie wyświetlasz widok (view) z bazy danych, które jest złączeniem kilku(nastu) tabel? Nadal nie widzisz uzasadnienia dla stosowania odpowiednich procedur składowanych, które "wiedzą", co sprawdzić, co zaktualizować i dlaczego oraz potrafią wywołać z wnętrza swojego ciała procedury na innych serwerach?

Podaj mi proszę pomysł, by kontrolować poprawność wprowadzanych danych w zależności od zawartości bazy danych, jeżeli nie stosujesz mechanizmów, które pytają bazę?
Oczywiście validację danych można w jakimś amoku zrobić w aplikacji klienta, ale czy nie wydaje się Tobie, że poprawniej będzie przerzucić to na stronę serwera bazy danych?

Przykład:
Użytkownik może wprowadzić do bazy danych tylko klienta, który zamieszkuje na terenie Pścimia Dolnego, chyba, że data jego urodzenia >= 01.09.1939 oraz innej bazie, na innym serwerze liczba zarejestrowanych mieszkańców w Pścimiu nie przekroczyła pewnej wartości ustalanej przez parametry zgromaczonej w bazie trzeciej na jeszcze innym serwerze.

Nadal chcesz to robić przy pomocy swoich cudownych mechanizmów, czy może rozważysz "rękodzieło" polegające na wywołaniu procedury składowanej i zezwoleniu użytkownikowi przeanalizować zwrócone komunikaty?

Można oczywiście zwrócić użytkownikowi -1 z komunikatem "nie udało zapisać się danych", ale nadal będzie on w czarnej dupie, gdyż nie będzie wiedział, dlaczego nie udało się zapisać.
A co z ostrzeżeniami, gdyby jednak chciał NA SIŁĘ i ŚWIADOMIE wpisać gościa, pomimo tego, że np. ze względu na brak połączenia z 3-cim serwerem, nie można ustalić aktualnie liczby mieszkańców Pścimia?

Wyobraź sobie, że w ramach "rękodzieła" zwracasz odpowiednie komunikaty opatrzone klauzulą błąd krytyczny lub nie, a użytkownik, gdy ewentualnie wyświetli mu się lista błędów niekrytycznych, będzie mógł jednak dokonać takiej rejestracji danych w bazie. Podoba się? jesteś przekonany, że klasyczna obsługa błędów serwera SQL też Ci da taką możliwość?

Umówmy się zatem, że ja akceptuję Twój, odmienny punkt widzenia na pewne sprawy, a Ty zaakceptujesz FAKT, że aplikacja klienta, która łączy się z z jedną z 20 baz, a z pozostałymi poprzez wewnętrzne wywołania triggerów i procedur składowanych dual-commit, winna aktualizować (wprowadzać/usuwać) dane z wykorzystaniem procedur składowanych, a nie delete na komponencie TTable.

Pozdrawiam :)

P.S.
Jeszcze gwoli wyjaśnienia tych UPPER....
Dopuszczasz taką myśl, że system baz danych 20 lat temu, nie był konfigurowalny na rozróżnianie lub nie małych i dużych liter?
Ponadto klient miał kaprys by odróżniać KOWALSKIEGO i Kowalskiego. Nadal będziesz omijał UPPER w swoich zapytaniach SQL?

Dokonując tak głębokiej analizy mojego posta, zapewne zauważyłeś, że pisałem o aplikacjach bazodanowych i nawet przywoływałem jakieś tam przykłady przetwarzania setek tysięcy rekordów.
Sądzisz, że pisałem o bazie PARADOX?
Uważam, że bazy tego typu już dawno powinny zostać pogrzebane i zdecydowanie LEPSZE, BEZPIECZNIEJSZE oraz SZYBSZE będą w takich zastosowaniach bazy SQL. Zamiast się się szarpać z beznadziejnym PARADOXEM, to już lepiej zainstalować małego, szybkiego i DARMOWEGO Firebirda, który jest dostępny na każdą platformę Linux i każdą Windę.

Rzecz jasna Ty możesz używać TTable, prostego Delete i nawet Paradox'a do obsługi wszystkich systemów, które oprogramowujesz.
Ciekawe, kto inwestuje w przyszłość?
Ten, który uczy się sprawnie posługiwać komponentem TTable, czy ten, który myśli o małych, ale i ogromnych bazach SQL, z którymi komunikował się będzie poprzez TQuery i TStoredProc?

//"Twoje TQuery z procedurą odświeżania, którą podałeś ma spore szanse ślicznie się wywalić z komunikatem out of memory właśnie na takiej tabeli, która ma ze 2 mln rekordów. Używasz tam Locate, które działa LOKALNIE, a więc pobiera wszystko do bufora i przeszukuje za pomocą zwykłe, prostej iteracji. Jak będzie szukać czegoś przy końcu takiego zestawu danych – sam wiesz, co się stanie."
//

  • Primo, Locate używam lokalnie na małym (wybranym już) zbiorze.
  • Secundo, jeżeli ktoś jest idiotą, który stosuje zapytania SQL z klauzulą WHERE po to, by lokalnie ściągnąć sobie 2 miliony rekordów z jakiegoś odległego serwera i dalej wśród nich coś wyszukiwać, to chyba nie powinien być dopuszczany do komputera.
    Ja na takie harce, to użytkownikom nie pozwalam, gdyż tak wielki cursor nie zostanie nawet przez serwer bazy zwrócony.

EOT

0

Moje odniesienie do Paradoxa było tylko dlatego, ze Wiszu pisał w oparciu o bazę Paradox. On jest właścicielem wątku i jemu należały się wyjaśnienia.
A Ty tu wyjeżdżasz z "napisz sobie procedurą składowaną"...

Tak masz rację, niczego nigdy nie napisałem z użyciem Informixa. I co z tego? Gdzie w Twojej wypowiedzi była mowa że "tak musi być, to tak jest w Informix "?
poza tym po prostu nie wierzę, że Informix nie obsługuje parametrów typu OUT (bo obecna wersja ma na pewno, ale wersja OnLine to jakiś zabytek i tego nie jestem pewien) alby Ty nie wiesz jak z tego korzystać w Delphi.

Poza tym mijasz się z prawdą w kilku miejscach (albo nie masz pojęcia o czym piszesz) i ładnie Ci to opiszę, ale teraz muszę zmykać.

PS.
Widzę, ze poczułeś się urażony - i bardzo dobrze, bo to co pokazałeś to popelina w czystej postaci.
Odpuściłbym Ci, gdybyś nie wyjechał z autorytarnym "tak się powinno pisać".
Moim zdaniem absolutnie tak się nie powinno pisać - praktycznie w każdej kwestii się nie zgadzam.

0

Sądziłem, że wstępnie zaznaczyłem, że chodzi mi raczej o wyrabianie nawyków określonych i ogólne podejście do tworzenia aplikacji KlientDelphi-BazaDanych SQL.

Pisząc "tak się powinno pisać" miałem na myśli, co "chyba" JEDNOZNACZNIE wynika z kontekstu całego posta, że:

  1. Bazy, na których winniśmy się dzisiaj ogniskować, to bazy SQL i właściwie już nawet NoSQL.
  2. Nie sięgać do prehistorii i wyrzucić bazy typu paradox, dbf, bo to szajs!
  3. Winno się stosować mechanizm "pytam-obsługuję-zapisuję". "Pytam" najlepiej wykorzystując QueryByExample (mogę wtedy swobodnie sięgać do wcześniejszych moich zapytań, a użytkownik nie musi się uczyć SQL'a), a "zapisuję" korzystając z najlepszego sposobu zapisu do tych baz, czyli polecenia SQL.

Na tym skupiam nacisk, a nie konkretnie na jakimś sposobie budowania formularza lub obsługi DBGrida!
Tak bystremu obserwatorowi umknęło to?

"bo obecna wersja ma na pewno, ale wersja OnLine to jakiś zabytek i tego nie jestem pewien) alby Ty nie wiesz jak z tego korzystać w Delphi."
"- Bo obecna wersja ma na pewno ......"

Możliwe, że ja nie potrafię pisać w Delphi. Nie wykluczam tego.
Ty natomiast NA PEWNO nie masz zielonego pojęcia o bazie Online-Informix!
Wyobraź sobie, że nawet dzisiaj nie obsłużysz zwrotnie parametru OUT, jeżeli nie otworzysz ".Open" procedury!
Ponadto taką bazę ma klient i Twoje wynurzenia, że to prehistoria mają się nijak do obowiązku obsługiwania jej

Wiemy już, że nie masz zielonego pojęcia o tej bazie.
Zaczynam powątpiewać, czy w ogóle coś więcej wiesz o bazach innych niż plikowe, skoro z taką lubością chcesz korzystać z komponentu TTable.

Pisałem aplikacje zaczynając od Delphi 2 poprzez 3/4/5......i nie zamierzam dłużej ich pisać z wykorzystaniem tego środowiska, choćby nawet wyszła wersja numer 123!
Fajnie było ale się skończyło.
Wiem, że długość używania jakiegoś narzędzia wcale nie determinuje, czy używa się go poprawnie i umiejętnie, ale ostrożny byłbym na Twoim miejscu z zarzutem, że NIE WIEM....jak tego narzędzia używać.

Jedne narzędzia nadają się do określonych rzeczy, a inne nie.
Delphi nie przystaje do dzisiejszego, Webowego świata, tak jak i Twoje zapatrzenie w obsługę baz plikowych typu paradox, czy dbf i zamiłowanie do komponentu TTable.

Życzę Ci, by Twoje aplikacje, które korzystają z komponentów TTable i pośredniego tłumacza BDE, kierującego instrukcje do bazy Paradox, pracowały w sieci rozproszonej i obsługiwane były przez co najmniej kilkuset użytkownikach jednocześnie. Życzę Tobie, by żadnych zakleszczeń w nich nie było i byś nadal potrafił każdy widok i każdy, wymyślony przez administratora bazy, mechanizm usuwania i aktualizowania potrafił obsłużyć poprzez Delete/Append/Update na komponencie TTable!

Wiesz, nie mam zamiaru kontynuować tej dyskusji, gdyż widzę, że zaczynasz personalne wycieczki w stylu "Ty nie wiesz jak korzystać z Delphi"

Rozumiem, że pełnisz tutaj funkcję lokalnego guru i niechcący postawiłem stopę na Twoim poletku.
Mając obecnie świadomość tego, ustępuję bez względu na to, co jeszcze napiszesz w tym wątku.

Guru może być tylko jeden, zatem Ty, paradox i TTable ( :D) zostajecie!

0

Dobrze, teraz udało Ci się zirytować mnie ;-)
Tak jak obiecałem, odpisze Ci na pewno - ale trochę później.

Którego Informixa mam pobrać, żeby sprawdzić Twoje rewelacje?
http://www-01.ibm.com/software/data/informix/downloads.html
Aby się przekonać co do prawdziwości Twoich tez w kontekście procedur i parametrów OUT?
A jeśli nie stamtąd to skąd?

PS.
Zwróciłem Ci uwagę, że nie każde TTable działa tak jak Ci się wydaje. Nie napisałem nigdzie, że jestem zwolennikiem TTable.
Gdybyś przeczytał w miarę uważnie mój kod, zauważyłbyś tam odpowiednik TQuery (a dokładnie jest to rozszerzone TQuery z FireDAC/AnyDAC, do którego dodałem kilka potrzebnych mi mechanizmów) z wbudowanym dynamicznym parserem SQL, a nie TTable...

PS2.
Nie rozpędzaj się tak, bo kod który pokazałeś do budowania formularza wyszukiwania jest... kuriozalny ;-)
Eufemistycznie rzecz ujmując...

0
wloochacz napisał(a):

......zauważyłbyś tam odpowiednik TQuery (a dokładnie jest to rozszerzone TQuery z FireDAC/AnyDAC, do którego dodałem kilka potrzebnych mi mechanizmów) z wbudowanym dynamicznym parserem SQL, a nie TTable...

......

Wiesz, każda kompromitacja ma swoje granice.
Jeżeli uważasz, że mogąc wysyłać bezpośrednio zapytania SQL do bazy, winienem korzystać z parsera, który Twoje "rękodzieło" dopiero przetłumaczy na zapytanie SQL, to chyba mamy różne spojrzenie na temat wydajności i niepotrzebnych pośredników vide Twój parser. :D
Jak Twój parser skorzysta z dialektu określonej bazy i jak na nim wymusisz np instrukcję SQL typu "SELECT cośtam FROM tabela WHERE ....... MATCHES ........."?
Zapytanie do baz NoSQL też Twój parser tłumaczy? :D

Dobra, nie odpowiadaj, bo to pytania retoryczne!

....
Rzucam ręcznik, uciekam z Twojego ogródka, bo widzę z kim dyskutuję.

EOT

Teraz już możesz do woli ujeżdżać! ;-)

0

Gdybyś miał wątpliwości, dlaczego dalszą dyskusję uważam za bezcelową, to wskazówką niech będzie cytat:

wloochacz napisał(a):

....Mam taki a nie inny styl pisania, który nie jest idealny (eufemistycznie rzecz ujmując) ......

Czytam Twoje posty i widzę, że masz jedną, słuszną i zawsze właściwą linię, czyli FireDAC!

Wiesz, były już na świecie nawet partie, które prezentowały jedyną i jedynie słuszną linię.
...One w cywilizowanym świecie też wymarły, a mają się dobrze jeszcze jedynie w Korei!

Żegnam!
Mało czule .... eufemistycznie rzecz ujmując. ;-)

0
BSorbus napisał(a):

Rozumiem, że w swoim życiu nie pisałeś NIGDY programu na bazę Informix-Online Dynamix Server i dlatego ciągle zadajesz pytania:
"O to ciekawe - po co jest ExecProc i Open za chwilę?"
Podobnie

"Rozumiem, że przez ten Open pobierasz jakieś ID z serwera, czy tak?
Skoro tak i spKlient_Ins jest komponentem typu TStoredProc, to dlaczego nie użyłeś parametru output??
Z parametrem outpu ten kod wyglądałby tak:

 spKlient_Ins.ExecProc;
 LastKlientSerial := spKlient_Ins.Params[9].Value;

"

Gdybyś przynajmniej jeden taki program napisał, to wiedziałbyś, że bez tego OPEN, procedura składowana w INFORMIX'ie nie zwróci Ci parametrów OUT i że obsługa w Delphi procedur składowanych dla tego systemu bazy danych jest odmienna niż dla np MS SQL.
Gdybyś przynajmniej jeden taki program napisał, to wiedziałbyś, że nic Ci nie da:
"LastKlientSerial := spKlient_Ins.Params[9].Value"

Gdybym wiedział, to bym nie pytał dlaczego robisz tak a siak.
Natomiast grzecznie zapytałem o bazę w wersji trial, bo chciałem sobie sam sprawdzić. Ale mnie olałeś, dlaczego?
Boisz się, że znów się minąłeś z prawdą czy co?

Pięknie piszesz o automatyzowaniu sortowania i podajesz dobrze napisany przykład.
Przyszło Ci jednak do głowy, że w DBGridzie mogą występować pola tylu Lockup do innych tabel i wtedy Twoje "select cośtam From tabela WHERE tutaj_pole_ktore_należy_do_innej_tabeli_bo_pochodzi_od_Lockup" wywali Ci błąd?

Co to są pola typu Lockup? Masz na myśli lookup? Jeśli tak, to;
Nie wywali błędu, bo pole typu lookup nie jest zdefiniowane w SQL (jego definicja opiera się na dwóch TDataSetach i jest robione w całości na kliencie), a więc parser go nie tknie, bo go nie zna. Poza tym, nie jesteś w stanie sortować po takich polach.
Ale i na to jest sposób - robisz sobie join i sortujesz jak tal-lala.
Poza tym, nie używam pól Lookup w ogóle, robię to zupełnie inaczej - z różnych powodów.
Dziwi mnie to, że przedstawiasz się jako piewca SQLa a tu nagle wyjeżdżasz z czymś takim jak nieoptymalne rozwiązanie dla dużych zestawów danych, czyli pola Lookup.

Dalej....,
Jak zadziałają Twoje złote pomysły na UPDATE/DELETE/INSERT, jeżeli w DBGridzie wyświetlasz widok (view) z bazy danych, które jest złączeniem kilku(nastu) tabel?
Nadal nie widzisz uzasadnienia dla stosowania odpowiednich procedur składowanych, które "wiedzą", co sprawdzić, co zaktualizować i dlaczego

Normalnie, do tego właśnie służy TUpdateSQL czyli do wykonania odpowiedniego kodu SQL (np. procedura składowana) z automatycznym bindowanie wartosci pól z DataSetu źródłowego do procedury, która odpowiada za Insert/Update/Delete/itd.
Czytasz czasem dokumentacje?

oraz potrafią wywołać z wnętrza swojego ciała procedury na innych serwerach?

Raz - o tym mowy nie było, o aktualizacji danych na innych serwerach, a Twoje przykłady były banalne.
Dwa - nie wiem jak w Informix, ale istnieje coś takiego jak LinkedServer.
Trzy - to i tak pomysł od czapy z takimi wymaganiami, bo powinien być AppSerer, który tym zarządza. Ale to naprawdę nie miejsce (mam na myśli ten konkretnie wątek) na taką dyskusję...

Podaj mi proszę pomysł, by kontrolować poprawność wprowadzanych danych w zależności od zawartości bazy danych, jeżeli nie stosujesz mechanizmów, które pytają bazę?
Oczywiście validację danych można w jakimś amoku zrobić w aplikacji klienta, ale czy nie wydaje się Tobie, że poprawniej będzie przerzucić to na stronę serwera bazy danych?

Jest różnica pomiędzy odpytywanie bazy danych a pisaniem procedury składowanej do każdego pierdnięcia.

Przykład:
Użytkownik może wprowadzić do bazy danych tylko klienta, który zamieszkuje na terenie Pścimia Dolnego, chyba, że data jego urodzenia >= 01.09.1939 oraz innej bazie, na innym serwerze liczba zarejestrowanych mieszkańców w Pścimiu nie przekroczyła pewnej wartości ustalanej przez parametry zgromaczonej w bazie trzeciej na jeszcze innym serwerze.

Nadal chcesz to robić przy pomocy swoich cudownych mechanizmów, czy może rozważysz "rękodzieło" polegające na wywołaniu procedury składowanej i zezwoleniu użytkownikowi przeanalizować zwrócone komunikaty?

Wykraczasz daleko poza temat wątku. ale skoro tak, to odpowiem Ci - powinien to być serwer aplikacyjny.
Ale równie dobrze możesz pisać procedury - ja jestem na to zbyt leniwy.

Można oczywiście zwrócić użytkownikowi -1 z komunikatem "nie udało zapisać się danych", ale nadal będzie on w czarnej dupie, gdyż nie będzie wiedział, dlaczego nie udało się zapisać.
A co z ostrzeżeniami, gdyby jednak chciał NA SIŁĘ i ŚWIADOMIE wpisać gościa, pomimo tego, że np. ze względu na brak połączenia z 3-cim serwerem, nie można ustalić aktualnie liczby mieszkańców Pścimia?

Wyobraź sobie, że w ramach "rękodzieła" zwracasz odpowiednie komunikaty opatrzone klauzulą błąd krytyczny lub nie, a użytkownik, gdy ewentualnie wyświetli mu się lista błędów niekrytycznych, będzie mógł jednak dokonać takiej rejestracji danych w bazie. Podoba się? jesteś przekonany, że klasyczna obsługa błędów serwera SQL też Ci da taką możliwość?

Wszystko zależy od tego, jak będziesz zarządzał własnymi wyjątkami pochodzącymi z serwera. Ale to i tak od czapy, bo nie masz centralnego zarządzania walidacją.
Ale co to ma do rzeczy? Dlaczego uciekasz od jasno postawionych przeze mnie pytań, które dotyczyły podstaw?
Chcesz i udowodnić, że tak ma być bo udało się Ci zrobić coś tam?

Umówmy się zatem, że ja akceptuję Twój, odmienny punkt widzenia na pewne sprawy, a Ty zaakceptujesz FAKT, że aplikacja klienta, która łączy się z z jedną z 20 baz, a z pozostałymi poprzez wewnętrzne wywołania triggerów i procedur składowanych dual-commit, winna aktualizować (wprowadzać/usuwać) dane z wykorzystaniem procedur składowanych, a nie delete na komponencie TTable.

TTable to był przykład.
Co do reszty - zafiksowałeś się jak uparty sześciolatek.

Pozdrawiam :)

P.S.
Jeszcze gwoli wyjaśnienia tych UPPER....
Dopuszczasz taką myśl, że system baz danych 20 lat temu, nie był konfigurowalny na rozróżnianie lub nie małych i dużych liter?
Ponadto klient miał kaprys by odróżniać KOWALSKIEGO i Kowalskiego. Nadal będziesz omijał UPPER w swoich zapytaniach SQL?

Jak ognia i napisałem ci dlaczego.
Słyszałeś kiedykolwiek o tzw. expression index?

Dokonując tak głębokiej analizy mojego posta, zapewne zauważyłeś, że pisałem o aplikacjach bazodanowych i nawet przywoływałem jakieś tam przykłady przetwarzania setek tysięcy rekordów.

Nie zauważyłem tam żadnych setek a tym bardziej tysięcy.
Natomiast zauważyłem kuriozalne rozwiązania i taką ilość niepotrzebnego kodu, że aż strach.</quote>

Sądzisz, że pisałem o bazie PARADOX?

Nie, to @Wiszu pisał o takiej bazie a Ty się wciąłeś pomiędzy wódkę a zakąskę.

Uważam, że bazy tego typu już dawno powinny zostać pogrzebane i zdecydowanie LEPSZE, BEZPIECZNIEJSZE oraz SZYBSZE będą w takich zastosowaniach bazy SQL. Zamiast się się szarpać z beznadziejnym PARADOXEM, to już lepiej zainstalować małego, szybkiego i DARMOWEGO Firebirda, który jest dostępny na każdą platformę Linux i każdą Windę.

Pełna zgoda.

Rzecz jasna Ty możesz używać TTable, prostego Delete i nawet Paradox'a do obsługi wszystkich systemów, które oprogramowujesz.

Irytująca nadinterpretacja z Twojej strony.

Ciekawe, kto inwestuje w przyszłość?
Ten, który uczy się sprawnie posługiwać komponentem TTable, czy ten, który myśli o małych, ale i ogromnych bazach SQL, z którymi komunikował się będzie poprzez TQuery i TStoredProc?

Problem polega na tym, że ktoś ci kiedyś powiedział że SQL jest lekiem na wszystko.
A Ty nie masz pojęcia co tak naprawdę dzieje się niżej, pod spodem takiego TTable czy innego TQuery.
I na tej podstawie ferujesz wyroki, tylko widzisz... Znalazł się ktoś, kto ośmiela burzyć Twój światopogląd.
Zwracam Ci uwagę na konkrety, które konsekwentnie olewasz.

//"Twoje TQuery z procedurą odświeżania, którą podałeś ma spore szanse ślicznie się wywalić z komunikatem out of memory właśnie na takiej tabeli, która ma ze 2 mln rekordów. Używasz tam Locate, które działa LOKALNIE, a więc pobiera wszystko do bufora i przeszukuje za pomocą zwykłe, prostej iteracji. Jak będzie szukać czegoś przy końcu takiego zestawu danych – sam wiesz, co się stanie."
//

  • Primo, Locate używam lokalnie na małym (wybranym już) zbiorze.

Nie było tego widać w przykładzie, za to z lubością odnosisz się że takie rozwiązanie jest super-skalowalne, a w środku przy odświeżaniu danych jest... Locate.

  • Secundo, jeżeli ktoś jest idiotą, który stosuje zapytania SQL z klauzulą WHERE po to, by lokalnie ściągnąć sobie 2 miliony rekordów z jakiegoś odległego serwera i dalej wśród nich coś wyszukiwać, to chyba nie powinien być dopuszczany do komputera.

A jeśli ktoś "programuje" nie zadając sobie trudu jak działa konkretne narzędzie, to kim jest?

Ja na takie harce, to użytkownikom nie pozwalam, gdyż tak wielki cursor nie zostanie nawet przez serwer bazy zwrócony.

EOT</quote>
Że co proszę? A to niby dlaczego serwer go nie zwróci?
Każdy z którym miałem do czynienia zrobi to bez bólu, ale osobną sprawą jest co z taką ilość danych stanie się na kliencie.

No i widzisz - zapytałem o proste sprawy i de-facto nie odpowiedziałeś mi na żadne moje pytanie.

0
BSorbus napisał(a):

Sądziłem, że wstępnie zaznaczyłem, że chodzi mi raczej o wyrabianie nawyków określonych i ogólne podejście do tworzenia aplikacji KlientDelphi-BazaDanych SQL.

Tak? To napisz artykuł, tak jak Ci niektórzy proponowali.
Będę miał ubaw po pachy ;-)

Pisząc "tak się powinno pisać" miałem na myśli, co "chyba" JEDNOZNACZNIE wynika z kontekstu całego posta, że:

  1. Bazy, na których winniśmy się dzisiaj ogniskować, to bazy SQL i właściwie już nawet NoSQL.

NoSQL? Ciekawe co jeszcze tu dołożysz, coraz lepiej...
Naprawdę uważasz że NoSQL jest najlepsze do wszystkiego?
To tak jakby zadać pytanie - co jest lepsze, prom kosmiczny czy prom wodny. Przecież to i to jest prom...

  1. Nie sięgać do prehistorii i wyrzucić bazy typu paradox, dbf, bo to szajs!

Dziś już tak, ale z tym szajsem to przesada. Są różne potrzeby i istnieją różne możliwości.
Tak samo nie da się jednoznacznie powiedzieć, że NoSQL jest lepszy od RDBMS, to zupełnie inne rozwiązania dedykowane to zupełnie innych zastosowań.
Wtrynianie tego do jednego worka jest dyletanctwem.

  1. Winno się stosować mechanizm "pytam-obsługuję-zapisuję". "Pytam" najlepiej wykorzystując QueryByExample (mogę wtedy swobodnie sięgać do wcześniejszych moich zapytań, a użytkownik nie musi się uczyć SQL'a), a "zapisuję" korzystając z najlepszego sposobu zapisu do tych baz, czyli polecenia SQL.

A znasz inny sposób na komunikację z bazą SQL (w kontekście przetwarzania danych) niż za pomocą SQL'a?
No więc właśnie.
Zatem wystaw sobie, że nawet jeśli ja w swoim TFDTable wykonam Delete, to de-facto wykonane zostanie polecenie:

delete from table where PK =  :PK

A więc użyje SQLa...

Na tym skupiam nacisk, a nie konkretnie na jakimś sposobie budowania formularza lub obsługi DBGrida!
Tak bystremu obserwatorowi umknęło to?

80% Twojego kodu w tym wątku dotyczyło budowania formularza wyszukiwania i to jeszcze w sposób godny programisty bez dużego doświadczenia...
Wyszukiwania!!
Czyli operacji, która powinna zostać zrobiona w sposób elastyczny, tak aby móc wyszukiwać dane w dowolnym zbiorze danych.
I to właśnie nazywam popeliną.

"bo obecna wersja ma na pewno, ale wersja OnLine to jakiś zabytek i tego nie jestem pewien) alby Ty nie wiesz jak z tego korzystać w Delphi."
"- Bo obecna wersja ma na pewno ......"

Możliwe, że ja nie potrafię pisać w Delphi. Nie wykluczam tego.
Ty natomiast NA PEWNO nie masz zielonego pojęcia o bazie Online-Informix!
Wyobraź sobie, że nawet dzisiaj nie obsłużysz zwrotnie parametru OUT, jeżeli nie otworzysz ".Open" procedury!
Ponadto taką bazę ma klient i Twoje wynurzenia, że to prehistoria mają się nijak do obowiązku obsługiwania jej

Poprosiłem o przykład tej bazy, tak abym mógł sam to zbadać.
Bazując na tym co wcześniej napisałeś, nie mogę wierzyć Ci na słowo - a nuż znowu mijasz się z prawdą?

Wiemy już, że nie masz zielonego pojęcia o tej bazie.

Do czego szczerze się przyznaję.

Zaczynam powątpiewać, czy w ogóle coś więcej wiesz o bazach innych niż plikowe, skoro z taką lubością chcesz korzystać z komponentu TTable.

Rozumiem, że zaczyna brakować argumentów merytorycznych, zatem będą wycieczki osobiste?
Spoko - tym bardziej umacniasz mnie w przekonaniu, że nie bardzo wiesz jak to można napisać inaczej.
Lepiej, elastyczniej i szybciej - czyli po prostu "jak powinno się pisać aplikacje bazodanowe w Delphi".

Pisałem aplikacje zaczynając od Delphi 2 poprzez 3/4/5......i nie zamierzam dłużej ich pisać z wykorzystaniem tego środowiska, choćby nawet wyszła wersja numer 123!

To przyjmij do wiadomości, że czasy się ciut zmieniły i to co prezentujesz cuchnie naftaliną starej szafy.

Fajnie było ale się skończyło.
Wiem, że długość używania jakiegoś narzędzia wcale nie determinuje, czy używa się go poprawnie i umiejętnie, ale ostrożny byłbym na Twoim miejscu z zarzutem, że NIE WIEM....jak tego narzędzia używać.

A wiesz? Bo na razie to ja widziałem mnóstwo kodu w stylu
if then else
oraz jakiegoś ręcznego sklejania stringów żeby zbudować polecenie SQL.
Do tego jakiś spaghetti code i mnóstwo pomieszanego kodu zależnego od SQL i kontrolek.
Wybacz, ale ten Twój kod wygląda na idealny przykład (anty)wzorca projektowo zwanego Big ball of mud. Poczytaj sobie co to jest.
O wzorcach projektowych tez poczytaj.

Poza tym - skoro ostatnio pisałeś w Delphi 5, to nie rozumiem jakim cudem uknułeś sobie w głowie, że napiszesz tekst o tym "jak programować w Delphi"?
Do wersji 2007/2009 włącznie, mogłeś tak myśleć - bo zmieniło się faktycznie niewiele.
Od wersji 2010 możliwości samego języka wzrosły wielokrotnie!

Jedne narzędzia nadają się do określonych rzeczy, a inne nie.
Delphi nie przystaje do dzisiejszego, Webowego świata, tak jak i Twoje zapatrzenie w obsługę baz plikowych typu paradox, czy dbf i zamiłowanie do komponentu TTable.

Po pierwsze - nie cały świat oprogramowania jest Webowy.
Po drugie - Delphi to nie tylko desktopowe aplikacje Windows.
Po trzecie - zatkałeś się na wmawianiu mi umiłowanie do TTable, powtórzę się - zaczyna Ci argumentów brakować?

Życzę Ci, by Twoje aplikacje, które korzystają z komponentów TTable i pośredniego tłumacza BDE, kierującego instrukcje do bazy Paradox, pracowały w sieci rozproszonej i obsługiwane były przez co najmniej kilkuset użytkownikach jednocześnie. Życzę Tobie, by żadnych zakleszczeń w nich nie było i byś nadal potrafił każdy widok i każdy, wymyślony przez administratora bazy, mechanizm usuwania i aktualizowania potrafił obsłużyć poprzez Delete/Append/Update na komponencie TTable!

To się da zrobić, tylko trzeba znać swój warsztat.
Ale nie - nie robiłem tego nigdy w ten sposób i robił nie będę, także odpuść sobie już wmawianie mi czegoś, czego nie powiedziałem, bo nuuuudne się robi.

Wiesz, nie mam zamiaru kontynuować tej dyskusji, gdyż widzę, że zaczynasz personalne wycieczki w stylu "Ty nie wiesz jak korzystać z Delphi"

He he - ja zaczynam?
OK, niech będzie że to moja wina.

Rozumiem, że pełnisz tutaj funkcję lokalnego guru i niechcący postawiłem stopę na Twoim poletku.
Mając obecnie świadomość tego, ustępuję bez względu na to, co jeszcze napiszesz w tym wątku.

Guru może być tylko jeden, zatem Ty, paradox i TTable ( :D) zostajecie!

...a teraz poczytaj to co napisałeś o osobistych wycieczkach.

0
BSorbus napisał(a):
wloochacz napisał(a):

......zauważyłbyś tam odpowiednik TQuery (a dokładnie jest to rozszerzone TQuery z FireDAC/AnyDAC, do którego dodałem kilka potrzebnych mi mechanizmów) z wbudowanym dynamicznym parserem SQL, a nie TTable...
......

Wiesz, każda kompromitacja ma swoje granice.

No popatrz - im więcej piszesz, tym bardziej widać ze to nie prawda.

Jeżeli uważasz, że mogąc wysyłać bezpośrednio zapytania SQL do bazy, winienem korzystać z parsera, który Twoje "rękodzieło" dopiero przetłumaczy na zapytanie SQL, to chyba mamy różne spojrzenie na temat wydajności i niepotrzebnych pośredników vide Twój parser. :D

Nie zrozumiałeś przykładu.
Co więcej - mam wrażenie nie wiesz co to jest ParserSQL i do czego to można wykorzystać. A można to naprawę wielu rzeczy.
Ja wykorzystuję go do dynamicznej modyfikacji kodu SQL, bez konieczności zastanawiania się jak posklejać stringi.
Używam go też do rozbioru "zdania" SQL, po to aby móc dynamicznie utworzyć np. złączenia, dodać warunki czy zmodyfikować klauzulę order by.
Dzięki temu mogę napisać bardzo elastyczny kod, który zadziała z dowolnym zapytaniem SQL, bez konieczności dostosowania tego kodu do każdego zapytania.
Jest to możliwe, bo masz obiektowy dostęp do kodu SQL i możesz nim dowolnie manipulować - również w sposób obiektowy.
Jednak zmartwię Cię - zazwyczaj wszystkie parsery bazują na wzorcu Visitor, który jest ciut trudniejszy do opanowania niż proste sklejanie SQLa za pomocą dziesiątek if then else

Co do wydajności; rozbiór nawet skomplikowanego zapytanie to ułamki milisekund.
Chcesz mi powiedzieć, że tak "ogromny" narzut ma znaczenie a bilans korzyści jest na korzyść ręcznego składania zapytań, czy tak?

Jak Twój parser skorzysta z dialektu określonej bazy i jak na nim wymusisz np instrukcję SQL typu "SELECT cośtam FROM tabela WHERE ....... MATCHES ........."?

Oczywiście są ograniczenia i nie każdy parser wspiera wszystkie możliwe dialekty, ale wszystkie typowe zapytania działają bez bólu i ot tak.
To co jest nietypowe, lub kiedy chcę skorzystać z dedykowanych rozwiązań dla konkretnej bazy danych, zawsze mogę otoczyć procedurą/widokiem/funkcją.
Ale tylko nietypowe - czyli nie więcej jak ile? Bo ja wiem, 5% maks 10% całego kodu.
Reszta to automat.
I to jest ogromny zysk!
Utrzymanie i rozszerzanie jest wielokrotnie prostsze i tańsze.
Poza tym, pozwala się skupić na tym co najważniejsze - a nie na klepaniu kolejnego SELECTa tylko w ciut innej konfiguracji...

Poza tym, mi nie jest potrzebna obsługa na tym samym poziomie automatyzacji wszystkich baz danych na świecie.
I na koniec - tak, parser którego używam w pełni wspiera dialekt mojej podstawowej bazy danych, czyli MSSQL.

Zapytanie do baz NoSQL też Twój parser tłumaczy? :D

NoSQL? Ty tak poważnie?
Chciałbym zobaczyć jak na NoSQL budujesz poważną aplikację np. księgową.
Kilofem też się da dłubać w nosie...

Dobra, nie odpowiadaj, bo to pytania retoryczne!

Bo i tak brakuje Ci argumentów czy i tak nie zrozumiesz?

....
Rzucam ręcznik, uciekam z Twojego ogródka, bo widzę z kim dyskutuję.
EOT

Teraz już możesz do woli ujeżdżać! ;-)

Który to już raz EOT rzucasz w tym wątku?

0
BSorbus napisał(a):

Gdybyś miał wątpliwości, dlaczego dalszą dyskusję uważam za bezcelową, to wskazówką niech będzie cytat:

wloochacz napisał(a):

....Mam taki a nie inny styl pisania, który nie jest idealny (eufemistycznie rzecz ujmując) ......

Czytam Twoje posty i widzę, że masz jedną, słuszną i zawsze właściwą linię, czyli FireDAC!

A tak, używam tylko FireDAC'a i z tego samego powodu do prezentacji danych używam tylko DeveloperExpress,a do wydruków FastReporta.
I nic innego, żadnych JVCL itd.
To wynika z efektywności ekonomicznej - uważam, że lepiej znać bardzo dobrze jedną bibliotekę niż troszkę wszystkie inne konkurencyjne.
A najlepiej skupić się na najlepszych dostępnych rozwiązaniach.
Poza tym, FireDAC to klasa sama dla siebie w dziedzinie DAL dla Delphi.
No, ale Ty przecież nie możesz tego wiedzieć, skoro przygodę z Delphi skończyłeś na wersji 5.0

Wiesz, były już na świecie nawet partie, które prezentowały jedyną i jedynie słuszną linię.
...One w cywilizowanym świecie też wymarły, a mają się dobrze jeszcze jedynie w Korei!

Przepraszam, ale o co chodzi? Co to za bzdury i co to ma do rzeczy?
Aaa... no tak - znów zabrakło Ci argumentów, tak mi przykro...

Żegnam!
Mało czule .... eufemistycznie rzecz ujmując. ;-)

I vice-versa.

0
wloochacz napisał(a)

Tak? To napisz artykuł, tak jak Ci niektórzy proponowali.

Nie "niektórzy", tylko ja (jestem jeden - wybacz rozczarowanie) - zasugerowałem stworzenie artykułu, dlatego że forum to nie miejsce na tak długie teksty; Od tego mamy dział Artykuły, w którym można zamieszczać teksty dowolnej długości, a społeczność serwisu chętnie lub niechętnie zapozna się z tekstem i go oceni, bądź także skomentuje;


Widzę, że ten wątek doprowadził do prywatnej wojenki na temat stricte "kto ma dłuższego", w takim razie wątek ze względów bezpieczeństwa zamykam.

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