В последнее время все чаще замечаю увеличение внимания к такой СУБД как PostgreSQL. Почитал на хабре пакет статей о том что PostgreSQL лучше чем Mysql. И решил попробовать данную систему на деле.
Стал смотреть через какие компоненты можно работать с Pg из Delphi. Таких набралось немного.
PgDAC от Devart
Firedac (AnyDac) от Embarcadero
ZeosLib от ZeosLib Development Team
Так же позже нашел PostgresDAC от microOLAP
Возможно имеются ещё какие то компоненты , но я не заострял на этом внимания. И того мы имеем 2 платных набора компонента и 1 бесплатный. Вспомнил еще про Unidac от Devart, но там несколько другая тема. По опыту работы с базами данных я решил заострить свое внимание на PgDAC, поскольку уже имею опыт работы с компонентами Devart, в частности пользовался SDAC для MsSQL, MyDAC для MySQL и LiteDAC для SQLite.
В общем скачал с сервера установщик PostgreSQL для Windows. Установил его на виртуальную машину, правда пришлось немного порыться в интернете как правильно его установить. Хотя инсталлятор и простой, не сравнить с инсталлятором от того же самого Microsoft SQL Server, но нормально Pg устанавливается только из под пользователя не содержащего в имени кириллицу и имеющего все права на папку куда будет производиться установка. Но это частности.
Установил СУБД, добавил пользователя и создал небольшую базу учета товара и заказов. Уж если и проверять работу компонентов то на тестовой программе, где можно проверить их работу.
Затем скачал с сайта Devart компонент PgDac 4.6.20 триальную версию. Для тестов этого вполне хватит. Для компонентов имеется удобный установщик, который все сам сделает и настроит.
В Delphi 7 появилась вкладка PgDAC.
На ней содержаться следующие компоненты.
TPgConnection компонент для управления подключения к базам PostgreSQL | |
TPgQuery компонент для выполнения запросов к базе и работы с наборами данных | |
TPgTable компонент для работы с данными в пределах одной таблицы | |
TPgSQL компонент для выполнения sql запросов или хранимых процедур без возврата значений. | |
TPgScript компонент для работы с большим sql кодом | |
TPgStoredProc компонент для работы с хранимыми процедурами | |
TPgUpdateSQL компонент для управления обновлением данных в Dataset компоненте | |
TPgLoader компонент для загрузки данных в базу данных | |
TPgDump компонент для сохранения и восстановления баз данных | |
TPgEncryptor компонент для шифрования данных | |
TCRBatchMove компонент для перемещения данных между наборами данных | |
TPgMetaData получение метаданных о объектах в базе денных | |
TPgAlerter компонент для работы с уведомлениями сервера | |
TPgConnectDialog диалоговое окно для подключения к серверу | |
TPgSQLMonitor компонент для мониторинга работы PgDac | |
TVirtualTable компонент для хранения данных в оперативной памяти | |
TCRDBGrid довольно таки продвинутый датагрид | |
TPgDataSource прокладка между источником данных и компонентами для отображения данных |
Поскольку я делал тестовый пример под Delphi 7, то я не буду описывать использование данных компонент для мобильной разработки. А коснусь только десктопных приложений.
В общем создал я в среде разработки вот такое вот приложение.
Это простая программа для учета товара и заказов. Не старался особо заморачиваться по поводу функционала, главное что бы можно было проверить работу компонентов.
А теперь более подробно про компоненты.
TPgconnection - подключение к базе. Данный компонент используется прямое подключение к серверу PostgreSQL посредством протокола TCP/IP без каких либо дополнительных библиотек.
Добавил на форму компонент данный компонент и написал процедуру подключения к базе.
PgCon.Database := 'minibd'; PgCon.Server := '192.168.65.130'; PgCon.Port := 5432; PgCon.Username := 'pgdac'; PgCon.Password := 'testdac'; PgCon.Options.UseUnicode:=True; try PgCon.Connect; StatusBar1.Panels[0].Text := 'Подключение успешно'; Tovar.Open; Zakaz.Open; Zdetail.Open; Klients.Open; except on e: Exception do StatusBar1.Panels[0].Text := e.Message; end;
Затем добавил на форму несколько компонентов TPgQuery, это так сказать главные рабочие лошадки при работе с базами данных. Они позволяют выполнять произвольные запросы для выборки наборов данных и обрабатывать их.
К примеру если требуется вывести данные из таблицы товаров то это делается так.
Добавляем компоненты TPgQuery, TPgDataSource и любой ddgrid для отображения данных. Затем в коде пишем.
procedure TForm1.ToolButton15Click(Sender: TObject); begin PgQuery1.SQL.Add('select * from tovar'); PgQuery1.Open; end;
У компонента PgDataSource1 устанавливаем свойство DataSet равное PgQuery1, а у грида для отображения данных выставляем свойство DataSource равным PgDataSource1. Запускаем программу и нажимаем на кнопку и данные отображаются.
Если требуется выбрать данные с условием то можно сделать так.
procedure TForm1.ToolButton15Click(Sender: TObject); begin PgQuery1.SQL.Add('select * from tovar'); PgQuery1.AddWhere('gruppa = 1'); PgQuery1.Open; end;
Можно конечно и просто в коде написать.
PgQuery1.SQL.Add('select * from tovar where gruppa = 1');
Тут кому как удобнее.
Допустим у нас имеется в базе несколько таблиц с одинаковой структурой и разными названиями. Иногда к ним требуется обращаться. Тут может выручить такое свойство TPgQuery как Macros.
Пишется такой код.
procedure TForm1.ToolButton15Click(Sender: TObject); begin PgQuery1.SQL.Add('select * from &otg'); PgQuery1.Macros.MacroByName('otg').Value:='otgruzka2015'; // сюда подставляем необходимое нам название таблицы PgQuery1.Open; end;
Если необходимо добавить данные то тут есть несколько вариантов. Либо так.
procedure TForm1.ToolButton15Click(Sender: TObject); begin Zakaz.SQL.Text:='select * from zakaz'; Zakaz.Open; Zakaz.Append; Zakaz.FieldByName('id').AsInteger:=1; Zakaz.FieldByName('name').AsString:='Иванов Иван Иванович'; Zakaz.FieldByName('dataz').AsDateTime:= Now; Zakaz.FieldByName('summa').AsFloat:= 123.345; Zakaz.Post; end;
Либо по другому.
procedure TForm1.ToolButton15Click(Sender: TObject); begin Zakaz.SQL.Text:='insert into zakaz(id,name,dataz,summa) values (:id, :nm, :dt, :sm);'; Zakaz.ParamByName('id').AsInteger:=1; Zakaz.ParamByName('nm').AsString:='Иванов Иван Иванович'; Zakaz.ParamByName('dt').AsDateTime:= Now; Zakaz.ParamByName('sm').AsFloat:= 123.345; Zakaz.Execute; end;
С методами Update и Delete можно работать так же.
В некоторых случаях можно использовать компонент TPgTable, если требуется работать с одной таблицей. В таком случает у него указывается таблица с которой предстоит работать в tablename, так же можно указать по каким полям осуществлять сортировку и и фильтрацию.
procedure TForm1.ToolButton15Click(Sender: TObject); begin PgTable1.TableName:='tovar'; PgTable1.OrderFields:='name, cena';// поля по которым будет проходить сортировка PgTable1.Open; end;
Поскольку в базе используется связь master-detail то у компонента detail TPgQuery отвечающего за отображения данных необходимо выставить соответствующие настройки.
DetailQuery.MasterSource:=MasterSource; DetailQuery.MasterFields:='idd'; DetailQuery.DetailFields:='zid';
Это обеспечивает соответствующее отображение данных из нужной таблицы.
Поскольку PosstgreSQL поддерживает хранимые процедуры то грех ими не воспользоваться. В наборе PgDAC для работы с процедурами используется компонент TPgStoredProc. Я добавил на форму их два штуки. Один из которых будет отвечать за получения уникального номера для заказа с сервера через хранимую процедуру, а второй будет производить подсчет суммы заказа. Вообще компонент TPgQuery позволяет получать номер последней добавленной записи через свойство LastInsertOID . Но для оперативной работы с данными лучше получать уникальный номер отдельно.
В PostgreSQL имена таблиц и процедур пишутся с маленькой буквы, это необходимо для кроссплатформенной совместимости, поскольку в Linux регистрозависимые имена файлов. Файлы с именами файл.txt и Файл.txt это разные файлы в отличие от Windows.
Процедура получения уникального последовательного номера.
CREATE OR REPLACE FUNCTION public.getnewid ( out doc integer) RETURNS integer AS $body$ BEGIN update fff set nomer = nomer +1 where docid =1; select nomer into doc from fff where docid = 1; END; $body$ LANGUAGE 'plpgsql' VOLATILE CALLED ON NULL INPUT SECURITY INVOKER COST 100;
Из кода программы она вызывается так.
GetDocId.StoredProcName := 'getnewid'; GetDocId.ExecProc; Zakaz.FieldByName('idd').AsString := GetDocId.Params.ParamValues['doc'];
Втора процедура для подсчета суммы заказа.
CREATE OR REPLACE FUNCTION public.sumdoc ( iddd varchar ) RETURNS void AS $body$ BEGIN update zakaz set summa = (select sum(cena * kolvo) from zakdetail where zid = iddd) where idd = iddd; END; $body$ LANGUAGE 'plpgsql' VOLATILE CALLED ON NULL INPUT SECURITY INVOKER COST 100;
Из программы она вызывается так.
PGSum.StoredProcName := 'sumdoc'; PGSum.Prepare;// получение параметров процедуры с сервера PGSum.ParamByName('iddd').AsString := Zakaz.fieldbyname('idd').AsString; PGSum.ExecProc;
Можно как вариант использовать компонент TpgQuery для работы с хранимыми процедурами, но когда есть отдельный компонент то зачем городить огород.
Для отладки рабочих запрос я использовал компонент TPgSQLMonitor который работает в связке с программой DBMonitor, данная программа скачивается отсюда. Компонент добавляется на форму или датамодуль работы с данными. У него устанавливается свойство Active:= true, затем запускается программа dbmonitor,и все запросы и так же их параметры будут отображаться в данной программе.
Если в программе требуется изменять какие либо данные без из отображения то можно использовать компонент TPgSQL данный компонент позволяет работать с запросами и хранимыми процедурами без отображения данных. Просто добавляете запрос. Устанавливаете параметры и выполняете его.
procedure TForm1.ToolButton15Click(Sender: TObject); begin PgSQL1.SQL.Add('update tovar set cena=cena * 1.25 where gruppa = 1'); PgSQL1.Execute; end;
Если же требуется работать с портянками кода вроде такого.
INSERT INTO public.tovar ("tid", "name", "cena", "kolvo", "gruppa") VALUES (323, E'ПО ЛК KAV на 1 год 2 ПК BOX', 1200, 9, E'Антивирусы'), (324, E'ПО ЛК KAV на 1 год 2 ПК Renewal Card (Продление)', 990, 10, E'Антивирусы'), (325, E'ПО ЛК KIS на 1 год 2 ПК BOX', 1600, 6, E'Антивирусы'), (326, E'ПО ЛК KIS на 1 год 2 ПК Renewal Card (Продление)', 1320, 10, E'Антивирусы'), (327, E'ПО ЛК KIS на 1 год 3 ПК BOX', 1990, 3, E'Антивирусы'), (328, E'ПО ЛК KIS на 1 год 3 ПК Renewal Card (Продление)', 1490, 1, E'Антивирусы'), .... (10001, E'ПО ЛК KIS на 1 год 5 ПК DVD BOX', 3899, 3, E'Антивирусы'), (10002, E'ПО ЛК KIS на 1 год 5 ПК Renewal Card (продление)', 2899, 3, E'Антивирусы');
То можно использовать TPgScript, этот компонент как раз и предназначен для работы с большими объемами sql кода. Можно загрузить в него sql файл и выполнить его.
procedure TForm1.ToolButton15Click(Sender: TObject); begin PgScript1.SQL.LoadFromFile('c:\test.sql'); PgScript1.Execute; end;
Либо же сразу выполнить файл.
procedure TForm1.ToolButton15Click(Sender: TObject); begin PgScript1.ExecuteFile('c:\test.sql'); end;
Так же в наборе компонентов присутствует TPgDump, он предназначен для сохранения и восстановления базы данных. Я добавил на форму PgDump и ProgressBar, а так же две кнопки, одна для сохранения данных вторая для восстановления.
Код сохранения данных.
procedure TForm1.Button2Click(Sender: TObject); begin PgDump1.TableNames := 'buhotchet,fff, klietns,tovar,zakaz,zakdetail';// указываем таблицы которые требуется сохранить PgDump1.Mode := dmAll; try PgDump1.BackupToFile('o:\test\dump.data'); ShowMessage('Сохранение успешно!'); except ShowMessage('Ошибка сохранения!'); end; end;
Код восстановления данных
procedure TForm1.Button3Click(Sender: TObject); begin ProgressBar1.Position := 0; try PgDump1.RestoreFromFile('o:\test\dump.data'); ShowMessage('Данные успешно восстановлены!'); except ShowMessage('Ошибка восстановления!'); end; end;
А для отображения процесса работы с данными добавил следующий код.
// сохранение данных procedure TForm1.PgDump1BackupProgress(Sender: TObject; ObjectName: string; ObjectNum, ObjectCount, Percent: Integer); begin ProgressBar1.Position := ObjectNum; ProgressBar1.Max := ObjectCount; end; // восстановление данных procedure TForm1.PgDump1RestoreProgress(Sender: TObject; Percent: Integer); begin ProgressBar1.Position := Percent; end;
Вообще компонент TPgDump имеет много опций для работы с данными, но я не буду заострять на нем внимание.
Так же PgDac поддерживает шифрование данных. Для этого используется компонент TPgEncryptor, он поддерживает несколько алгоритмов шифрования. Я добавил компонент на форму и выставил у него настройки.
PgEncryptor1.EncryptionAlgorithm:= eaAES128; PgEncryptor1.HashAlgorithm:= haMD5;
Затем создал таблицу, которая будет шифроваться.
TPgEncryptor поддерживает шифрование для следующих типов данных - ftString, ftWideString, ftBytes, ftVarBytes, ftBlob, ftMemo, ftWideMemo, учитывайте это при необходимости шифровать данные. Это обусловлено тем что зашифрованные данные лежат в ячейках таблиц, они состоят из самих зашифрованных данных, а так же хеша данных для проверки корректности расшифровки. Поэтому числовые столбцы шифровать не получиться.
А затем написал следующий код.
procedure TForm1.Button1Click(Sender: TObject); begin if EditPass.Text <> '' then begin BuhQuery.Encryption.Encryptor := PgEncryptor1; BuhQuery.Encryption.Fields := 'mesyac, summa';// добавляем столбцы для шифровки BuhQuery.Encryption.Encryptor.Password := EditPass.Text; BuhQuery.DataTypeMap.AddFieldNameRule('mesyac', ftString); BuhQuery.DataTypeMap.AddFieldNameRule('summa', ftString); BuhQuery.Open; end else begin BuhQuery.Encryption.Encryptor := nil; BuhQuery.Open; end; end;
Рузультат работы с паролем и без.
Для работы с событиями в PostgreSQL у PgDac имеется компонента TPgAlert. События это механизм обмена сообщениями между процессами в PostgreSQL. Допустим нам необходимо обновлять грид в программе при добавление в таблицу новых данных. Для этого у таблицы создается триггер на добавления данных, который в случае если данные верны посылает сообщение серверу что данные добавлены, а программы потом получают от сервера это сообщение и обновляют данные. Вот для получения этих сообщение от сервера и нужен TPgAlert. Я добавил его на форму и создал обработчик события OnEvent
procedure TForm1.PgAlerterEvent(Sender: TObject; const EventName: String; PID: Integer; const EventMessage: String); begin if EventMessage = 'newzakaz' then Zakaz.Refresh; end;
После этого у компонента PgAlerter настроил свойство Events на канал work. С данного канала компонент будет слушать сообщения. Вообще там длинна сообщения может быть примерно 8000 символов, можно целый внутренний чат организовать при желание (но зачем ?????).
А затем на сервере создал правило для обработки новых данных.
CREATE RULE zakaz_rl AS ON INSERT TO public.zakaz DO (SELECT pg_notify('work'::text, 'newzakaz'::text) AS pg_notify;);
Теперь при добавление данных во всех копиях программы будет обновлен список заказов.
Я не стал описывать остальные компоненты из набора поскольку не было нужды их применять. По мере того как буду их использовать буду описывать работу с ними.
Да тут проблема была с темой оформления для jcomments, сейчас все поправил.
Про данный момент я был не вкурсе,я поверхностно почитал про pg
я не стал останавливаться на этом моменте
В PostgreSQL имена таблиц и процедур пишутся с маленькой буквы, это необходимо для кросплатформенн ой совместимости
Это по умолчанию такое поведение, можно настроить регистронезавис имые таблицы
RSS лента комментариев этой записи