Pull to refresh
0

Некоторые особенности языка программирования среды разработки tmaplatform

Reading time 6 min
Views 2.6K


Данный пост рассказывает о языках программирования и запросах среды разработки учетных приложений tmaplatform.

У всех вкусы разные...

При работе с платформой Вы можете использовать несколько языков программирования: Паскаль (Similar Pascal), Бейсик (Similar Basic). В стадии разработки Java-подобный язык, который появится в ближайшее время. Все примеры поста написаны на Similar Pascal.

В платформу интегрирован язык запросов, названный нами «Внутренний SQL», большая часть поста будет именно о нем.

Независимость от СУБД.

У каждой СУБД есть свои плюсы и минусы, разные СУБД предназначены для разных задач и объемов хранимых данных. И просто разные разработчики привыкли к разным серверам баз данных. Это аксиома.

Среда разработки tmaplatform позволяет легко (легко – значит без изменения кода) переносить программы между различными серверами баз данных: MySQL (работает), PgSQL (тестирование), MSSQL (тестирование), SQLite(тестирование), в дальнейшем данный перечень будет расширяться. И все мы знаем, что при всей универсальности языка SQL просто так перевести программу для работы с БД с одного сервера на другой не получится. Еще сильнее осложнит переход активное использование триггеров, хранимых процедур и функций.

Поэтому мы добавили в платформу внутренний язык запросов, максимально похожий на SQL92 (как базовый стандарт SQL). При выполнении Вашей программы платформа транслирует запросы с внутреннего языка на язык используемой БД.

Этот язык запросов не позволяет использовать 100% возможностей Вашего любимого сервера баз данных. Поэтому мы оставили возможность совместно с внутренним языком запросов использовать язык запросов базы данных (прямые запросы). При использовании прямых запросов переносимость теряется. Но если Вы используете внутренний язык запросов в 98% программы, то это облегчит Вам перенос программы и отладку на несколько порядков.

Непосредственные запросы

Запросы к базе данных можно писать непосредственно в коде программы. Язык запросов является частью языка программирования. Поэтому сразу во время ввода запроса платформа проверяет наличие таблиц, полей и их типы. Еще до запуска программы платформа проверит корректность всех запросов.

ClientId := 3;
D := (Select Sum(Summa) From Documents Where Client = :ClientId);

Например, если тип переменной ClientId отличается от типа поля Client, то платформа оповестит об этом (подчеркнет место ошибки красной линией).

Так же в редакторе платформы доступна подсказка по синтаксису, именам полей, функциям базы данных и автозавершение.

При рефакторинге базы данных платформа автоматически скорректирует все запросы. То есть, когда вы разделяете таблицу на две части, запрос "Select A, B From Table" будет заменен на "Select A, LinkField.B From Table1".
Параметры запроса указываются после символа ":", при этом можно указать не только переменную, но и любое выражение языка программирования.

Технология AutoJoin

Ничего так кратко и лаконично не описывает что-либо в программировании, как пример:

Select Account.Client.City.Country.Name From Document

В этом запросе мы получаем наименование страны, в которой находится город, в котором зарегистрирован клиент, которому принадлежит счет, на который оформлен документ.

В стандартном SQL этот запрос выглядит так:

Select Countries.Name From Document
Left Join Accounts On Accounts.Id = Document.Account
Left Join Clients On Clients.Id = Accounts.Client
Left Join Cities On Cities.Id = Clients.City
Left Join Countries On Countries.Id = Cities.Country

Правда, проще? Объем запроса уменьшился в 5 раз. Эти 4 блока Join приходятся только на одно поле. А если у нас 20 полей? Ну, в общем, Вы поняли.

Single, новая команда SQL

Оператор Select возвращает набор данных (далее датасет), а если Вам требуется получить единственное значение, то приходится добавлять в программу команды извлечения данных.

D := (Select Sum(S) From Documents Where Client=:Client);
If D.Count=0 Then S := 0 Else S := D[0].S;

Оператор Single работает аналогично Select, но возвращает единственное значение (первое поле первой строки).

S := (Single Sum(S) From Documents Where Client=:Client);

Обращение к внешним запросам

При использовании подзапросов к любому внешнему запросу можно обратиться, указав перед именем переменной несколько точек (количество зависит от уровня вложенности подзапроса).

Select Name, (Select Sum(Summa) From Money Where Client=.Id) From Clients
Select Name, (Select (Select Sum(Summa) From Money Where Client=..Id)) From Clients

Оптимизация запросов

Запросы в программе должны быть как можно проще и нагляднее (ведь, возможно, не только Вам, но и кому-то еще придется разбираться в этой программе). Но часто такие запросы не являются самыми быстрыми. И, к тому же, внутренний язык запросов не позволяет использовать все возможности СУБД, чтобы написать оптимальный запрос.

Платформа содержит инструмент «Оптимизатор запросов». С помощью него вы можете задавать пары запросов на внутреннем и внешнем языке. То есть указать самый быстрый запрос:

Заменить с

Select Name, (Select Sum(Summa) From Documents Where Client=.Id) From Clients

Заменить на

Select Client.Name, Sum(Document.Summa) From Clients, Documents Where Clients.Id=Documents.Client

Также этот инструмент позволяет опытным путем проверить идентичность результатов работы запросов и сравнить время выполнения. Оптимизируйте программу, не усложняя её!

Типизированные датасеты.

Любой Select-запрос возвращает датасет (объект типа Dataset). Мы сделали работу с ним такой же, как и с массивом.

Dataset := DirectQuery('Select * From mysql.user');
For y:=0 To Dataset.Count-1 Do
  For x:=0 To Dataset.Columns.Count-1 Do
   Warning(Dataset[x,y]);

Более интересная возможность — типизированные датасеты. В прошлом примере Dataset[x,y] возвращает тип данных Variant и контроль типов на этапе ввода текста программы не осуществлялся. Типизированные датасеты позволяют обращаться к определенным полям определенного типа данных — Dataset[x].ИмяПоля. То есть работает контроль типов во время ввода, автозавершение и т.п.

Procedure OnCreate; 
Var
  D : Dataset Of Record 
        Id : Integer;
        CreateDate : DateTime;
        ClientId : Integer;
        CountryName : String; 
      End;
Begin 
  D := (Select Id, CreateDate, Client, Client.City.Country.Name From Document);
  Warning(D[2].Name); // Обращение к произвольному полю
  Foreach E in D Do // Перебор всех элементов
  Begin
    Warning(E.Id);  // Обращение к полю
    Warning(E.CountryName);  // Обращение к полю
  End;
End;

Следует отметить, что типы полей, возвращаемые Select-запросом, результат которого присваивается датасету, должны быть такого же типа, как и поля самого датасета.

Транзакции

Язык программирования поддерживает блок Transaction. Куда же без них? Все помнят первый пример в любом учебнике с переводом денег со счета на счет? :)

Transaction
Begin
  (Insert Table1 ...)
  (Insert Table2 ...)
End;

Этот блок значит, что другие клиенты не смогут увидеть изменения в базе данных, пока не завершится блок Transaction. И наоборот, Вы не сможете увидеть изменения, вносимые другими клиентами. Если в этом блоке происходит исключение, то все изменения, внесенные в базу данных, откатываются.

Транзакции могут быть вложенными.

Можно указать уровень изоляции транзакции.

Transaction Continous Read
Begin
End;

Автоматическое обновление

При изменении базы данных платформа анализирует запрос и автоматически обновляет интерфейс. Причем, учитываются таблицы, используемые во вьюхах (view) и вычисляемых полях. А обновляются только измененные записи.

Этот же механизм используется в Select-триггерах и материализованных вьюхах (view), которые реализует платформа, если СУБД их не поддерживает. (Например, MySQL).

SQL к локальным данным

SQL-запросы можно писать не только к таблицам базы данных, но и к локальным данным.

Procedure OnCreate;
Var
  Data : Dataset Of Record ... End;
  Data1 : Dataset Of Record ... End;
Begin
  ...      
  Data1 := (Select FirstName+' '+LastName, Age From :Data Where Age>20 Order By -Age, Name);
  ...      
End;

При этом в запросах можно использовать любые функции программы.

Хранимые процедуры.

Хранимые процедуры, функции и триггеры – программы на процедурном языке СУБД (T-SQL, PL/SQL и т.д.), которые хранятся и выполняются на сервере БД. Платформа предоставляет возможность писать эти программы на своем внутреннем языке для независимости от какой-либо конкретной СУБД (текст процедуры/триггера при этом транслируется).

Использование одного и того же языка и API во всей программе — это несомненный плюс. К тому же, Similar Pascal с интегрированным языком запросов более удобен, предсказуем и нагляден, чем языки хранимых процедур (например, у MySQL).

Вы можете просто перенести код из программы в хранимую процедуру и обратно.

А главный плюс — это то, что один раз написанная хранимая процедура будет работать на всех поддерживаемых серверах баз данных.

Пример хранимой процедуры:

Procedure Recalc(Curr, Contragent : Integer);
Var
  S, CurrRate, NewCurrRate : Currency;
begin
  Foreach C In (Select Id, DocDate, Sum, Curr, NewSum, NewCurr From Documents Where Curr=:Curr And Contragent=:Contragent) Do
  Begin
    CurrRate := GetCurrRate(C.DocDate, C.Curr);
    NewCurrRate := GetCurrRate(C.DocDate, C.NewCurr);
    S := RoundDiv(RoundMul(C.Sum, CurrRate, 4), NewCurrRate, 2);
    If C.Summa <> S Then
      (Update Documents Set Summa = :S Where Id = :C.Id);
  End;
End;

Заключение.

Все вышеперечисленное повышает скорость разработки, понимания и отладки программы. Эти факторы, естественно, являются основополагающими при выборе среды разработки. Хотите попробовать tmaplatform – записывайтесь на бета-тестирование (кидайте ваш email мне(smirnovss) в личку)
Полное описание платформы со справкой смотрите тут tmaplatform.ru
Tags:
Hubs:
+17
Comments 38
Comments Comments 38

Articles

Information

Website
www.rtit.ru
Registered
Founded
Employees
11–30 employees
Location
Россия