Calculated field gibi davranan bir field inşa etmek (MySQL)

Delphi'de kod yazma ile ilgili sorularınızı bu foruma yazabilirsiniz.
Cevapla
ertank
Üye
Mesajlar: 1088
Kayıt: 11 Eyl 2015 11:45

Re: Calculated field gibi davranan bir field inşa etmek (MySQL)

Mesaj gönderen ertank » 25 Kas 2017 01:33

fatihbarut yazdı:
25 Kas 2017 09:48
Arkadaşlar,
MySQL'de sayı ile fiyatı çarpıp toplam fiyat gösteren bir calculated field'ım vardı. Herşey mutlu mesut gidiyordu.
Derken Unidump ile veri tabanını yedekleyip geri aldım ve virtual fieldları desteklemediğini gördüm.
Şimdi bu toplam ücret virtual fieldını normale çevirip aynı işi triger'la yapmam gerekecek.
Nasıl olacağı hakkında fikri olan var mı?
teşekkürler
Merhaba,

Benim anladığım kadarıyla bahsettiğiniz özellik MySQL generated columns özelliği
https://dev.mysql.com/doc/refman/5.7/en ... lumns.html
Yine benim anladığım VIRTUAL olarak tanımlanmış generated colum değeri disk üzerinde saklanmaz.
As of MySQL 5.7.6, CREATE TABLE supports the specification of generated columns. Values of a generated column are computed from an expression included in the column definition.
Bu durumda TUniDump ile aldığınız bilgileri ilgili VIRTUAL kolon verileri olmadan aynı tanıma sahip boş başka bir MySQL tablosuna yüklediğiniz zaman ilgili VIRTUAL kolonlarınız hesaplanarak yine aynı değerleri vermesi gerekli.

Dışa aktarılan verileri yeni bir tablo içine yükleyerek aynı değerleri görüp görmediğinizi hiç test ettiniz mi?

ertank
Üye
Mesajlar: 1088
Kayıt: 11 Eyl 2015 11:45

Re: Calculated field gibi davranan bir field inşa etmek (MySQL)

Mesaj gönderen ertank » 25 Kas 2017 11:10

Az önce test ettiğim kadarıyla TUniDump yedeklemesinde bütün kolonları yedeğe dahil ediyor. Özetle "SELECT * FROM <tablo_adi>" komutu ile listelenen tüm kolonlar yedeğe dahil edilmiş oluyor.

Ancak MySQL için VIRTUAL kolonların yedeğe dahil edilmesi geri yüklemenin yapılamamasına sebep oluyor. Çünkü MySQL VIRTUAL tanımlı kolonlara veri ataması yapılmasına izin vermiyor.

Sorun için aşağıdaki şekilde dolaylı bir çözüm mümkün olabilir. Aşağıdaki gibi bir tablo tanımınız olduğunu düşünelim:

Kod: Tümünü seç

CREATE TABLE `deneme` (
  `abc` varchar(10) COLLATE utf8_turkish_ci DEFAULT NULL,
  `i1` int(11) DEFAULT NULL,
  `i2` int(11) DEFAULT NULL,
  `calc` int(11) GENERATED ALWAYS AS ((`i1` + `i2`)) VIRTUAL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_turkish_ci;
İçindeki veriler aşağıdaki gibi olsun:

Kod: Tümünü seç

# abc, i1, i2, calc
'abc1', '1', '2', '3'
'abc2', '2', '2', '4'
'abc3', '3', '2', '5'
'abc4', '4', '2', '6'
'abc5', '5', '2', '7'
'abc6', '6', '2', '8'
Aşağıdaki gibi bir kod kullanarak sadece gerekli kolonlara ait verilerin yedeklenmesini sağlamak mümkün olacaktır:

Kod: Tümünü seç

  UniDump1.BackupQuery('select abc,i1,i2 from deneme');
  UniDump1.SQL.SaveToFile('test.sql');
Oluşan yedek dosyasının içeriği aşağıdaki gibi olacaktır:

Kod: Tümünü seç

-- UniDAC version: 7.1.3
-- MySQL server version: 5.7.20
-- MySQL client version: 8.0.0 Direct
-- Script date 26/11/2017 00:02:09
-- ---------------------------------------------------------------------- 
-- Server: 192.168.1.101
-- Database: test

/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
-- 
-- Dumping data for table deneme
-- 

TRUNCATE TABLE deneme;
INSERT INTO deneme(abc, i1, i2) VALUES
 ('abc1',1,2),
 ('abc2',2,2),
 ('abc3',3,2),
 ('abc4',4,2),
 ('abc5',5,2),
 ('abc6',6,2);

/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
Bu dosyanın geri yüklenmesinde de bir sorun oluşmayacaktır.

Bu işlemin zorluğu tüm database yedeklemesi için her bir tabloyu ayrı ayrı ve eğer var ise tabloya özel SELECT komutu ile yedeklemek gerekmesidir.

ertank
Üye
Mesajlar: 1088
Kayıt: 11 Eyl 2015 11:45

Re: Calculated field gibi davranan bir field inşa etmek (MySQL)

Mesaj gönderen ertank » 26 Kas 2017 05:00

Aşağıdaki kod örneği ile database içindeki tüm kullanıcı tarafından oluşturulan tablo ve bu tablo kolon isimlerini VIRTUAL kolonlar filtrelenmiş şekilde text dosyasına kaydedebilirsiniz. İlgili text dosyalarını TUniDump için yükleyip kullanmanız halinde VIRTUAL kolon sorununuz kalmamış olur. Sadece TUniDump ile ilgili kodunuzda bir miktar düzenleme yapmanız gerekecektir.

Kod: Tümünü seç

procedure GetRealFields(Connection: TUniConnection; const TableName: string; List: TStrings);
var
  Query: TUniQuery;
begin
  Query := TUniQuery.Create(nil);
  try
    Query.Connection := Connection;
    Query.SQL.Text := 'show columns from ' + TableName + ' where Extra <> ''VIRTUAL GENERATED''';
    Query.Open();

    List.Clear();
    while not Query.Eof do
    begin
      List.Add(Query.Fields[0].AsString);
      Query.Next();
    end;
  finally
    Query.Free();
  end;
end;

procedure DumpTablesFields(Connection: TUniConnection);
var
  TableList: TStringList;
  FieldList: TStringList;
  SW: TStreamWriter;
  I: Integer;
  I2: Integer;
  TempString: string;
begin
  TableList := TStringList.Create();
  try
    Connection.GetTableNames(TableList);
    FieldList := TStringList.Create();
    try
      for I := 0 to Pred(TableList.Count) do
      begin
        GetRealFields(Connection, TableList[I], FieldList);
        SW := TStreamWriter.Create(TableList[I] + '.txt', False, TEncoding.UTF8);
        try
          TempString := 'SELECT ';
          for I2 := 0 to Pred(FieldList.Count) do
          begin
            TempString := TempString + FieldList[I2] + ',';
          end;
          if FieldList.Count > 0 then SetLength(TempString, Length(TempString) - 1);
          TempString := TempString + ' FROM ' + TableList[I];

          SW.WriteLine(TempString);
        finally
          SW.Free();
        end;
      end;
    finally
      FieldList.Free();
    end;
  finally
    TableList.Free();
  end;
end;

ertank
Üye
Mesajlar: 1088
Kayıt: 11 Eyl 2015 11:45

Re: Calculated field gibi davranan bir field inşa etmek (MySQL)

Mesaj gönderen ertank » 26 Kas 2017 01:24

ShellAPI unitesi içindeki ShellExecute() fonksiyonunu inceleyebilirsiniz.

https://msdn.microsoft.com/en-us/librar ... s.85).aspx

ertank
Üye
Mesajlar: 1088
Kayıt: 11 Eyl 2015 11:45

Re: Calculated field gibi davranan bir field inşa etmek (MySQL)

Mesaj gönderen ertank » 26 Kas 2017 01:47

https://stackoverflow.com/questions/134 ... mmand-line
http://www.mediacollege.com/computer/da ... ackup.html
http://www.thegeekstuff.com/2008/09/bac ... mysqldump/
https://meta.wikimedia.org/wiki/Databas ... e_examples

Benim gördüğüm kadarıyla parametreler özel durumlarda kullanılıyor ve genel kullanım şekli aşağıdaki gibi:

Kod: Tümünü seç

mysqldump  -u[username] -p[password] databasename > databasefilename.sql

ertank
Üye
Mesajlar: 1088
Kayıt: 11 Eyl 2015 11:45

Re: Calculated field gibi davranan bir field inşa etmek (MySQL)

Mesaj gönderen ertank » 26 Kas 2017 03:12

Virtual kolonlar çalışma zamanında sürekli hesaplanırlar. Tablo içinde kaydedilmezler. Bu sebeple yedek verisi içine kaydetmenin bir anlamı olmayabilir.
Bir istisnası VIRTUAL değil STORED şeklinde tanımlanmış kolonlar olabilir. Denemedim ancak belki STORED şeklindeki kolonlar yedek içine dahil ediliyor olabilir. Bu tür kolonlar çalışma zamanında hesaplanmayarak INSERT/UPDATE sırasında hesaplanır ve tabloya (disk üzerine) kaydedilir.

ertank
Üye
Mesajlar: 1088
Kayıt: 11 Eyl 2015 11:45

Re: Calculated field gibi davranan bir field inşa etmek (MySQL)

Mesaj gönderen ertank » 26 Kas 2017 03:51

Test ettiğim kadarıyla STORED şeklinde olsa dahi GENERATED türündeki kolon verileri yedeğe dahil edilmiyor. Bununla birlikte MySQL yedeğini program içinden almak için aşağıdaki gibi bir kod kullanılabilir:

Kod: Tümünü seç

function GetDosOutput(CommandLine: string; Work: string = 'C:\'): string;
var
  SA: TSecurityAttributes;
  SI: TStartupInfo;
  PI: TProcessInformation;
  StdOutPipeRead, StdOutPipeWrite: THandle;
  WasOK: Boolean;
  Buffer: array[0..255] of AnsiChar;
  BytesRead: Cardinal;
  WorkDir: string;
  Handle: Boolean;
begin
  Result := '';
  with SA do begin
    nLength := SizeOf(SA);
    bInheritHandle := True;
    lpSecurityDescriptor := nil;
  end;
  CreatePipe(StdOutPipeRead, StdOutPipeWrite, @SA, 0);
  try
    with SI do
    begin
      FillChar(SI, SizeOf(SI), 0);
      cb := SizeOf(SI);
      dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
      wShowWindow := SW_HIDE;
      hStdInput := GetStdHandle(STD_INPUT_HANDLE); // don't redirect stdin
      hStdOutput := StdOutPipeWrite;
      hStdError := StdOutPipeWrite;
    end;
    WorkDir := Work;
    Handle := CreateProcess(nil, PChar('cmd.exe /C ' + CommandLine),
                            nil, nil, True, 0, nil,
                            PChar(WorkDir), SI, PI);
    CloseHandle(StdOutPipeWrite);
    if Handle then
      try
        repeat
          WasOK := ReadFile(StdOutPipeRead, Buffer, 255, BytesRead, nil);
          if BytesRead > 0 then
          begin
            Buffer[BytesRead] := #0;
            Result := Result + string(Buffer);
          end;
        until not WasOK or (BytesRead = 0);
        WaitForSingleObject(PI.hProcess, INFINITE);
      finally
        CloseHandle(PI.hThread);
        CloseHandle(PI.hProcess);
      end;
  finally
    CloseHandle(StdOutPipeRead);
  end;
end;
Kaynak: https://stackoverflow.com/questions/911 ... delphi-app

Kullanımı aşağıdaki şekilde olacaktır:

Kod: Tümünü seç

var
  Dump: TStringList;
begin
  Dump := TStringList.Create();
  try
    Dump.Text := GetDosOutput('"C:\Program Files\MySQL\MySQL Server 5.7\bin\mysqldump.exe" -u<kullanıcı> -p<şifre> <database> 2>nul');
    Dump.SaveToFile(FormatDateTime('yyyy-mm-dd_hh.nn', Now()) + '.sql');
    ShowMessage('Yedekleme tamamlandı');
  finally
    Dump.Free();
  end;
Eğer son sürümlerden bir Delphi kullanıyor isterseniz TZipFile sınıfını kullanarak yerden kazanmak için yedeği sıkıştırmanız mümkün.

Kod: Tümünü seç

uses
  Zip;

var
  Dump: TStringStream;
  ZipFile: TZipFile;
  FileName: string;
begin
  FileName := IncludeTrailingPathDelimiter(ExtractFilePath(Application.ExeName)) +  FormatDateTime('yyyy-mm-dd_hh.nn', Now()) + '.zip';
  ZipFile := TZipFile.Create();
  try
    if FileExists(FileName) then DeleteFile(FileName);
    ZipFile.Open(FileName, zmWrite);

    Dump := TStringStream.Create(GetDosOutput('"C:\Program Files\MySQL\MySQL Server 5.7\bin\mysqldump.exe" -u<kullanıcı> -p<şifre> <database> 2>nul'));
    try
      ZipFile.Add(Dump, ChangeFileExt(ExtractFileName(FileName), '.sql'));
      ZipFile.Close();
      ShowMessage('Yedekleme tamamlandı');
    finally
      Dump.Free();
    end;
  finally
    ZipFile.Free();
  end;
end;
Not: Eğer sıkıştırılmamış yedek boyutu 1GB gibi bir değere ulaşır ise mysqldump.exe'ye direk dump dosyasını yazması şeklinde bir parametre vermek daha anlamlı olacaktır. Uygulama windows hafıza kullanım limitlerine takılabilir.

Cevapla