Приветствую, уважаемые хабражители!

Я занимаюсь переводом кода из MS SQL Server в PostgreSQL с начала 2019 года и сегодня  продолжу сравнение этих СУБД.

В прошлой публикации мы рассматривали отличия в быстродействии MS SQL Server и PostgreSQL для «1C».

В Ozon есть решения и на MS SQL Server, и на PostgreSQL: первая используется в логистике и системах внутренних сервисов, вторая — в mission critical-подсистемах, от которых напрямую зависит бизнес компании (склад, корзина, оплата картами, платежи, информация о товарах на сайте и др.).

Периодически появляются задачи перевода решений из огромных монолитных баз из MS SQL Server в PostgreSQL. Поэтому давайте сравним основные конструкции синтаксиса этих СУБД для правильного чтения кода, а также для того, чтобы быстро изменять код из MS SQL Server для PostgreSQL и наоборот.

Начнём с сопоставления типов.

Сопоставление типов

MS SQL

PostgreSQL

BIGINT

BIGINT, INT8

BINARY(n)

BYTEA

VARBINARY(n)

BYTEA

VARBINARY(max)

BYTEA

ROWVERSION

BYTEA

IMAGE

BYTEA

FIELDHIERARCHYID

BYTEA, LTREE (расширение)

BIT

BOOLEAN, BOOL

CHAR(n), n<=8000

TEXT

NCHAR(n), n<=4000

TEXT

VARCHAR(n), n<=8000

TEXT

NVARCHAR(n), n<=4000

TEXT

VARCHAR(max)

TEXT

NVARCHAR(max)

TEXT

TEXT

TEXT

NTEXT

TEXT

FLOAT(n)

DOUBLE PRECISION, FLOAT8

SMALLMONEY

MONEY

MONEY

MONEY

INT, INTEGER

INT, INTEGER, INT4

SMALLINT

SMALLINT, INT2

NUMERIC(n,m)

NUMERIC(n,m)

DEC(n,m), DECIMAL(n,m)

DEC(n,m), DECIMAL(n,m)

TINYINT

SMALLINT, INT2

REAL

REAL, FLOAT4

UNIQUEIDENTIFIER

CHAR(16), UUID

DATE

DATE

TIME(n)

TIME(n)

DATETIME

TIMESTAMP(3)

DATETIME2(n)

TIMESTAMP(m)

DATETIMEOFFSET(n)

TIMESTAMP(n) WITH TIME ZONE, TIMESTAMPTZ

SMALLDATETIME

TIMESTAMP(0)

XML

XML

Примечание. Типы CHAR и VARCHAR лучше не использовать. Причины подробно описаны здесь.

Более подробно о типах данных:

  1. MS SQL Server

  2. PostgreSQL

Теперь перейдём к сопоставлению синтаксиса MS SQL Server и PostgreSQL.

Сопоставление синтаксиса MS SQL Server и PostgreSQL

I. Регистрозависимое обращение к схемам, таблицам (представлениям) и их полям и другим объектам базы данных

В MS SQL Server при обращениях к объектам можно использовать квадратные скобки (они обязательны, только если в названии объекта или его поля присутствуют недопустимые символы):

[schema]
[table]
[view]
[object]
[table].[field]
[view].[field]
[schema].[table]
[schema].[view]
[schema].[object]
[schema].[table].[field]
[schema].[view].[field]

В PostgreSQL для этого используются двойные кавычки (они обязательны, только если в названии объекта присутствуют заглавные буквы или есть недопустимые символы в названии объекта или его поля):

"schema"
"table"
"view"
"table"."field"
"view"."field"
"schema"."table"
"schema"."view"
"schema"."table"."field"
"schema"."view"."field"

II. Выборка заданных N данных

В MS SQL Server используется TOP:

В PostgreSQL используется LIMIT:

SELECT TOP(N) ...;

SELECT .... LIMIT N;

III. Постраничная загрузка данных (скользящее окно)
Задача: извлечь 100 строк начиная с 202-й строки включительно по возрастанию даты рождения:

в MS SQL Server:

в PostgreSQL:

SELECT *
FROM tbl
ORDER BY BirthDate ASC
OFFSET 201 ROW FETCH
NEXT 100 ROWS ONLY;

select *
from tbl
order by BirthDate asc
[--offset 201 row fetch
next 100 rows only;]
LIMIT 100 OFFSET 200

Примечание. Вместо row можно использовать rows в любом месте запроса, а вместо next можно использовать first в обеих СУБД.

IV. Выборка первого непустого значения

MS SQL Server

PostgreSQL

COALESCE — рекомендуется

ISNULL — не рекомендуется

coalesce

V. Тернарный оператор IIF

MS SQL Server

PostgreSQL

IIF (<условие>,<выражение_если_условие_истинно>,<выражение_если_условие_ложно>)

или

CASE WHEN <условие> THEN <выражение_если_условие_истинно> ELSE <выражение_если_условие_ложно> END

case when <условие> then <выражение_если_условие_истинно> else <выражение_если_условие_ложно> end

VI. Создание псевдонима

MS SQL Server

PostgreSQL

AS — рекомендуется

= — не рекомендуется

as

VII. Выражения CASE

MS SQL Server

PostgreSQL

CASE-WHEN-THEN-END

CASE-WHEN-THEN-ELSE-END

case-when-then-end

case-when-then-else-end

VIII. Работа с переменными

Объявление переменной

MS SQL Server

PostgreSQL

DECLARE @val;

declare val;

Примечание. В MS SQL Server при объявлении переменных используется знак @ перед именем, а в PostgreSQL — нет. Также, помимо PL/pgSQL, в PostgreSQL можно встраивать и другие языки, такие как PL/Python и PL/Perl.

Присвоение переменной значения

MS SQL Server

PostgreSQL

SET @переменная = значение;

переменная := значение

Примечание. В PostgreSQL используется := для PL/pgSQL и просто = для PL/Python и PL/Perl.

Вывод значения на консоль

MS SQL Server

PostgreSQL

print 'строка';

print @переменная;

RAISERROR(@переменная, 1, 1) WITH NOWAIT;

RAISE NOTICE '%', 'строка';

RAISE NOTICE '%', <переменная>;

IX. Управление выполнением кода

Выполнение скрипта

В MS SQL Server:

declare @_query int;
set @_query=777;
set @query=1+8;
RAISERROR(@_query, 1, 1) WITH NOWAIT; --PRINT @_query;

В PostgreSQL:

Шаблон:

do $$
<объявление переменных> 
begin
<код> 
end;
$$;

Пример (вывод информации):

do $$
declare _query int;
begin
_query:=777;
_query:=1+8;
RAISE NOTICE '%', _query;
end;
$$;

Пример (передача значения клиенту):

do $$
declare _query int;
begin
_query:=777;
_query:=1+8;
PERFORM
set_config('my._query',
_query::text, FALSE);
end;
$$;
SELECT current_setting
('my._query');

Для PostgreSQL:

  1. В DBeaver (бобре) нужно нажать CTRL+SHIFT+O при отсутствии окна вывода, а в pgAdmin вывод происходит автоматически.

  2. В psql и так всё работает.

Цикл WHILE

MS SQL Server

PostgreSQL

WHILE <условие_при_котором_цикл_работает>

BEGIN

...

END

while <условие_при_котором_цикл_работает>

loop

...

end loop

Логическое ветвление

MS SQL Server

PostgreSQL

IF-BEGIN-END

IF-BEGIN-END-ELSE-BEGIN-END

if-then-else-end if;

if-then-elseif-then-else-end if;

Более подробно про управление выполнением кода:

  1. Управление выполнением кода в MS SQL Server

  2. Управляющие структуры в PostgreSQL

X. Функции для работы со строками

Определение длины строки (количество символов в строке)

MS SQL Server

PostgreSQL

LEN (<строка>)

length (<строка>)

Примечание. В MS SQL Server исключаются конечные пробелы. Если нужно учитывать и их, то необходимо воспользоваться функцией DATALENGTH (<строка>), которая возвращает суммарное количество байтов в символах строки.

Возвращение символа по его коду:

MS SQL Server

PostgreSQL

char(n)

chr(n)

Конкатенация строк

MS SQL Server

PostgreSQL

+

||

Нахождение позиции вхождения подстроки

В MS SQL Server:

В PostgreSQL:

CHARINDEX(<что_ищем>, <где_ищем>,<с_какой_позиции_ищем_начиная_с_1>)

strpos(<где_ищем>, <что_ищем>)

strpos(substring(<где_ищем>, <с_какой_позиции_ищем_начиная_с_1>, length(<где_ищем>)- <с_какой_позиции_ищем_начиная_с_1>+1), <что_ищем>)

Примечание. Точного соответствия не будет, если производить поиск не с начала строки.

Регистронезависимое сравнение и поиск данных

В MS SQL Server:

В PostgreSQL:

1. LIKE

2. a = b

3. <>

4. a in (b1, ...)

1. ilike

2. lower(a) = lower(b) или upper(a)=upper(b)

3. lower(a) <> lower(b) или upper(a)<>upper(b)

4. lower(a) in (lower(b1), ...) или upper(a) in (upper(b1), ...)

Примечание. В PostgreSQL рекомендуется произвести оптимизацию через создание функционального индекса:

create [concurrently] index idx_lower_<field> 
on <schema>.<table> (lower(<field>));

--После создания concurrently-индекса, 
--его необходимо проверить на наличие битых индексов следующим запросом:
SELECT indexrelid::regclass FROM pg_index where not indisvalid;

--Далее для обновления статистики по нужной таблице 
--необходимо выполнить команду ANALYZE:
ANALYZE <table>;

Более подробно про команду ANALYZE.

Слияние строк по запросу в одну строку по заданному разделителю

В MS SQL Server можно использовать функцию STUFF следующим образом:

STUFF(( SELECT DISTINCT
                 ', ' + CONVERT(varchar, tbl.<поле>)
          FROM
            <схема>.<таблица> tbl
          [WHERE
            <условия>]
          FOR XML PATH(''))
      , 1
      , 1
      , '') AS STUFF_tbl;

Также начиная с версии 2017 доступна функция STRING_AGG.

В PostgreSQL для этого можно использовать функцию string_agg таким образом:

string_agg((SELECT
distinct ', ' ||
cast(tbl.<поле> as VARCHAR) FROM
<схема>.<таблица> tbl,
[WHERE
<условия>]
), 1, 1,
'') AS
string_agg_field;

Более подробно про функции для работы со строками:

  1. MS SQL Server

  2. PostgreSQL

XI. Функции для работы с датой и временем

Получение текущей даты и времени (локальное время)

MS SQL Server

PostgreSQL

GetDate()

SysDateTime()

current_timestamp

clock_timestamp

now()

Получение текущей даты

MS SQL Server

PostgreSQL

CAST(GetDate() as DATE)

current_date

Пример преобразования формата даты и времени из строки public_date:

В MS SQL Server:

FORMAT(public_date, 'dd.MM.yyyy HH:mm:ss', 'ru-RU') — предпочтительный способ

convert(varchar(32),convert(datetime,public_date,104),120)

В PostgreSQL:

to_char(to_timestamp(public_date, 'dd.MM.yyyy hh24.mi'), 'yyyy-mm-dd hh24:mi:ss')

Приращение даты/времени

В MS SQL Server:

В PostgreSQL:

DateAdd(datepart, count, dt);

dt + (count * interval '1 datepart');
или
dt + interval 'count datepart';

Более подробно про функции для работы с датой и временем:

1. MS SQL Server

2. PostgreSQL

XII. Получение количества строк, затронутых при выполнении последней команды

MS SQL Server

PostgreSQL

@@ROWCOUNT

get diagnostics <переменная>:=row_count;

XIII. Выполнение динамического SQL-кода

MS SQL Server

PostgreSQL

execute sp_executesql @sql;

execute _sql;

XIV. Проверка и приведение типов

Проверка строки на то, что она является числом

В MS SQL Server:

встроенная функция isnumeric(val)

В PostgreSQL:

CREATE OR REPLACE
FUNCTION
dbo.isnumeric(_input varchar(255) DEFAULT NULL::varchar(255))
RETURNS bit
LANGUAGE plpgsql
AS $function$
/*
Проверяет,
является ли входная строка
числом
*/
declare _result bit;
begin
begin
perform
_input::numeric;
_result:=1::bit;
exception
when others THEN
_result:=0::bit;
end;
return
_result;
end;
$function$
;

Безопасное приведение типа

В MS SQL Server:

try_cast(val as <type>)

Примечание. try_cast в MS SQL Server возвращает NULL, если значение невозможно привести к заданному типу, в других случаях — работает как оператор CAST.

В PostgreSQL есть два способа:

1) через обработку ошибок:

declare _result оператор CAST <type>;
... 
BEGIN
_result :=
cast(val as <type>);

exception

when others then

_result :=null;
end;

2) через реализацию функции:

CREATE OR REPLACE
FUNCTION
dbo.try_cast(value character varying, typename CHARACTER varying)
returns text
LANGUAGE plpgsql
AS $function$
declare _sql_command text;
DECLARE _result text;
begin
_result=value;
_sql_command :=
'select
cast('||''''||
value||''''||' as '||
typename||');';
BEGIN
execute _sql_command;
exception
when others then
_result :=null;
end;
return
_result;
end;
$function$
;

Функция в итоге не возвращает преобразованное в заданный тип значение.
Функция на вход принимает текст и возвращает текст.
Если значение невозможно привести к заданному типу, то возвращается NULL.

Пример использования (чтобы было как в MS SQL Server):

cast(dbo.try_cast(val::text, '<type>') as <type>)

XV. DML-команды

Обновление данных

Пример в MS SQL Server:

Обновление поля Name в таблице Production.ScrapReason для тех строк, для которых есть соответствующие записи в таблице Production.WorkOrder по равенству ScrapReasonID и у которых значение ScrappedQty больше 300:

UPDATE
  sr
SET
  sr.Name = 'Name'
OUTPUT
  deleted.*
, inserted.*
FROM Production.ScrapReas sr
  JOIN Production.WorkOrder wo ON (sr.ScrapReasonID = wo.ScrapReasonID)
                                  AND (wo.ScrappedQty > 300);

Ключевое слово OUTPUT позволяет получить данные об обновлении.

Пример в PostgreSQL:

Обновление поля Name в таблице production.scrapreason для тех строк, для которых есть соответствующие записи в таблице production.workorder по равенству scrapreasonid и у которых значение scrappedqty больше 300:

update
production.scrapreason as sr
set sr.Name = 'Name'
from production.workorder as wo
where (sr.scrapreasoid = wo.scrapreasonoid)
and (wo.scrappedqty > 300)
returning *;

Ключевое слово returning позволяет получить данные об обновлении.

Более подробно о команде UPDATE:

  1. MS SQL Server

  2. PostgreSQL

Удаление данных

Пример в MS SQL Server:

Удаление из таблицы Sales.SalesPersonQuotaHistory тех записей, для которых есть соответствующие записи в таблице Sales.SalesPerson по равенству BusinessEntityID и у которых значение SalesYTD больше 2500000.00:

DELETE FROM
spqh
OUTPUT deleted.*
FROM Sales.SalesPersonQuotaHistory spqh
  INNER JOIN Sales.SalesPerson sp ON (spqh.BusinessEntityID = sp.BusinessEntityID)
WHERE
  (sp.SalesYTD > 2500000.00);

Ключевое слово OUTPUT позволяет получить данные об удалении.

Пример в PostgreSQL:

Удаление из таблицы sales.salespersonquotahistory тех записей, для которых есть соответствующие записи в таблице sales.salesperson по равенству businessentitid и у которых значение salesytd больше 2500000.00:

delete from
sales.salespersonquotahistory AS spqh
using
sales.salesperson AS sp
where
(spqh.businessentityid = sp.businessentitid)
and (sp.salesytd > 2500000.00)
returning *;

Ключевое слово returning позволяет получить данные об удалении.

Более подробно о команде DELETE:

  1. MS SQL Server

  2. PostgreSQL

Получение изменённых записей

В MS SQL Server:

В PostgreSQL:

insert/update/delete таблица
Output deleted/inserted.<столбец>
into [@/#] <таблица>
Values|From <запрос>

insert/update/delete таблица
values()|from <запрос>|using <запрос>
returning *, столбец/столбцы

В update есть доступ только к inserted.

Примечание. В PostgreSQL не нужна промежуточная таблица для получения изменённых записей.

Более подробно:

1. OUTPUT в MS SQL Server

2. returning в PostgreSQL

Удаление дубликатов (дублирующих строк):

В MS SQL Server:

with dbl_in_stage as (
select
row_number() over
(partition by
<field_1>, ...,
<field_N> order by
1) as rn
from
<схема>.<таблица>
as stg
)
delete from
dbl_in_stage where
rn > 1;

В PostgreSQL:

with x as (
select a, ctid, row_number() over(partition by a order by ctid) rn
from t
)
delete from t
using x
where t.a = x.a
and t.ctid = x.ctid
and x.rn > 1;

или более сложный вариант:

delete from <схема>.<таблица>
where ctid=any(
  array(select unnest(ctids[2:]) 
        from (
          select array_agg(
            ctid order by string_to_array(
              regexp_replace(ctid::text, E'\\(|\\)','','g'),',')::bigint[]) ctids 
          FROM <схема>.<таблица> as T group by T::text) as T)::tid[]);

Примечание. Оптимальный вариант — внести в таблицу уникальный ключ, так как работа с метаданными увеличивает нагрузку на систему.

При наличии уникального ключа удалять дубликаты в PostgreSQL можно следующим образом:

delete from <схема>.<таблица> where <уникальный ключ> in
(select <уникальный ключ>
from (
select *, row_number() over (partition by <field_1>, ..., <field_N> order by 1) as rn
from <схема>.<таблица>
) as tbl where rn > 1);

XVI. DDL-команды для работы с таблицами

Удаление таблицы с предварительной проверкой

В MS SQL Server:

Для основной таблицы:

DROP TABLE IF EXISTS <schema>.<table>;

Для локальной временной таблицы:

IF EXISTS(SELECT [name] FROM tempdb.sys.tables WHERE [name] like '#<table>%')
BEGIN
DROP TABLE #<table>;
END;

Для глобальной временной таблицы:

IF EXISTS(SELECT [name] FROM tempdb.sys.tables WHERE [name] like '##<table>%')
BEGIN
DROP TABLE ##<table>;
END;

Здесь:

  1. #<table> — локальная временная таблица, которая видна только в текущей сессии

  2. ##<table> — глобальная временная таблица, которая видна всем пока она существует

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

В PostgreSQL:

Для основной таблицы:

drop table if exists
<schema>.<table>;

Для временной таблицы:

drop table if exists <table>;

Более детально про удаление таблиц:

  1. MS SQL Server

  2. PostgreSQL

Создание таблицы через выборку

В MS SQL Server:

Для основной таблицы:

select ...
into <table>
from …

Для временной таблицы:

select ...
into #<table>
from …

В PostgreSQL:

Для основной таблицы:

create table <table> as
select ...

Для временной таблицы:

create temp table <table> as
select …

Более детально про создание таблиц через выборку:

  1. MS SQL Server

  2. PostgreSQL

Создание/изменение и удаление значения по умолчанию для колонки таблицы

В MS SQL Server:

Добавление:

ALTER TABLE
<схема>.<таблица>
ADD CONSTRAINT
<название_правила>
DEFAULT
<значение_по_умолчанию> FOR <поле>;

Выборка всех значений по умолчанию:

SELECT SCHEMA_NAME(t.[schema_id]) AS sch
, t.name AS tbl
, col.name AS colname
, dc.definition AS def
FROM sys.default_constraints dc
  INNER JOIN sys.columns col ON dc.parent_object_id = col.[object_id]
  INNER JOIN sys.tables t ON t.[object_id] = col.[object_id];

Удаление:

DROP DEFAULT IF EXISTS <название_правила>;

Изменение происходит через удаление и добавление.

В PostgreSQL:

Создание и изменение:

alter table
<схема>.<таблица>
alter column <поле>
set default
<значение_по_умолчанию>;

Выборка всех значений по умолчанию:

select
col.table_schema,
col.table_name,
col.column_name,
col.column_default
from
information_schema.columns as col;

Удаление:

alter table <схема>.<таблица>
alter column <поле> drop default;

Изменение типа колонки таблицы

В MS SQL Server:

В PostgreSQL:

ALTER TABLE
<схема>.<таблица>
ALTER COLUMN <поле>
<новый_тип> [NULL|NOT NULL];

alter table
<схема>.<таблица>
alter column <поле>
type <новый_тип>;

Примечание. Если у изменяемого столбца есть какие-либо констрейнты или значения по умолчанию, то сначала нужно их сохранить, а затем удалить. Вернуть их можно будет после изменения типа.

Перенос автоинкрементных полей

В MS SQL Server делаем запрос вида:

SELECT
  'do $$
declare
start_with_val
bigint;
declare
sql_statement
varchar;
begin
start_with_val :=
coalesce((select
max(' + c.[name] + ') from
' + s.[name] + '.' + o.[name] + '),0)+1;
sql_statement :=
''alter table
' + s.[name] + '.' + o.[name] + ' alter ' + c.[name] + '
add generated by
default as identity
(start with ''
||
cast(start_with_val
as varchar)||'');'';
execute
sql_statement;
end;
$$;' AS plsql_statement
--select distinct s.name
FROM
  sys.all_columns c
  INNER JOIN sys.all_objects o ON o.[object_id] = c.[object_id]
  INNER JOIN sys.schemas s ON s.[schema_id] = o.[schema_id]
WHERE
  is_identity <> 0
  AND SCHEMA_NAME(o.[schema_id]) <> 'sys'
  AND o.[type] = 'U';

Пример:

do $$ 
declare  start_with_val bigint; 
declare  sql_statement varchar; 
begin
start_with_val := coalesce((select  max(ID) from  dbo.ExchangeQueue),0)+1;  
sql_statement := 'alter table  dbo.ExchangeQueue alter ID  add generated by  default as identity  (start with '  ||  cast(start_with_val  as varchar)||');';  
EXECUTE sql_statement;  
end; 
$$;

Полученные скрипты применяем на стороне PostgreSQL.

Создание автоинкрементных полей

В MS SQL Server:

ALTER TABLE [схема].[таблица]
ADD
  <IDENTITY-поле> bigint IDENTITY(1, 1) NOT NULL;

В PostgreSQL:

do $$  
DECLARE start_with_val bigint;  
DECLARE sql_statement varchar;  
BEGIN  start_with_val := coalesce((select  max(<IDENTITY-поле>) from  <схема>.<таблица>),0)+1;  
sql_statement := 'alter table <схема>.<таблица> alter <IDENTITY-поле> add generated by  default as identity (start with ' || cast(start_with_val  as varchar)||');';  
EXECUTE sql_statement; 
END; 
$$;

Более детально про создание таблиц:

MS SQL Server

PostgreSQL

Более детально про изменение таблиц:

MS SQL Server

PostgreSQL

XVII. Создание и изменение представления

В MS SQL Server:

В PostgreSQL:

CREATE OR ALTER VIEW

[схема].[название_представления]

AS

create or replace view

<схема>.<название_представления>

as

Примечание. В PostgreSQL лучше сначала удалять представление, а потом заново его создавать, если набор полей меняется или меняются названия выходных полей, иначе можно получить ошибку при обращении к изменённому представлению.

Более подробно про создание и изменение представлений:

MS SQL Server

PostgreSQL

XVIII. Построчная обработка строк в наборе

В MS SQL Server:

--объявление переменных @field_1, ...@field_N

DECLARE <курсор>
CURSOR LOCAL FOR
<SELECT>;

OPEN <курсор>;

FETCH NEXT FROM
<курсор> INTO
@field_1, ...@field_N;

WHILE
(@@FETCH_STATUS = 0)
BEGIN
--оперируем значениями переменных @field_1, ...@field_N
...
FETCH NEXT
FROM <курсор> INTO @field_1, ...@field_N;
END

CLOSE <курсор>;
DEALLOCATE <курсор>;

В PostgreSQL:

do $$
declare _val
record;
begin

drop table if
exists _tmp_tbl;

create temp
table _tmp_tbl as
<select>
for _val in
(select field_1, ..., field_n from_tmp_tbl)
loop
--можно обратиться к любому выбранному ранее полю через _val.<поле>. Например, _val.<field_1>
end loop;
end
$$

XIX. Системные информационные функции безопасности

Текущий пользователь

В MS SQL Server используется функция CURRENT_USER().

В PostgreSQL:

  1. session_user — под каким пользователем открыта сессия

  2. current_user (или просто user) — под каким контекстом (ролью) идёт выполнение (session_user переключается для выполнения — здесь важно, под каким правом делается переключение)

Получение имени экземпляра и IP-адреса сервера СУБД

В MS SQL Server:

Получить информацию об IP-адресе сервера СУБД:

SELECT
  CONNECTIONPROPERTY('
net_transport') AS net_transport
, CONNECTIONPROPERTY('
protocol_type') AS protocol_type
, CONNECTIONPROPERTY('
auth_scheme') AS auth_scheme
, CONNECTIONPROPERTY('
local_net_address') AS local_net_address
, CONNECTIONPROPERTY('
local_tcp_port') AS local_tcp_port
, CONNECTIONPROPERTY('
client_net_address') AS client_net_address;

Получить название экземпляра СУБД:

SELECT @@SERVERNAME;

В PostgreSQL:

Получить IP-адрес сервера СУБД:

do $$
declare
title varchar(100) :=host(inet_server_addr());
begin
raise notice '%',
title;
end; $$;

Получение названия экземпляра СУБД пока не реализовано.

Более подробно про системные информационные функции безопасности:

  1. MS SQL Server

  2. PostgreSQL

XX. Определение и вызов хранимой процедуры

Определение хранимой процедуры

В MS SQL Server:

CREATE OR ALTER PROCEDURE
[схема].[назание_процедуры]
<переменная_1> <тип_1>[=<значение_по_умолчанию_1>],
...
AS
BEGIN
...
END

В PostgreSQL:

CREATE OR REPLACE PROCEDURE
<схема>.<название_процедуры>
(
[INOUT] <переменная_1> <тип_1>[=<значение_по_умолчанию1>],
...
)
LANGUAGE plpgsql
AS $body$
[<Объявление переменных>]
BEGIN
...
END;
$body$
;

Вызов хранимой процедуры

В MS SQL Server:

EXEC <схема>.<процедура> 
<переменная_1>=<значение_1>, ..., <переменная_OUTPUT> OUT[PUT];

В PostgreSQL:

call <схема>.<процедура> (
  <переменная_1>=<значение_1>, ..., <переменная_OUTPUT>);

XXI. Создание скалярной функции

В MS SQL Server:

CREATE OR ALTER
FUNCTION [схема].[название_функции]
(<параметр_1> <тип_1>[=<значение_по_умолчанию_1>], ...)
RETURNS <возвращаемый_тип> AS
BEGIN
... RETURN ...
END

В PostgreSQL:

CREATE OR REPLACE FUNCTION
<схема>.<название_функции>
(<параметр_1> <тип_1>[=<значение_по_умолчанию_1>], ...)
RETURNS <возвращаемый_тип>
LANGUAGE plpgsql
AS $body$
[<Объявление переменных>]
begin
... return (
select ...
);
end;
$body$
;

XXII. Передача табличного значения (вывод таблицы)

В MS SQL Server:

CREATE OR ALTER
PROCEDURE
[схема].[название_хранимой_процедуры]
<параметр_1> <тип_1>,
...,
<параметр_N> <тип_N>
AS
BEGIN
...
SELECT ...
END

В PostgreSQL:

create or replace function
<схема>.<название_функции>
(<параметр_1> <тип_1>, ..., <параметр_N> <тип_N>)
return table (<поле_1> <тип_1>, ..., <поле_N> <тип_N>)
language 'plpgsql'
as $body$
[<Объявление переменных>]
begin
return query (select ....);
end;
$body$;

XXIII. DML-триггеры

Пример в MS SQL Server:

CREATE TRIGGER [info].[tr_isupoll_question_text_last_update_trigger]
ON [info].[isupoll_question_text]
FOR UPDATE
AS
UPDATE
  info.isupoll_question_text
SET
  last_update_date = GETDATE()
, last_update_user = SUSER_NAME()
FROM
  info.isupoll_question_text ds
  INNER JOIN INSERTED i ON 
  ds.isupoll_question_text_id = i.isupoll_question_text_id;

Здесь создаётся триггер tr_isupoll_question_text_last_update_trigger для таблицы  info.isupoll_question_text после обновления данных, который для обновляемых строк проставляет текущие дату, время и пользователя соответственно.

DROP TRIGGER IF EXISTS [tr_isupoll_question_text_last_update_trigger] 
on [info].[isupoll_question_text];

Здесь удаляется триггер tr_isupoll_question_text_last_update_trigger для таблицы info.isupoll_question_text

Пример в PostgreSQL:

CREATE OR REPLACE
FUNCTION dbo.update_mod()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
begin
new.last_update_date=now();

new.last_update_user=session_user;

return new;
end;
$function$
;

Здесь создаётся функция dbo.update_mod(), которая заполняет два поля текущими датой, временем и пользователем соответственно.

create trigger tr_isupoll_question_text_last_update_trigger before update 
on info.isupoll_question_text for each row execute function dbo.update_mod();

Здесь создаётся триггер tr_isupoll_question_text_last_update_trigger для таблицы  info.isupoll_question_text до обновления данных, который для каждой строки вызывает выполнение функции dbo.update_mod().

drop trigger if exists tr_isupoll_question_text_last_update_trigger 
on info.isupoll_question_text;

Здесь удаляется триггер tr_isupoll_question_text_last_update_trigger для таблицы info.isupoll_question_text.

Важно! В триггере используйте ключевое слово before, когда хотите нашкодничать в той же таблице, для которой создаётся триггер, и after — для логирования в другую таблицу.

Более подробно про DML-триггеры:

  1. MS SQL Server

  2. PostgreSQL

И в качестве бонуса кратко рассмотрим сопоставление основных системных представлений и приведём ссылки для мониторинга.

Немного о сопоставлении системных представлений и мониторинге

Сопоставление системных представлений

MS SQL Server

PostgreSQL

Описание

Представления схемы системных сведений

System Views

sys.dm_exec_query_stats

pg_stat_statements

Предоставляет статистику по выполненным запросам.

В MS SQL Server содержит только то, что в кеше, а в PostgreSQL — всю статистику.

Для PostgreSQL:

CREATE EXTENSION pg_stat_statements;

на каждую БД.

sys.dm_exec_function_stats

pg_stat_user_functions

Предоставляет статистику по вызовам пользовательских функций.

sys.dm_db_index_usage_stats

pg_stat_all_indexes

pg_stat_user_indexes

Предоставляет статистику по использованию всех пользовательских индексов.

sys.master_files

sys.fn_virtualfilestats (NULL, NULL)

pg_stat_database

Предоставляет статистические данные по каждой БД.

Системные представления PostgreSQL:
Сборщик статистики

Изображение взято с Postgres 13 Observability Updates.

Системные представления MS SQL Server:

  1. SQL Server 2012 System Views Map

  2. Системные динамические административные представления

Мониторинг работы СУБД

Заключение

Мы рассмотрели сопоставление типов и основные конструкции синтаксиса MS SQL Server и PostgreSQL, что позволит быстрее адаптировать решения из одной СУБД под другую.

Также мы пробежались по сопоставлению системных представлений и ссылкам на документацию, в том числе о мониторинге, что позволит анализировать производительность реализованного или перенесённого кода.

Благодарность

Спасибо коллегам за ценные комментарии:

Источники

Комментарии (53)


  1. BigD
    21.01.2022 10:14
    +3

    MS SQL Server недостаточно хорош для business critical? Или другая причина движения в сторону постгре?


    1. jobgemws Автор
      21.01.2022 10:27
      +2

      MS SQL, конечно, достаточно хорош для бизнес-критикал систем, и многие компании его так и юзают.

      Просто у нас так сложилось, что где-то MS SQL, а где-то — PostgreSQL.


    1. Tzimie
      21.01.2022 12:33
      +2

      У многих просто жаба


      1. jobgemws Автор
        21.01.2022 19:05

        Прямолинейно, но в этом есть доля правды скорее всего)


      1. vervolk
        22.01.2022 08:07
        +1

        У когото сейчас санкции и легально купить ни Windows server ни ms sql невозможно.


        1. jobgemws Автор
          22.01.2022 08:19

          Суровая правда текущей реальности


        1. jobgemws Автор
          22.01.2022 11:59

          Но стоит отметить, что есть вполне законные способы обойти эти ограничения. Причём не только в аутсорс отдать создание и эксплуатацию сред.


    1. speshuric
      22.01.2022 13:43
      +1

      В целом замечателен. Только Enterprise лицензия больше $5к за ядро. А сейчас ядер в пухлом (и относительно недорогом!) сервере под сотню. А Enterprise нужен, если памяти >128ГБ (ёлы-палы, 128 ГБ это сейчас комфортный комп разработчка, а не сервер предприятия). И если двигаться в сторону микро- или хотя бы мини- сервисов, то лицензиями придётся покрывать несколько больших серверов.


      1. jobgemws Автор
        22.01.2022 14:02

        В том числе и это.

        Хотя часто Standard хватает+можно получить существенную скидку у партнёров при большой закупке.

        И ещё вопрос сколько по деньгам обойдётся кастомизация открытого (не бесплатного!) решения и его сопровождение. На сколько стабильно и безопасно работают те или иные возможности. И в случае чего кому предъявлять притензию.

        А если ещё использовать ORM, то при росте данных можно получить реальные проблемы в производительности.

        Здесь очень хорошо эти и другие моменты описаны особенно в конце.


  1. n0isy
    21.01.2022 10:40
    +2

    IIF в MS SQL это новодел (с SQL 2016), а так всегда был CASE. Ещё не описаны оконные функции, хотя они указаны для удаления дубликатов.


    1. jobgemws Автор
      21.01.2022 10:49

      Все конструкции описать-цели такой не стояло.
      Только самое часто встречаемое.
      Много чего нет в материале, это так.
      Однако, есть то, что встречалось часто при переводе нескольких проектов.
      Т е материал создавался из опыта (не теории).
      IIF тоже нужно было перенести-потому он и описан в материале.
      CASE-выражение есть и в MS SQL Server, и в PostgreSQL.


    1. jobgemws Автор
      21.01.2022 10:54

      Добавил CASE, чтобы не вводить путаницу


  1. mfreal
    21.01.2022 10:45
    +5

    Годно, давно искал подобную статью, этот наверное самый подробный)


    1. jobgemws Автор
      21.01.2022 10:46

      Здесь описаны основные конструкции, которые встречались из жизни.

      Но далеко не все-чтобы не перегружать материал.


  1. Akina
    21.01.2022 10:46
    +2

    В "I. Регистрозависимое обращение к схемам, таблицам (представлениям) и их полям и другим объектам базы данных" - о квотировании сказано, а вот именно о регистрозависимости - ни полслова.

    Не понял "V. Тернарный оператор IIF" - написано так, словно MS SQL не поддерживает CASE...


    1. jobgemws Автор
      21.01.2022 10:55
      +1

      Благодарю за замечание. В п.5 добавил CASE, чтобы не вводить путаницу


  1. Vicking
    21.01.2022 12:30
    +2

    Также, помимо PL/pgSQL, в PostgreSQL можно встраивать и другие языки, такие как PL/Python и PL/Perl

    А в MS SQL Server можно встраивать Python и R. Вроде R c 2016, а Python с 2017, если ничего не путаю.


    1. jobgemws Автор
      21.01.2022 13:01
      +1

      Да, Вы правы:

      1. R

      2. Python

      3. PHP

      4. Ruby


  1. belch84
    21.01.2022 18:26
    +1

    Я занимаюсь переводом кода из MS SQL Server в PostgreSQL с начала 2019
    Интересно, возможно ли в этом случае программное конвертирование исходного кода? На самой первой своей работе я когда-то заглядывал в чужой исходный текст конвертера для программ на FORTRAN из операционной системы DOS/360 в OS/360. Конвертер был написан на ассемблере DOS/360. Различия FORTRANов были незначительными, но изменение большого количества текстов вручную было бы сложной задачей (нужно помнить, что это была еще эпоха префокарт). Кстати, сама система, для которой создавался конвертер, была довольно дурацкой и состояла чуть ли не из тысяч фортрановских подпрограмм. Для описанных различий диалектов SQL программное конвертирование кажется вполне реальным


    1. jobgemws Автор
      21.01.2022 18:29

      Думаю вполне возможно. По крайней мере еще в ВУЗе была лаба по переносу (не всего конечно, но опять же основных конструкций) из VB.NET в C#.NET и наоборот с учетом вложенности типов. Потому здесь тоже можно причем со временем даже почти все переносить в обе стороны (ну что поддается переносу, т е если есть точное соответствие). Однако, на это уйдет очень много времени+постоянно что-то новое выходит.


    1. edo1h
      21.01.2022 18:35
      +1

      1. jobgemws Автор
        21.01.2022 19:04

        Но судя по версии 1С очень старое решение, а новые фичи появляются чаще, чем раз в год. Так что в любом решении важно зафиксировать версии откуда и куда и чтобы они соовтетствовали тому, на чем будет решение применено.


        1. edo1h
          21.01.2022 19:28
          +1

          Но судя по версии 1С очень старое решение

          а какие версии 1с им указывать? следующая мажорная нативно умеет постгре


          1. jobgemws Автор
            21.01.2022 19:43

            Про версию 7.7 имел в виду


            1. edo1h
              21.01.2022 23:45
              +1

              и я про неё. 8.x работает с postgre из коробки, транслятор запросов из ms sql в postgresql для 1c 8.x не продашь.


              1. jobgemws Автор
                22.01.2022 01:21

                Вы её пробовали в работе?

                В целом какие недостатки или особенности отметили?

                Просто не особо верится, что раз и прямо из коробки всё само встало.


  1. lisper
    21.01.2022 19:52
    +1

    В пункте "Регистронезависимое сравнение и поиск данных" наверное стоит упомянуть, что регистронезависимость сравнения будет зависеть от выбранного значения параметра Collation конкретной БД. Т.е. при значении Collation, например, SQL_Latin1_General_CP1_CS_AS, сравнение строк будет учитывать регистр (_CS - Case Sensitive), а также аксоны и диакритические знаки ('e' <> 'è', _AS - Accent Sensitive), тогда как при SQL_Latin1_General_CP1_CI_AI - нет.

    Кстати, есть ли нечто подобное в PostgreSQL?


    1. jobgemws Автор
      21.01.2022 19:53

      Благодарю за дополнение, но не будем перегружать материал.

      По последнему вопросу не могу ответить, т к так глубоко не копал.


  1. K_Chicago
    21.01.2022 20:32
    +1

    Как я понимаю, PG равняется на Oracle в первую очередь.

    Поэтому настоящее сравнение во многом сходно со сравнением синтаксиса Oracle с MS SQL.

    Хотелось бы увидеть именно сравнение производительности и синтаксиса PG и Oracle.

    Я сейчас занимаюсь как раз бенчмаркингом PG по сравнению с Oracle на примере API большого веб приложения. Пока получается что в большинстве случаев PG сравним или в 2-3 раза медленнее Oracle, хотя в иерархических запросах и курсорах с функцией random PG отстает в 10-20 раз.


    1. jobgemws Автор
      21.01.2022 20:46
      +1

      Сравнение производительности для 1С для конкретных задач проводилась ранее между MS SQL Server и PostgreSQL и ссылка дана на результат и о том как проводились тесты вначале публикации.

      С Oracle сам не сталкивался, потому не могу ничего сказать по этому поводу.

      Но Вы можете описать как тестировали и что получили в своей публикации.

      Думаю это будет очень интересно и полезно.


      1. K_Chicago
        22.01.2022 02:56
        +1

        я уже описал что мог, а тестирую простейшим образом - у нас есть набор хранимых процедур и идентичные таблицы с данными, запускаю в цикле 1000 раз в Oracle SQLPlus, с SET TIMING ON/OFF, и в PG с вычислением разницы timestampt в конце и в начале цикла.

        Результаты вполне устойчивые. Сами процедуры/функции в основном открытие рефкурсоров на запросы от простеньких до довольно сложных. Пока главные отличия - в иерархии и в random, основное в общем сравнимо, хотя PG стабильно в 2-4 раза медленнее.


        1. jobgemws Автор
          22.01.2022 08:14

          А где можно почитать подробнее как настраивали обе СУБД, характеристики железа каждого сервера под СУБД (Вы же понимаете, что если обе СУБД на одном сервере, то нужно будет выключать ту СУБД, которую в данный момент не тестируете), настройки ОС (тут опять же нужно смотреть какая ОС лучше подходит под конкретную СУБД), настройки файловой системы и настройки системы ввода-вывода?

          И полностью код теста для каждой СУБД тоже важно видеть.

          Потому что в интернете много чего пишут, но по факту забыли что-то настроить должным образом и результаты получились ошибочными. В частности, часто забывают в Windows выключить индексирование диска, в следствие чего делают вывод, что в линуксе ФС быстрее на 40%. Если индексирование выключить, то получится примерно одинаково.

          Потому важно полное и детально описание настроек и характеристик, и сам код для тестирования под каждую СУБД.


    1. rrrad
      22.01.2022 00:45
      +1

      В первую очередь, PostgreSQL равняется на стандарт SQL, хотя не отказывается от своих расширений. То, что oracle гораздо ближе по синтаксису к стандарту, чем mssql (взять хотя бы конкатенацию строк - зачем выдумывать свой оператор), это, наверное, исторически сложилась из-за типичной для microsoft тех лет стратегии захвата рынка, а oracle развивался вместе со стандартами (или даже до них: взять хотя бы особый синтаксис сравнений, заменяющих left join-ы, он появился до появления left join-а в стандарте).

      Единственный компонент oracle, на который postgresql ориентирован - язык pl/psql, но всё-таки, синтаксис у них отличается в деталях, подозреваю, что pl/sql был всего лишь хорошей идеей для повторения чего-то похожего, а задача обеспечения совместимости не ставилась.


      1. K_Chicago
        22.01.2022 02:51
        +1

        PG во всю себя позиционирует именно как замена Oracle во всех отношениях. И новые релизы PG постоянно указывают на все большую совместимость с Oracle в смысле облегчения миграции оттуда сюда. Я пока с програмной точки зрения вижу один большой недостаток PG по сравнению с Oracle - отсутствие простого синтаксиса для иерархических запросов, а мои измерения показывают что PG-решение примерно в 100 раз медленнее оракловского:(


        1. rrrad
          22.01.2022 21:20
          +1

          Рекурсивные CTE - гораздо более понятный синтаксис, чем start with/connect by. Причём всё, что можно сделать через start with/connect by, можно выразить и через рекунсивный cte, а вот в обратную сторону это не выполняется, так что этот синтаксис еще и более расширенный.

          Что касается скорости - про 100 раз хотелось бы пруфов с корректным измерением, но боюсь, что лицензия Oracle вам не позволит без согласования с ними опубликовать тест сравнения с другой СУБД.


          1. jobgemws Автор
            22.01.2022 21:23

            А как лицензия может запретить публиковать тест сравнения с другой СУБД?


            1. rrrad
              22.01.2022 23:30

              1. jobgemws Автор
                23.01.2022 08:21

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

                Вы же тоже по договору понимаете какие пункты ничтожны относительно местного законодательства (или проверяете) или верите тому, что написала одна сторона даже пусть большая компания?

                Также в лицензиях часто пишут, что Вы приобретаете не право на продукт, а право использовать продукт, что отсюда вытекает, что не можете в случае потерь для бизнеса или личных целей требовать возместить потери. Однако, в российском законодательстве есть две статьи: про качество услуги/товаров и ещё что-то, благодаря чему можно и нужно в том числе через суд взыскать потери с компании, которая произвела некачественное решение в виде ПО или сервиса и для которой Вы приобрели платную лицензию, т е заплатили.

                Примеры судебных решений много в интернете.

                Также можно через суд поставить гиганта на место в случае аннулирования лицензии по основаниям, противоречащим местному законодательству. И такие примеры есть в интернете.


              1. jobgemws Автор
                23.01.2022 08:22

                Важно иметь штат юристов, хорошо понимающих в этой сфере.


              1. jobgemws Автор
                23.01.2022 08:26

                Или если не хотите возможных судебных тяжб, то обычно создают временно компанию-дочку и от её имени публикуют или выступают (причём можно даже бренд головной компании оставить) , а потом в случае чего, это не Ваша компания была, а вообще сторонне юрлицо и что право выступать с брендом Вашей компании не было дано.

                Т е я не я, лошадь не моя.

                Всё, прицепиться не к чему.


                1. rrrad
                  23.01.2022 12:49
                  +1

                  Вам не кажется, что публикация сравнения производительности двух субд - слишком малозначительная причина для того, чтобы создавать дочернее юр.лицо или нанимать/нагружать юристов дополнительной работой (если, конечно, они не наняты для другого и временно простаивают)?

                  Тем более что по хорошему, тестировать надо под конкретный профиль использования СУБД, а он у каждого будет свой. Плюс, нужно учитывать способности имеющихся в наличии разработчиков (если не нанимаем новых под конкретный проект). А значит, любое тестирование (на сколько оно правильным бы не было), для другого проекта может оказаться, как минимум, малополезным, как максимум - неприменимым. При этом, в худшем случае, человек, почитая подобное сравнение, примет неправильное решение (и даже не узнает об этом).

                  Приведу пример: есть в одной компании биллинг, написанный на Oracle. Админов попросили оценить требуемый сайзинг для случая перевода биллинга на PostgreSQL. Они смигрировали таблицы и запустили несколько отчётов (каждый из них - SQL-запросы на несколько листов исходного кода). На выходе получили просто невозможный сайзинг (увеличение мощности RAC-а из трёх серверов в десятки раз). При этом, зная, как оптимизируются запросы у разработчика биллинга, уверен, что многие места в этих отчётах захинтованы, чтобы Oracle не выбирал заведомо неподходящий план. Естественно, в PostgreSQL это не применимо и многие вещи пришлось бы переписать.

                  Другой пример: у нас есть ряд SQL-запросов, генерирующих иерархический json (демонстрация того, как это делать - http://sqlfiddle.com/#!17/b0d7a/7). На Oracle такого 1:1, скорее всего, не написать, поэтому когда коллеге надо было сделать подобный функционал под Oracle, он посмотрел на наши примеры, решил, что это слишком сложно, и написал рекурсивную функцию на pl/sql. В итоге один такой оракловый запрос выполняется дольше чем у нас десяток (совсем честное сравнение не делалось, но железо было примерно похожее, а объём генерируемого json-а и уровень вложенности у нас был, в основном, больше) и им пришлось вставить кеш таких json-ов с ручным сбросом. При чём им повезло с тем, что кеш применим, у нас json-ы генерируются свои под конкретного пользователя.


                  1. jobgemws Автор
                    23.01.2022 13:29

                    Абсолютно с Вами согласен.

                    Однако, увы, неправильные решения в том числе именно так и принимаются причём повсеместно и даже сейчас.

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


      1. K_Chicago
        22.01.2022 02:59
        +1

        я бы сказал что вот как раз скриптовый язык PG - это очень таксебе pl/sql, с очень большой натяжкой. Пакетов нету, одно это уже роняет ценность как минимум вполовину. При миграции пакеты традиционно меняют на схемы...ужос же.


        1. rrrad
          22.01.2022 21:26
          +1

          Схемы в PostgreSQL не прибиты к юзерам, так что этот ваш "ужос" - вкусовщина.

          Зато исполнение кода в PostgreSQL не блокирует програмные объекты. В оракле на высоконагруженной системе обновить популярный пакет (да даже функцию) на нагруженной системе - целая проблема.


    1. CrushBy
      22.01.2022 11:25
      +1

      Пока получается что в большинстве случаев PG сравним или в 2-3 раза медленнее Oracle, хотя в иерархических запросах и курсорах с функцией random PG отстает в 10-20 раз.

      На самом деле, там не все так просто. У Oracle, например, сильно умнее планировщик запросов, но он, соответственно, планирует гораздо медленнее, чем PostgreSQL. И если Вы делаете много разных простых запросов (у которых планы не кэшируются), то бывают ситуации, когда PostgreSQL в итоге работает быстрее.

      Но у того же PostgreSQL есть одна большая проблема - он не умеет "перепланировать" запрос в ходе выполнения (что в каком-то виде умеет Oracle). То есть, если PostgreSQL построил план, а в ходе выполнения выяснилось, что записей там не 100000, а 10, то PostgreSQL продолжит выполнение по старому плану, что может привести к катастрофическим затратам на выполнение. Мы это (так как автоматически компилируем запросы) решаем таким образом : когда видим, что запрос выполняется дольше чем должен (порог определяется эвристически), то останавливаем запрос и автоматически разбиваем его на несколько запросов (с временными таблицами). Тогда уже при повторном выполнении PostgreSQL будет знать статистику во временной таблице и построит новый план.

      Но стратегически просто надо сразу писать запросы с учетом определенных СУБД. И то, что оптимизировано под PostgreSQL не станет быстрее работать на Oracle (без рефакторинга). Более подробно разницу выполнения запросов на разных СУБД с примерами мы описывали в этой статье.


      1. rrrad
        22.01.2022 22:59
        +1

        Вообще, существует достаточно вариантов, когда написанный под PostgreSQL код будет медленнее работать на Oracle, если его просто перенести и подправить синтаксически.

        Что касается планировщика: для простых запросов это идёт ораклу в минус, т.к. тупо теряется время на IPC, в итоге, пока Oracle планирует запрос, PostgreSQL его уже выполнил. Один из примеров - execute (immediate) с простым запросом в цикле. Да, так неправильно делать и под PostgreSQL это также будет работать хуже, чем статически написанный запрос. Но при этом потеря производительности из-за динамического запроса по сравнению с обычным в Oracle будет гораздо больше (раз в 10), чем в PostgreSQL.


        1. jobgemws Автор
          22.01.2022 23:07

          Приведите полностью скрипт для слона и для оракла и по времени сколько получилось?

          Также важно расписать настройки каждой СУБД, настройки ОС под каждую СУБД, ФС, систему ввода-вывода и железо.

          Только после этого можно объективно сравнить производительность решения и оценить качество данной проверки (всё ли было настроено верно и в равных ли условиях были обе СУБД).


          1. rrrad
            22.01.2022 23:58
            +1

            Не приведу - давно было, сейчас доступа к тем машинам нету. Да и правильный тест делается не совсем так: нужна одна машина (как вариант - полностью идентичные машины), куда накатывается сперва одна субд, затем другая. Могу лишь сказать, что опыта в настройке Oracle у админов, которые эти сервера ставили, было гораздо больше, чем в настройке PostgreSQL.


            1. jobgemws Автор
              23.01.2022 08:11
              +1

              В том то и проблема, что возможно настройки должны быть разными. Но для Oracle не буду утверждать (т к с ним не работал). Однако, слона лучше на винду не ставить равно как и скуль до 2019 версии лучше не на винду не ставить. Иначе заранее уже получите плохой результат. Если нет достаточного опыта в админстве одной из СУБД, то привлекают 2-3 экспертов, кто может провести ревью настроек и помочь в подготовке среды для тестирования. По крайней мере мы так делали. У нас тогда скуль 2017 был на линухе и был в проигрышном варианте, но всё равно оказался шустрее слона даже при таких настройках. Но там пришлось на линуксе тестить, ибо заказчик отказывался от винды.

              Ссылка на результат тестирования приведён вначале публикации.


  1. NewFirefly
    21.01.2022 22:48
    +1

    У нас на проекте PG 14, и много где current_timestamp используется, но у Вас эта функция неупомянута или в MS SQL нет сравнимого типа?


    1. jobgemws Автор
      21.01.2022 22:49
      +1

      Это текущая дата и время начала транзакции, а не текущая дата и время в глобальном понимании. Источник


  1. vagon333
    22.01.2022 06:18
    +1

    Используем SQL CLRs (функции, триггеры, хранимки на C#).
    Сталкивались ли с SQL CLR и на каком языке реализовывали альтернативу?
    PS: рассматриваем частичный перенос базок на PG.


    1. jobgemws Автор
      22.01.2022 08:25

      Подобные решения уходили в сам сервис. Однако, слон поддерживает разные языки программирования, о чем более подробно можно почитать здесь. Т е можно и в процедуру завернуть.