/
Stils
/
SQLTranslator
Обзор
Документация
Войти
/
Stils
/
SQLTranslator
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
newrag.json
960 строк
84 KB
stilsman
rag
23 май 2025, 16:40
23 май 2025, 16:40
197e8f5
Код
Авторство
О чём код?
[ { "id": "101", "title": "Автоинкрементные поля: IDENTITY vs SERIAL", "page": 1, "keywords": [ "автоинкремент", "identity", "serial", "T-SQL", "PostgreSQL" ], "text": "T-SQL использует конструкцию IDENTITY(seed, increment) для автоинкрементных столбцов. В PostgreSQL 9.4 используется тип SERIAL или явное объявление с использованием SEQUENCE. Пример: IDENTITY(1,1) → SERIAL" }, { "id": "102", "title": "LIMIT vs TOP", "page": 2, "keywords": [ "LIMIT", "TOP", "SELECT", "T-SQL", "PostgreSQL" ], "text": "T-SQL использует TOP (n) в SELECT-запросах, например SELECT TOP 10 * FROM table. PostgreSQL использует LIMIT: SELECT * FROM table LIMIT 10." }, { "id": "103", "title": "GETDATE() vs CURRENT_TIMESTAMP", "page": 3, "keywords": [ "GETDATE", "CURRENT_TIMESTAMP", "время", "дата", "T-SQL", "PostgreSQL" ], "text": "В T-SQL для получения текущего времени используется GETDATE(). В PostgreSQL 9.4 аналог — CURRENT_TIMESTAMP или NOW()." }, { "id": "104", "title": "CONVERT vs CAST", "page": 4, "keywords": [ "CONVERT", "CAST", "приведение типов", "T-SQL", "PostgreSQL" ], "text": "T-SQL использует как CAST, так и CONVERT. PostgreSQL поддерживает только CAST или синтаксис ::. Пример: CONVERT(VARCHAR, date) → date::VARCHAR" }, { "id": "105", "title": "ISNULL vs COALESCE", "page": 5, "keywords": [ "ISNULL", "COALESCE", "NULL", "T-SQL", "PostgreSQL" ], "text": "T-SQL использует ISNULL для замены NULL значений. В PostgreSQL вместо ISNULL используется COALESCE. Пример: ISNULL(col, 0) → COALESCE(col, 0)" }, { "id": "106", "title": "TRY...CATCH vs EXCEPTION", "page": 6, "keywords": [ "TRY CATCH", "EXCEPTION", "ошибки", "T-SQL", "PostgreSQL" ], "text": "T-SQL использует конструкцию TRY...CATCH. В PostgreSQL используется блок BEGIN...EXCEPTION WHEN... THEN... внутри функций или DO $$ блоков на PL/pgSQL." }, { "id": "107", "title": "MERGE vs INSERT ON CONFLICT", "page": 7, "keywords": [ "MERGE", "UPSERT", "INSERT", "ON CONFLICT", "T-SQL", "PostgreSQL" ], "text": "T-SQL поддерживает MERGE для операций UPSERT. В PostgreSQL до версии 9.5 MERGE не поддерживается. В версии 9.4 необходимо реализовывать UPSERT через CTE или функцию. Пример: WITH upd AS (...) INSERT/UPDATE..." }, { "id": "108", "title": "STORED PROCEDURES vs FUNCTIONS", "page": 8, "keywords": [ "процедуры", "функции", "stored procedures", "functions", "T-SQL", "PostgreSQL" ], "text": "В T-SQL хранимые процедуры создаются через CREATE PROCEDURE и могут использовать RETURN. В PostgreSQL до версии 11 нет полноценной поддержки процедур — используются функции (CREATE FUNCTION) с языком plpgsql." }, { "id": "109", "title": "TRANSACTION ISOLATION LEVEL", "page": 9, "keywords": [ "транзакции", "ISOLATION LEVEL", "T-SQL", "PostgreSQL" ], "text": "T-SQL использует SET TRANSACTION ISOLATION LEVEL [READ UNCOMMITTED|READ COMMITTED|REPEATABLE READ|SERIALIZABLE]. PostgreSQL поддерживает те же уровни, но синтаксис другой: SET TRANSACTION ISOLATION LEVEL ... внутри BEGIN." }, { "id": "110", "title": "SET NOCOUNT ON", "page": 10, "keywords": [ "SET NOCOUNT", "производительность", "T-SQL", "PostgreSQL" ], "text": "T-SQL поддерживает SET NOCOUNT ON для отключения сообщений о количестве строк. PostgreSQL не имеет прямого аналога, поведение контролируется клиентским драйвером или настройками." }, { "id": "111", "title": "Временные таблицы: #temp и ##temp vs TEMP TABLE", "page": 11, "keywords": [ "временные таблицы", "TEMP", "#", "##", "T-SQL", "PostgreSQL" ], "text": "В T-SQL временные таблицы создаются с префиксом # (локальные) или ## (глобальные) и существуют в tempdb. В PostgreSQL используются конструкции CREATE TEMP TABLE или CREATE TEMPORARY TABLE, действующие в пределах сессии или транзакции. PostgreSQL не поддерживает глобальные временные таблицы (##). Пример: T-SQL: SELECT * INTO #temp FROM my_table; → PostgreSQL: CREATE TEMP TABLE temp AS SELECT * FROM my_table;" }, { "id": "112", "title": "APPLY (CROSS APPLY и OUTER APPLY)", "page": 12, "keywords": [ "APPLY", "CROSS APPLY", "OUTER APPLY", "T-SQL", "PostgreSQL", "LATERAL" ], "text": "T-SQL поддерживает CROSS APPLY и OUTER APPLY для соединения с табличными функциями или подзапросами. В PostgreSQL аналогом является использование оператора LATERAL. Пример: FROM users u CROSS APPLY dbo.GetOrders(u.id) o → FROM users u, LATERAL GetOrders(u.id) o" }, { "id": "113", "title": "CTE: Common Table Expressions", "page": 13, "keywords": [ "CTE", "WITH", "рекурсия", "T-SQL", "PostgreSQL" ], "text": "Обе СУБД поддерживают CTE (WITH), включая рекурсивные запросы. Однако в PostgreSQL необходимо явно указывать RECURSIVE, если выражение рекурсивное. Пример: T-SQL: WITH cte AS (...) → PostgreSQL: WITH RECURSIVE cte AS (...)" }, { "id": "1.1", "title": "Типы данных: MONEY и NUMERIC", "page": 1, "keywords": ["MONEY", "NUMERIC", "тип данных", "SQL Server", "PostgreSQL"], "text": "T-SQL: тип данных MONEY занимает 8 байт, точно хранит до 19 цифр с 4 знаками после запятой (аналог NUMERIC(19,4)). PostgreSQL: MONEY также 8 байт, но по умолчанию масштаб 2 (аналог NUMERIC(19,2) при локали C), т.е. две дробные цифры. Пример: DECLARE @m MONEY = 123.4567; → CREATE TABLE t(m MONEY DEFAULT 123.4567);." }, { "id": "1.2", "title": "Типы данных: UNIQUEIDENTIFIER vs UUID", "page": 1, "keywords": ["UNIQUEIDENTIFIER", "UUID", "тип данных", "GUID", "уникальный идентификатор"], "text": "T-SQL: тип UNIQUEIDENTIFIER хранит 16-байтовые GUID. Часто задаётся с DEFAULT NEWID(). PostgreSQL: эквивалентный тип называется UUID. Его значение можно генерировать функциями расширения (например, uuid-ossp). Пример: CREATE TABLE t(id UNIQUEIDENTIFIER DEFAULT NEWID()); → CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\"; CREATE TABLE t(id UUID DEFAULT uuid_generate_v4());." }, { "id": "1.3", "title": "Типы данных: DATETIME, DATETIME2, SMALLDATETIME", "page": 1, "keywords": ["DATETIME", "DATETIME2", "TIMESTAMP", "времена", "тип данных"], "text": "T-SQL: DATETIME (1 января 1753 – 31 декабря 9999, точность ~3.33 мс), SMALLDATETIME (1900–2079, точность 1 минута), DATETIME2(p) (0001–9999, точность до 100 наносекунд при p=7). PostgreSQL: вместо них используются TIMESTAMP [WITHOUT TIME ZONE] и TIMESTAMP WITH TIME ZONE. Например, DATETIME2(p) соответствует TIMESTAMP(p) (PostgreSQL ограничен p ≤ 6, т.е. 1 микросекунда точности). Пример: DATETIME2(3) → TIMESTAMP(3)." }, { "id": "1.4", "title": "Типы данных: DATETIMEOFFSET", "page": 1, "keywords": ["DATETIMEOFFSET", "TIMESTAMPTZ", "тип данных", "часы", "временная зона"], "text": "T-SQL: DATETIMEOFFSET(p) хранит дату-время с часовым поясом. PostgreSQL: аналогичный тип — TIMESTAMP WITH TIME ZONE (TIMESTAMPTZ). Пример: CREATE TABLE t(dt DATETIMEOFFSET(2)); → CREATE TABLE t(dt TIMESTAMP(2) WITH TIME ZONE);." }, { "id": "1.5", "title": "Типы данных: BIT vs BOOLEAN", "page": 1, "keywords": ["BIT", "BOOLEAN", "логический тип данных"], "text": "T-SQL: BIT хранит 0, 1 или NULL. PostgreSQL: аналогичный логический тип — BOOLEAN, значения TRUE/FALSE (или t/f). Пример: CREATE TABLE t(b BIT); INSERT INTO t VALUES (1); → CREATE TABLE t(b BOOLEAN); INSERT INTO t VALUES (TRUE);." }, { "id": "1.6", "title": "Типы данных: NVARCHAR, VARCHAR, TEXT", "page": 1, "keywords": ["NVARCHAR", "VARCHAR", "TEXT", "строковые типы", "Unicode"], "text": "T-SQL: NVARCHAR(n) (до 4000) — Unicode-строка (UTF-16), NVARCHAR(MAX) (~2 ГБ), также есть VARCHAR(n), VARCHAR(MAX) и устаревший TEXT. PostgreSQL: нет отдельного NVARCHAR (символьные типы хранятся в кодировке БД), вместо этого VARCHAR(n) и TEXT. Пример: NVARCHAR(50) → VARCHAR(50) (с учётом кодировки БД), VARCHAR(MAX)/TEXT в SQL Server → TEXT в PostgreSQL." }, { "id": "1.7", "title": "Типы данных: BINARY, VARBINARY, IMAGE", "page": 1, "keywords": ["BINARY", "VARBINARY", "IMAGE", "BYTEA", "бинарные данные"], "text": "T-SQL: BINARY(n), VARBINARY(n), VARBINARY(MAX), IMAGE используются для хранения бинарных данных. PostgreSQL: применяется тип BYTEA (или большие объекты lo, но обычно BYTEA). Пример: CREATE TABLE t(data VARBINARY(MAX)); → CREATE TABLE t(data BYTEA);." }, { "id": "1.8", "title": "Типы данных: TINYINT, SMALLMONEY", "page": 1, "keywords": ["TINYINT", "SMALLMONEY", "тип данных"], "text": "T-SQL: TINYINT (0–255) — в PostgreSQL отсутствует; обычно используют SMALLINT (диапазон −32768–32767). SQL Server: есть SMALLMONEY (64-битный с 4 знаками), PostgreSQL: такого типа нет, используют MONEY. Пример: DECLARE @x TINYINT = 200; → CREATE TABLE t(x SMALLINT DEFAULT 200);." }, { "id": "2.1", "title": "DDL: Имена, кавычки и COLLATE", "page": 2, "keywords": ["имена таблиц", "кавычки", "COLLATE", "DDL"], "text": "T-SQL: идентификаторы могут содержать специальные символы и регистр, например [Test Table]. COLLATE указывается после типа, например NVARCHAR(10) COLLATE Cyrillic_General_CI_AS. PostgreSQL: идентификаторы чувствительны к регистру и требуют двойных кавычек: \"Test Table\". COLLATE указывается как VARCHAR(10) COLLATE \"ru_RU\". Временные таблицы создаются через CREATE TEMP TABLE, вместо #TempTable." }, { "id": "2.2", "title": "DDL: IDENTITY vs SERIAL/GENERATED", "page": 2, "keywords": ["IDENTITY", "SERIAL", "GENERATED", "DDL", "автоинкремент"], "text": "T-SQL: при создании таблицы используют IDENTITY(start,step), например ID INT IDENTITY(1,1). PostgreSQL: исторически использовали SERIAL (напр. SERIAL эквивалентно созданию последовательности и DEFAULT nextval) или в PostgreSQL ≥10 GENERATED {ALWAYS|BY DEFAULT} AS IDENTITY. Пример: CREATE TABLE t(id INT IDENTITY(1,1) PRIMARY KEY); → CREATE TABLE t(id SERIAL PRIMARY KEY); (или id INT GENERATED BY DEFAULT AS IDENTITY)." }, { "id": "2.3", "title": "DDL: ALTER TABLE и DEFAULT", "page": 2, "keywords": ["ALTER TABLE", "DEFAULT", "переименование", "DDL", "констрейнты"], "text": "T-SQL: ALTER TABLE t ADD CONSTRAINT DF DEFAULT GETDATE() FOR dt; → PostgreSQL: ALTER TABLE t ALTER COLUMN dt SET DEFAULT now();. Для переименований: T-SQL использует sp_rename, в PostgreSQL — ALTER TABLE t RENAME COLUMN old TO new." }, { "id": "2.4", "title": "DDL: Индексы (CREATE INDEX)", "page": 2, "keywords": ["CREATE INDEX", "индексы", "кластерный индекс", "INCLUDE"], "text": "T-SQL: CREATE [UNIQUE] INDEX idx ON table(col1, col2) (есть опция CLUSTERED/NONCLUSTERED, включая столбцы через INCLUDE). PostgreSQL: CREATE INDEX idx ON table(col1, col2) (нет CLUSTERED в синтаксисе, INCLUDE доступен в поздних версиях). Например: CREATE UNIQUE INDEX i1 ON t(col1, col2); выполняется аналогично." }, { "id": "2.5", "title": "DDL: Наследование таблиц (PostgreSQL)", "page": 2, "keywords": ["наследование таблиц", "PostgreSQL", "DDL"], "text": "T-SQL (SQL Server) не поддерживает наследование таблиц. PostgreSQL имеет расширение: при CREATE TABLE child (...) INHERITS (parent). Унаследованные столбцы ведут себя как отдельные таблицы с общими полями. Пример: CREATE TABLE parent(id INT); CREATE TABLE child(extra TEXT) INHERITS (parent);." }, { "id": "3.1", "title": "DML: SELECT TOP vs LIMIT/OFFSET", "page": 3, "keywords": ["SELECT", "TOP", "LIMIT", "OFFSET", "DML", "ограничение строк"], "text": "T-SQL: ограничение числа строк делается с SELECT TOP n .... PostgreSQL: используется LIMIT n [OFFSET m]. Пример: SELECT TOP 10 name FROM t ORDER BY id; → SELECT name FROM t ORDER BY id LIMIT 10;. Также PostgreSQL поддерживает ANSI-стиль OFFSET k LIMIT n." }, { "id": "3.2", "title": "DML: SELECT INTO", "page": 3, "keywords": ["SELECT INTO", "CREATE TABLE AS", "вставка", "создание таблицы"], "text": "T-SQL: SELECT ... INTO new_table FROM ... создаёт новую таблицу. PostgreSQL: используют CREATE TABLE new_table AS SELECT .... Пример: SELECT * INTO new_table FROM old_table; → CREATE TABLE new_table AS SELECT * FROM old_table;." }, { "id": "3.3", "title": "DML: APPLY (CROSS/OUTER APPLY)", "page": 3, "keywords": ["APPLY", "LATERAL", "CROSS APPLY", "LEFT JOIN", "табличные выражения"], "text": "T-SQL: есть операторы CROSS APPLY и OUTER APPLY для коррелированных подзапросов. PostgreSQL: эквивалент — CROSS JOIN LATERAL и LEFT JOIN LATERAL. Пример: SELECT * FROM t CROSS APPLY f(t.id); → SELECT * FROM t CROSS JOIN LATERAL f(t.id);." }, { "id": "3.4", "title": "DML: PIVOT/UNPIVOT", "page": 3, "keywords": ["PIVOT", "UNPIVOT", "сводные", "переключатель", "табличные выражения"], "text": "T-SQL: есть конструкции PIVOT и UNPIVOT для поворота строк в столбцы и обратно. PostgreSQL: прямых аналогов нет, приходится использовать либо условную агрегацию (CASE WHEN ... THEN ... END) либо расширение tablefunc с функцией crosstab. Например, PIVOT в SQL можно заменить в PostgreSQL SELECT ... FROM (...) AS src GROUP BY ... с FILTER или SUM(CASE ...)." }, { "id": "3.5", "title": "DML: INSERT с OUTPUT vs RETURNING", "page": 3, "keywords": ["INSERT", "OUTPUT", "RETURNING", "генерируемые ключи"], "text": "T-SQL: оператор INSERT позволяет выводить вставленные строки через OUTPUT inserted.col. PostgreSQL: используется RETURNING. Пример: INSERT INTO t(col) OUTPUT inserted.id VALUES (1); → INSERT INTO t(col) VALUES (1) RETURNING id;. Также для вставки конкретных ID: T-SQL: SET IDENTITY_INSERT table ON, PostgreSQL: нужно напрямую указывать значение (или OVERRIDING SYSTEM VALUE в более новых версиях)." }, { "id": "3.6", "title": "DML: MERGE (UPSERT)", "page": 3, "keywords": ["MERGE", "UPSERT", "ON CONFLICT", "вставка или обновление"], "text": "T-SQL: поддерживает MERGE ... WHEN MATCHED/NOT MATCHED THEN ... для операций «вставка или обновление». PostgreSQL 9.4 не имеет MERGE (UPSERT появился в 9.5 через ON CONFLICT). Для эмуляции используют последовательность INSERT ... ON CONFLICT ... DO UPDATE (в 9.5+) или PL/pgSQL/CTE. Пример: MERGE INTO t ... нужно переделать в INSERT ... ON CONFLICT ... (в 9.4 придётся писать отдельную логику)." }, { "id": "3.7", "title": "DML: UPDATE ... FROM", "page": 3, "keywords": ["UPDATE", "FROM", "JOIN", "DML", "обновление"], "text": "T-SQL: позволяет UPDATE t SET t.a = s.b FROM source s WHERE t.id = s.id. PostgreSQL: синтаксис похож, поддерживается UPDATE ... FROM: UPDATE t SET a = s.b FROM source s WHERE t.id = s.id;. Пример: в T-SQL UPDATE t SET t.a = s.b FROM t JOIN s ON t.id=s.id; в PostgreSQL UPDATE t SET a = s.b FROM s WHERE t.id = s.id;." }, { "id": "3.8", "title": "DML: DELETE и OUTPUT/RETURNING", "page": 3, "keywords": ["DELETE", "OUTPUT", "RETURNING", "удаление"], "text": "T-SQL: DELETE FROM table OUTPUT deleted.col. PostgreSQL: DELETE FROM table RETURNING col. Пример: DELETE FROM t OUTPUT deleted.id WHERE cond; → DELETE FROM t WHERE cond RETURNING id;." }, { "id": "4.1", "title": "Процедуры: CREATE PROCEDURE", "page": 4, "keywords": ["CREATE PROCEDURE", "функции", "процедуры", "PL/pgSQL"], "text": "T-SQL: CREATE PROCEDURE имя ... AS BEGIN ... END. PostgreSQL: до версии 11 не было процедур, вместо них пишут CREATE FUNCTION имя(...) RETURNS void AS $$ BEGIN ... END; $$ LANGUAGE plpgsql;. Пример: CREATE PROCEDURE p AS BEGIN SELECT 1; END; → CREATE OR REPLACE FUNCTION p() RETURNS void AS $$ BEGIN RAISE NOTICE '1'; END; $$ LANGUAGE plpgsql;." }, { "id": "4.2", "title": "Функции: RETURNS и TABLE", "page": 4, "keywords": ["CREATE FUNCTION", "TABLE-выражения", "RETURNS TABLE", "функции"], "text": "T-SQL: CREATE FUNCTION f() RETURNS TABLE(x INT) AS RETURN SELECT 1 AS x;. PostgreSQL: CREATE FUNCTION f() RETURNS TABLE(x INT) AS $$ BEGIN RETURN QUERY SELECT 1 AS x; END; $$ LANGUAGE plpgsql;." }, { "id": "4.3", "title": "Переменные: DECLARE и присвоение", "page": 4, "keywords": ["DECLARE", "переменные", "SET", "SELECT", "присвоение"], "text": "T-SQL: переменные объявляются с DECLARE @v INT = 1; или DECLARE @v INT; SET @v = 1;. PostgreSQL: переменные можно объявлять только внутри функций/блоков DO, без @: DECLARE v INT := 1; или v := 1;. Пример: DECLARE @x INT = 5; SET @x = @x + 1; (T-SQL) эквивалентно в PL/pgSQL DO $$ DECLARE x INT := 5; BEGIN x := x + 1; END; $$ LANGUAGE plpgsql;." }, { "id": "5.1", "title": "Курсоры", "page": 5, "keywords": ["CURSOR", "FETCH", "DECLARE", "курсоры"], "text": "T-SQL: курсор объявляется как DECLARE c CURSOR FOR SELECT ...; OPEN c; FETCH NEXT FROM c INTO @v; ... CLOSE c; DEALLOCATE c;. PostgreSQL: в PL/pgSQL DECLARE c CURSOR FOR SELECT ...; OPEN c; FETCH c INTO v; ... CLOSE c;. Важно: курсоры PostgreSQL работают внутри транзакций, имена без @. Пример: DECLARE @id INT; DECLARE c CURSOR FOR SELECT id FROM t; OPEN c; FETCH NEXT FROM c INTO @id; → DO $$ DECLARE id INT; CURSOR c FOR SELECT id FROM t; BEGIN OPEN c; FETCH c INTO id; CLOSE c; END; $$ LANGUAGE plpgsql;." }, { "id": "5.2", "title": "Условные конструкции: IF/ELSE, WHILE", "page": 5, "keywords": ["IF", "ELSE", "WHILE", "LOOP", "условные операторы"], "text": "T-SQL: есть IF condition BEGIN ... END и цикл WHILE. Пример: IF @x > 0 BEGIN PRINT 'OK'; END ELSE BEGIN PRINT 'No'; END. PostgreSQL (PL/pgSQL): аналогично: IF x > 0 THEN RAISE NOTICE 'OK'; ELSE RAISE NOTICE 'No'; END IF;. Циклы WHILE и конструкции FOR ... IN ... LOOP доступны. В чистом SQL (без процедур) условные конструкции недоступны (только внутри функций/DO)." }, { "id": "5.3", "title": "Обработка ошибок: TRY/CATCH vs EXCEPTION", "page": 5, "keywords": ["TRY/CATCH", "EXCEPTION", "RAISERROR", "RAISE"], "text": "T-SQL: блок BEGIN TRY ... END TRY BEGIN CATCH ... END CATCH и RAISERROR. PostgreSQL: BEGIN ... EXCEPTION WHEN ... THEN ... END. Пример: BEGIN TRY SELECT 1/0; END TRY BEGIN CATCH PRINT ERROR_MESSAGE(); END CATCH → DO $$ BEGIN SELECT 1/0; EXCEPTION WHEN division_by_zero THEN RAISE NOTICE '%', SQLERRM; END; $$;." }, { "id": "6.1", "title": "Функции: строковые (LEN, SUBSTRING, CONCAT)", "page": 6, "keywords": ["LEN", "LENGTH", "SUBSTRING", "CONCAT", "строковые функции"], "text": "T-SQL: функции LEN(str), SUBSTRING(str, start, len), CHARINDEX(substr, str), CONCAT(...), LEFT(str,n), RIGHT(str,n). PostgreSQL: LENGTH(str) (или CHAR_LENGTH), SUBSTRING(str FROM start FOR len), POSITION(substr IN str), CONCAT(...), LEFT(str,n), RIGHT(str,n). Пример: LEN(s), CHARINDEX('a', s) в PostgreSQL: LENGTH(s), POSITION('a' IN s)." }, { "id": "6.2", "title": "Функции: дата/время (DATEDIFF, GETDATE)", "page": 6, "keywords": ["DATEDIFF", "GETDATE", "NOW", "функции даты", "DATEPART"], "text": "T-SQL: функции GETDATE() (текущая дата-время), DATEADD, DATEDIFF(unit, start, end), DATEPART(unit, datetime). PostgreSQL: аналогично NOW(), CURRENT_TIMESTAMP, функция DATE_TRUNC, EXTRACT(unit FROM datetime). Пример: SELECT DATEDIFF(day, '2021-01-01', '2021-01-10') → SELECT EXTRACT(DAY FROM '2021-01-10'::timestamp - '2021-01-01'::timestamp);." }, { "id": "6.3", "title": "Функции: численные и агрегаты (SUM, STDEV)", "page": 6, "keywords": ["SUM", "STDEV", "STDDEV", "агрегация", "функции"], "text": "T-SQL: функции SUM, AVG, MIN, MAX, COUNT, а также STDEV, STDEVP. PostgreSQL: аналогично SUM, AVG, MIN, MAX, COUNT, есть STDDEV_POP/STDDEV_SAMP (сокращённо STDDEV) и VAR_POP/VAR_SAMP. Например, SELECT STDEV(x) FROM t; → SELECT STDDEV(x) FROM t;. Кроме того, PostgreSQL имеет функцию GROUPING(col) вместо GROUPING_ID(col, ...)." }, { "id": "6.4", "title": "Системные функции и переменные", "page": 6, "keywords": ["@@ROWCOUNT", "SCOPE_IDENTITY", "статус", "системные функции"], "text": "T-SQL: системные функции @@ROWCOUNT, @@IDENTITY, SCOPE_IDENTITY(). PostgreSQL: нет прямых аналогов, но в PL/pgSQL есть GET DIAGNOSTICS rowcount = ROW_COUNT для числа затронутых строк и функция currval('seq_name') для последнего значения последовательности. Пример: SELECT @n = @@ROWCOUNT; → GET DIAGNOSTICS n = ROW_COUNT; в PL/pgSQL." }, { "id": "7.1", "title": "Транзакции и уровни изоляции", "page": 7, "keywords": ["BEGIN TRANSACTION", "изоляция", "READ COMMITTED", "SERIALIZABLE", "SNAPSHOT"], "text": "T-SQL: BEGIN TRANSACTION, COMMIT, ROLLBACK, уровни изоляции READ UNCOMMITTED, READ COMMITTED, REPEATABLE READ, SNAPSHOT, SERIALIZABLE. PostgreSQL: транзакции аналогичны (BEGIN, COMMIT), но нет режима SNAPSHOT — вместо него SERIALIZABLE в PG 9.4 обеспечивает аналогичные гарантии. Уровень READ UNCOMMITTED в PostgreSQL фактически работает как READ COMMITTED. Пример: SET TRANSACTION ISOLATION LEVEL SNAPSHOT; → SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;." }, { "id": "7.2", "title": "Транзакции: SAVE и ROLLBACK", "page": 7, "keywords": ["SAVE TRANSACTION", "SAVEPOINT", "ROLLBACK", "транзакции"], "text": "T-SQL: SAVE TRANSACTION name для отметки, ROLLBACK TRANSACTION name для отката. PostgreSQL: SAVEPOINT name, ROLLBACK TO SAVEPOINT name. Пример: SAVE TRANSACTION mySavepoint; ... ROLLBACK TRANSACTION mySavepoint; → SAVEPOINT mySavepoint; ... ROLLBACK TO SAVEPOINT mySavepoint;." }, { "id": "7.3", "title": "Обработка ошибок: RAISERROR vs RAISE", "page": 7, "keywords": ["RAISERROR", "RAISE", "исключения"], "text": "T-SQL: RAISERROR('message', severity, state) или THROW для генерации ошибки. PostgreSQL (PL/pgSQL): RAISE [NOTICE|EXCEPTION] 'message';. Пример: RAISERROR('Error %d', 16, 1, 123); → RAISE EXCEPTION 'Error %', 123;." }, { "id": "8.1", "title": "CTE (WITH)", "page": 8, "keywords": ["CTE", "WITH", "общая табличная выражения", "рекурсивные запросы"], "text": "T-SQL и PostgreSQL оба поддерживают конструкцию WITH cte AS (...). Синтаксис и семантика во многом совпадают. В обоих можно использовать WITH RECURSIVE. Пример: WITH cte AS (SELECT ... ) SELECT * FROM cte; работает одинаково." }, { "id": "8.2", "title": "TVF (табличные функции)", "page": 8, "keywords": ["табличные функции", "TVF", "скалярные функции", "SELECT ... FROM function"], "text": "T-SQL: таблицы можно возвращать из функции, вызывая ее как SELECT * FROM dbo.fn(params). PostgreSQL: аналогично, если функция объявлена RETURNS TABLE, то ее можно вызывать в FROM: SELECT * FROM f(params). Пример: SELECT * FROM dbo.GetEmployees(); → SELECT * FROM get_employees();." }, { "id": "9.1", "title": "Триггеры", "page": 9, "keywords": ["триггер", "AFTER", "BEFORE", "INSTEAD OF", "NEW", "OLD"], "text": "T-SQL: CREATE TRIGGER ... ON table [AFTER|INSTEAD OF] INSERT, UPDATE, DELETE. Доступны псевдо-таблицы inserted/deleted. PostgreSQL: CREATE FUNCTION ... RETURNS trigger, затем CREATE TRIGGER ... BEFORE/AFTER INSERT OR UPDATE OR DELETE ON table FOR EACH ROW EXECUTE FUNCTION .... В теле триггера используются NEW и OLD. Пример: AFTER INSERT в SQL Server ↔ AFTER INSERT ON table FOR EACH ROW в PostgreSQL." }, { "id": "9.2", "title": "NULL и COLLATE", "page": 9, "keywords": ["NULL", "ANSI_NULLS", "COLLATE", "сравнение NULL"], "text": "T-SQL: есть SET ANSI_NULLS ON/OFF (если OFF, = NULL работает иначе). В PostgreSQL ANSI_NULLS всегда включён (NULL не равно никакому значению). Колонкам можно присваивать COLLATE: в SQL Server синтаксис ... COLLATE имя, в PostgreSQL — ... COLLATE \"имя_локали\". Пример: WHERE col = NULL (если OFF, возвращает TRUE) нет аналогов в PostgreSQL — нужно IS NULL." }, { "id": "10.1", "title": "Системные представления и catalog", "page": 10, "keywords": ["sys.tables", "INFORMATION_SCHEMA", "pg_catalog", "схема"], "text": "T-SQL: системная схема sys.* (например, sys.tables, sys.columns, sys.indexes). PostgreSQL: каталог pg_catalog (pg_class, pg_attribute и т.д.) и information_schema. Пример: SELECT * FROM sys.tables; → SELECT * FROM pg_catalog.pg_tables; или через information_schema.tables. Колонки и названия могут отличаться." }, { "id": "10.2", "title": "INFORMATION_SCHEMA vs sys.*", "page": 10, "keywords": ["INFORMATION_SCHEMA", "системные представления", "метаданные"], "text": "T-SQL и PostgreSQL оба поддерживают INFORMATION_SCHEMA, но набор таблиц и столбцов может отличаться. SQL Server дополнительно использует sys.tables, sys.columns, sys.databases и т.д. PostgreSQL: аналогами служат pg_catalog.pg_class (таблицы), pg_attribute (столбцы), pg_database и другие. Пример: SELECT * FROM INFORMATION_SCHEMA.TABLES работает в обеих СУБД, но sys.sql_expression_dependencies есть только в SQL Server." }, { "id": "11.1", "title": "Логика: объединение строк", "page": 11, "keywords": ["конкатенация", "строки", "+", "||", "CONCAT"], "text": "T-SQL: конкатенация строк оператором +, например SELECT 'a' + 'b';. PostgreSQL: оператор || или функция CONCAT. Пример: SELECT 'a' + 'b'; в PostgreSQL выдаст ошибку, вместо этого SELECT 'a' || 'b';." }, { "id": "11.2", "title": "Логика: булевы значения", "page": 11, "keywords": ["булевы", "TRUE", "FALSE", "0", "1", "логика"], "text": "T-SQL: булевы значения хранятся в типе BIT (0 или 1), где SELECT CAST(1 AS BIT) = 1. PostgreSQL: булевы BOOLEAN (TRUE/FALSE). При переносе логики нужно пересмотреть условия, например WHERE flag = 1 в PG становится WHERE flag IS TRUE. Пример: IF @b = 1 SELECT 'ok'; → IF b THEN RAISE NOTICE 'ok';." }, { "id": "11.3", "title": "Логика: DATE и DATETIME", "page": 11, "keywords": ["datetime", "дата", "TIMESTAMP", "TIMEZONE", "разница"], "text": "T-SQL: GETDATE() возвращает локальное время сервера, GETUTCDATE() — UTC-время. PostgreSQL: NOW() по умолчанию возвращает TIMESTAMP WITH TIME ZONE (локальное время с часовым поясом) или CURRENT_TIMESTAMP. Следует учесть, что без указания AT TIME ZONE результаты могут отличаться. Пример: SELECT GETDATE(); → SELECT NOW();. Для получения UTC: SELECT NOW() AT TIME ZONE 'UTC';." }, { "id": "11.4", "title": "Сортировка NULL-значений", "page": 11, "keywords": ["ORDER BY", "NULL", "NULLS FIRST", "NULLS LAST", "T-SQL", "PostgreSQL"], "text": "В T‑SQL значения NULL при сортировке считаются минимальными. При ORDER BY … ASC строки с NULL будут выводиться первыми (NULLs considered lowest). В PostgreSQL по умолчанию NULL считается наибольшим (для ASC сортировки фактически используется NULLS LAST, а для DESC – NULLS FIRST). При необходимости можно явно указывать NULLS FIRST или NULLS LAST в выражении ORDER BY. Например:\n\n-- T-SQL (NULL идут первыми)\nSELECT col FROM table ORDER BY col ASC;\n-- PostgreSQL (по умолчанию NULL идут в конец, т.е. как NULLS LAST)\nSELECT col FROM table ORDER BY col ASC;\n-- Чтобы в PostgreSQL получить поведение SQL Server (NULLs FIRST):\nSELECT col FROM table ORDER BY col ASC NULLS FIRST;\n" }, { "id": "11.5", "title": "TOP … WITH TIES vs FETCH FIRST", "page": 11, "keywords": ["TOP", "WITH TIES", "LIMIT", "FETCH FIRST", "PostgreSQL 9.4"], "text": "В T‑SQL есть конструкция TOP N WITH TIES, которая возвращает первые N строк по ORDER BY и все дополнительные строки, имеющие те же значения в сортируемых колонках (т.е. «соперников» N-й строки). Например: SELECT TOP 3 WITH TIES * FROM table ORDER BY score;. PostgreSQL до версии 13 не поддерживал опцию WITH TIES; вместо этого используют стандартный LIMIT или FETCH FIRST N ROWS ONLY. Например:\n\n-- SQL Server\nSELECT TOP 3 WITH TIES * FROM employees ORDER BY salary;\n-- PostgreSQL 9.4\nSELECT * FROM employees ORDER BY salary FETCH FIRST 3 ROWS ONLY;\n \nПри таком запросе PostgreSQL вернёт ровно 3 строки; чтобы включить «соперников», придётся применять оконные функции, например RANK() (при этом RANK() даст тот же результат, что и WITH TIES)." }, { "id": "11.6", "title": "TIMESTAMP и автообновление дат", "page": 11, "keywords": ["DEFAULT GETDATE()", "CURRENT_TIMESTAMP", "trigger", "TIMESTAMP", "PostgreSQL"], "text": "В SQL Server функцию GETDATE() можно использовать как значение по умолчанию для столбца DATETIME. Например: created DATETIME DEFAULT GETDATE(). Однако при обновлении строки значение не меняется автоматически – для этого требуется триггер. Например, триггер AFTER UPDATE может выполнять SET modified = GETDATE() для изменённых строк. В PostgreSQL аналогично используют DEFAULT CURRENT_TIMESTAMP или DEFAULT now() для назначения времени вставки, а автоматическое обновление при UPDATE делают триггером. Например:\n\nCREATE TABLE users (\n id SERIAL,\n created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n modified TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n);\nCREATE FUNCTION trg_upd() RETURNS trigger AS $$\nBEGIN\n NEW.modified = now();\n RETURN NEW;\nEND;\n$$ LANGUAGE plpgsql;\nCREATE TRIGGER t BEFORE UPDATE ON users\nFOR EACH ROW EXECUTE FUNCTION trg_upd();\n" }, { "id": "11.7", "title": "IDENTITY_INSERT и явная вставка ID", "page": 11, "keywords": ["IDENTITY_INSERT", "SERIAL", "sequence", "PostgreSQL", "IDENTITY"], "text": "В SQL Server столбцы с IDENTITY обычно генерируют значения автоматически, и для явной вставки своего значения требуется включать режим SET IDENTITY_INSERT. Например:\n\nSET IDENTITY_INSERT users ON;\nINSERT INTO users (id, name) VALUES (100, 'John');\nSET IDENTITY_INSERT users OFF;\n \nPostgreSQL (версии 9.4) при использовании SERIAL или BIGSERIAL позволяет просто указать значение для столбца: INSERT INTO users (id, name) VALUES (100, 'John');. При этом для корректного обновления счётчика последовательности обычно выполняют SELECT setval('users_id_seq', (SELECT MAX(id) FROM users));. Специального режима типа IDENTITY_INSERT в PostgreSQL нет — при необходимости явная вставка идёт без ограничений прав (но требуется разрешение на вставку в таблицу)." }, { "id": "11.8", "title": "SELECT без FROM", "page": 11, "keywords": ["SELECT", "без FROM", "T-SQL", "PostgreSQL", "DUAL"], "text": "В T‑SQL разрешён запрос SELECT без секции FROM для получения констант или вызова функций. Например: SELECT 1 AS num; или SELECT GETDATE();. Аналогично и в PostgreSQL можно выполнять SELECT без FROM. Как отмечено на StackOverflow, «SQL Server, PostgreSQL и SQLite не требуют FROM DUAL». Примеры преобразования:\n\n-- SQL Server\nSELECT GETDATE() AS now;\nSELECT 1 AS num;\n-- PostgreSQL\nSELECT now() AS now;\nSELECT 1 AS num;\n \nОтличие лишь в названиях встроенных функций (GETDATE() → now(), системная переменная @@VERSION → функция version() и т.д.)." }, { "id": "11.9", "title": "OUTPUT vs RETURNING", "page": 11, "keywords": ["OUTPUT", "RETURNING", "INSERT", "UPDATE", "DELETE", "T-SQL", "PostgreSQL"], "text": "В SQL Server для операций INSERT, UPDATE, DELETE есть ключевое слово OUTPUT, которое возвращает значения затронутых строк (использует виртуальные таблицы INSERTED/DELETED). PostgreSQL предоставляет аналогичный по смыслу стандартный оператор RETURNING, работающий во всех таких операциях. Например:\n\n-- SQL Server\nINSERT INTO products(name) OUTPUT INSERTED.id VALUES('Book');\nUPDATE products SET price = price*1.1 OUTPUT INSERTED.id, INSERTED.price WHERE id < 10;\nDELETE FROM products OUTPUT DELETED.* WHERE discontinued = 1;\n-- PostgreSQL\nINSERT INTO products(name) VALUES('Book') RETURNING id;\nUPDATE products SET price = price*1.1 WHERE id < 10 RETURNING id, price;\nDELETE FROM products WHERE discontinued = true RETURNING *;\n \nВ обоих случаях возвращаются данные вставленных/обновлённых/удалённых строк, но синтаксис разный." }, { "id": "11.10", "title": "GOTO в хранимых процедурах", "page": 11, "keywords": ["GOTO", "перенос исполнения", "PL/pgSQL", "управление потоком"], "text": "В T‑SQL поддерживается оператор GOTO с метками для изменения потока выполнения внутри процедуры или пакета. Например:\n\nLabel:\n SELECT 1;\nGOTO Label;\n \nВ PostgreSQL в PL/pgSQL конструкции GOTO нет. Как отмечено на StackOverflow, «PL/PgSQL не имеет оператора GOTO». Для переходов следует использовать другие средства управления потоком (например, вложенные IF, циклы или RETURN из функций)." }, { "id": "11.11", "title": "WAITFOR / пауза выполнения", "page": 11, "keywords": ["WAITFOR DELAY", "pg_sleep", "задержка", "T-SQL", "PostgreSQL"], "text": "В SQL Server оператор WAITFOR позволяет приостановить выполнение. Например, WAITFOR DELAY '00:00:05'; делает паузу на 5 секунд. В PostgreSQL для задержки используют функцию pg_sleep(seconds). Например:\n\n-- SQL Server: приостановиться на 5 секунд\nWAITFOR DELAY '00:00:05';\n-- PostgreSQL: аналогично\nSELECT pg_sleep(5);\n \nФункция pg_sleep() приостанавливает выполнение в секундах (может принимать дробные значения)." }, { "id": "11.12", "title": "TEXTSIZE и VARCHAR(MAX)", "page": 11, "keywords": ["TEXTSIZE", "VARCHAR(MAX)", "TEXT", "лимит", "PostgreSQL"], "text": "SQL Server имеет устаревшие типы text, ntext, image и новые varchar(max), nvarchar(max). Параметр SET TEXTSIZE задаёт максимальный объём возвращаемых текстовых данных (по умолчанию 4 КБ, -1 означает неограниченно). Например, SET TEXTSIZE 1000; SELECT CAST(col AS VARCHAR(MAX)) FROM tbl;. В PostgreSQL тип text неограничен (максимум ~1 ГБ) и не требует специальных настроек, а varchar без указания длины допускает строки любой длины. В PG нет аналога SET TEXTSIZE. Чтобы получить тот же эффект, достаточно использовать text или varchar без ограничения: \n\n-- SQL Server\nSET TEXTSIZE 1000;\nSELECT CAST(col AS VARCHAR(MAX)) FROM tbl;\n-- PostgreSQL\nSELECT col::text FROM tbl;\n" }, { "id": "12.1", "title": "Роли и права: EXECUTE и EXECUTE AS", "page": 12, "keywords": ["GRANT EXECUTE", "EXECUTE AS", "SECURITY DEFINER", "права", "PostgreSQL"], "text": "В SQL Server на процедуры/функции выдают разрешение через GRANT EXECUTE ON OBJECT::schema.proc TO user. Также существует оператор EXECUTE AS для выполнения кода от имени другого пользователя. В PostgreSQL аналогично можно выдать GRANT EXECUTE ON FUNCTION func(type) TO role. В отличие от EXECUTE AS, PostgreSQL использует модификатор SECURITY DEFINER в определении функции, чтобы она выполнялась с привилегиями владельца. При этом нет прямого оператора EXECUTE AS user; вместо этого функцию создают так: \n\nCREATE FUNCTION func(...) RETURNS ... LANGUAGE plpgsql\nSECURITY DEFINER AS $$\nBEGIN\n -- тело функции\nEND;\n$$;\n \nТаким образом функция выполнится от имени своего создателя (владельца)." }, { "id": "12.2", "title": "CHECK-ограничения и NULL", "page": 12, "keywords": ["CHECK", "NULL", "ограничения", "T-SQL", "PostgreSQL"], "text": "В T-SQL и PostgreSQL семантика CHECK-ограничений совпадает: условие считается пройденным, если выражение возвращает TRUE или NULL. Например, в SQL Server:\n`CREATE TABLE tbl(a INT CHECK(a > 0)); INSERT INTO tbl(a) VALUES (NULL);` – вставка NULL проходит (NULL приводит к UNKNOWN, ограничение не срабатывает):contentReference[oaicite:0]{index=0}. В PostgreSQL поведение аналогично: CHECK считается выполненным, если результат условия – TRUE или NULL:contentReference[oaicite:1]{index=1}. При конверсии SQL-скриптов важно помнить, что оба диалекта допускают NULL через CHECK, поэтому если нужно запретить NULL, следует явно добавить NOT NULL." }, { "id": "12.3", "title": "DEFERRABLE CONSTRAINTS и их поддержка", "page": 12, "keywords": ["DEFERRABLE", "CONSTRAINTS", "деферируемые", "T-SQL", "PostgreSQL"], "text": "В SQL Server директива DEFERRABLE для ограничений не поддерживается: все ограничения проверяются немедленно (рантаймом):contentReference[oaicite:2]{index=2}. В PostgreSQL при создании ограничения можно указать `DEFERRABLE INITIALLY IMMEDIATE|DEFERRED`, и режим проверки можно менять командой `SET CONSTRAINTS`:contentReference[oaicite:3]{index=3}. При конвертации скриптов синтаксис DEFERRABLE нужно убрать или игнорировать: SQL Server просто не поймет эту опцию, и логику отложенной проверки придётся реализовывать иначе (например, через триггеры или пересмотреть логику транзакций)." }, { "id": "12.4", "title": "Стратегии блокировок и deadlock", "page": 12, "keywords": ["блокировки", "deadlock", "конкуренция", "T-SQL", "PostgreSQL"], "text": "SQL Server по умолчанию использует пессимистичную модель блокировок: при чтении или записи он устанавливает блокировки на строки/страницы:contentReference[oaicite:4]{index=4}. Это может приводить к блокировкам и deadlock’ам. PostgreSQL же использует MVCC: каждая транзакция работает со своей «снимком» данных, и читатели не блокируют писателей и наоборот:contentReference[oaicite:5]{index=5}. Тем не менее deadlock возможен и там, и там при конкурентных обновлениях. В SQL Server при deadlock сразу возвращается ошибка 1205, и одна из транзакций откатывается:contentReference[oaicite:6]{index=6}. В PostgreSQL после таймаута deadlock_timeout происходит обнаружение и бросается ошибка 'deadlock detected...':contentReference[oaicite:7]{index=7}. Откат транзакции происходит автоматически. Заметим также, что SQL Server поддерживает подсказки блокировки (например, WITH (ROWLOCK), NOLOCK и др.), а в PostgreSQL используются явные `SELECT ... FOR UPDATE/SHARE` или `LOCK TABLE`. В SQL Server есть эскалация блокировок на страницы при больших транзакциях, у PostgreSQL такого механизма нет." }, { "id": "12.5", "title": "Хинты (FORCE INDEX, OPTION(RECOMPILE)) и аналоги", "page": 12, "keywords": ["хинты", "FORCE INDEX", "OPTION(RECOMPILE)", "T-SQL", "PostgreSQL"], "text": "T-SQL позволяет давать оптимизатору подсказки в запросе: например, `FORCESEEK/INDEX`, `OPTION (RECOMPILE)`, `MAXDOP` и другие (см. документацию:contentReference[oaicite:8]{index=8}). В PostgreSQL таких встроенных хинтов нет. Так, в PG нет понятия повторной компиляции плана – оптимизатор каждый раз строит план заново, поэтому `OPTION(RECOMPILE)` обычно просто удаляют:contentReference[oaicite:9]{index=9}. Аналогом хинта на индекс может служить ручное отключение последовательного сканирования: `SET enable_seqscan = OFF; SELECT ...; SET enable_seqscan = ON;`. Для более точных подсказок есть расширение pg_hint_plan (оно не входит в стандартный набор PostgreSQL). Например, запрос в SQL Server: `SELECT * FROM tbl WITH (FORCESEEK);` в PostgreSQL без расширений не поддерживается; нужно полагаться на хорошие статистики и индексы." }, { "id": "12.6", "title": "Хранимые процедуры с несколькими наборами результатов", "page": 12, "keywords": ["хранимые процедуры", "результат", "мультизапрос", "T-SQL", "PostgreSQL", "refcursor"], "text": "В T-SQL хранимая процедура может возвращать несколько результирующих наборов просто двумя SELECT подряд. Например:\n`CREATE PROCEDURE sp AS BEGIN SELECT * FROM table1; SELECT * FROM table2; END;`.\nВ PostgreSQL (до версии 11) нет отдельных процедур, только функции. Функция может вернуть только один набор строк или курсор. Чтобы получить несколько наборов, нужно использовать `SETOF refcursor`: функция объявляется `RETURNS SETOF refcursor`, внутри открываются курсоры и для каждого выполняется `RETURN NEXT`:contentReference[oaicite:10]{index=10}. Например:\n`CREATE OR REPLACE FUNCTION sp() RETURNS SETOF refcursor AS $$ DECLARE c1 refcursor; c2 refcursor; BEGIN OPEN c1 FOR SELECT * FROM table1; RETURN NEXT c1; OPEN c2 FOR SELECT * FROM table2; RETURN NEXT c2; END; $$ LANGUAGE plpgsql;`.\nПри вызове такой функции через клиент нужно делать `FETCH` по каждому курсору (и учесть, что курсоры действуют в контексте транзакции, т.е. должна быть включена транзакция, чтобы они оставались открытыми)." }, { "id": "12.7", "title": "Поведение NULL в UNIQUE и GROUP BY", "page": 12, "keywords": ["NULL", "UNIQUE", "GROUP BY", "T-SQL", "PostgreSQL"], "text": "При ограничении UNIQUE в SQL Server допускается не более одного NULL в столбце – последующие записи с NULL вызовут ошибку (технически SQL Server считает, что NULL != NULL, но при ограничении он допускает лишь одну NULL):contentReference[oaicite:11]{index=11}. В PostgreSQL, наоборот, NULL считаются различными значениями, и в колонке с UNIQUE может быть несколько записей NULL. Например:\n`CREATE UNIQUE INDEX ix ON tbl(col); INSERT INTO tbl(col) VALUES (NULL), (NULL);` – в SQL Server вторая вставка упадёт, а в PostgreSQL обе пройдут. Что касается GROUP BY, в обоих диалектах все NULL сгруппируются вместе как отдельная группа (NULL в ключе считается одним «группировочным» значением). Существенных различий в GROUP BY нет." }, { "id": "12.8", "title": "Разрешения и роли", "page": 12, "keywords": ["разрешения", "роли", "GRANT", "T-SQL", "PostgreSQL"], "text": "В SQL Server существуют отдельные понятия логинов (логины SQL Server или Windows на уровне сервера) и пользователей (персон на уровне базы данных), а также отдельные роли сервера и роли базы. В PostgreSQL все объединено: понятие роли может выступать и пользователем, и группой:contentReference[oaicite:12]{index=12}. Например, в SQL Server:\n`CREATE LOGIN user1 WITH PASSWORD='pass'; CREATE USER user1 FOR LOGIN user1; GRANT SELECT ON table TO user1;`.\nВ PostgreSQL эквивалент:\n`CREATE ROLE user1 LOGIN PASSWORD 'pass'; GRANT SELECT ON table TO user1;`.\nПо умолчанию в SQL Server у пользователя схема dbo, в PostgreSQL – схема public. При миграции нужно перенести пользователей/ролей SQL в PostgreSQL-роли (с атрибутом LOGIN) и заново выдать права через GRANT." }, { "id": "12.9", "title": "Тонкости с COLLATE и его наследованием", "page": 12, "keywords": ["COLLATE", "наследование", "сортировка", "T-SQL", "PostgreSQL"], "text": "В SQL Server COLLATE можно задавать на уровне столбца или выражения; имена колляций включают параметры сравнения (регистрозависимость, акценты и т.д.) и кодовую страницу:contentReference[oaicite:13]{index=13}. В PostgreSQL 9.4 все строки хранятся в кодировке UTF-8 и сравниваются по локалям ОС. Колляция базы задаётся при создании (LC_COLLATE, LC_CTYPE) и наследуется по умолчанию на столбцы. В запросах PostgreSQL можно явно указывать `COLLATE имя_локали`, но имя должно существовать в системе (например, 'ru_RU.UTF-8'). При наследовании таблиц в PostgreSQL дочерняя таблица получает те же столбцы (и их колляции) от родителя, потому что столбцы совпадают по имени и типу. При конверсии нужно сверять или удалять COLLATE: например, SQL-серверную `COLLATE Cyrillic_General_CI_AS` может заменить PostgreSQL-колляция `ru_RU.UTF-8` или другая соответствующая локаль." }, { "id": "12.10", "title": "Конфликты транзакций и откат при deadlock", "page": 12, "keywords": ["deadlock", "конфликт", "транзакции", "ROLLBACK", "T-SQL", "PostgreSQL"], "text": "Если возникает deadlock между транзакциями, обе СУБД прерывают одну из транзакций с ошибкой и делают ROLLBACK. В SQL Server генерируется ошибка 1205, например:\n`Msg 1205, Level 13, State 51, Line 7: Transaction (Process ID 51) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.`:contentReference[oaicite:14]{index=14}. В PostgreSQL после короткого ожидания возникает ошибка вида:\n`ERROR: deadlock detected\nDETAIL: Process 123 waits for ... blocked by process 456 ... HINT: See server log for query details.`:contentReference[oaicite:15]{index=15}. В обоих случаях клиентская транзакция откатывается, и приложение может повторить операцию. Обратите внимание, что PostgreSQL сначала ждет (параметр deadlock_timeout) и лишь затем выявляет deadlock, а SQL Server обнаруживает его сразу." }, { "id": "12.11", "title": "Identity vs SERIAL auto-increment", "page": 12, "keywords": ["IDENTITY", "SERIAL", "auto-increment", "sequence"], "text": "SQL Server supports IDENTITY columns for auto-incrementing integers, whereas PostgreSQL 9.4 did not implement the SQL standard IDENTITY syntax. Instead, one uses the SERIAL (or BIGSERIAL) pseudo-type, which creates an associated sequence for auto-increment . For example, CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT); automatically creates a sequence that supplies values to id." }, { "id": "12.12", "title": "Stored procedures vs functions", "page": 12, "keywords": ["stored procedure", "function", "SQL Server", "PostgreSQL", "PL/pgSQL"], "text": "T-SQL provides both stored procedures (CREATE PROCEDURE) and functions (CREATE FUNCTION), where procedures do not return a value to the caller and functions do. In PostgreSQL 9.4, there were no separate stored procedures (that changed only in Postgres 11); all database subroutines are created with CREATE FUNCTION. PostgreSQL functions must return a value (or void) and are invoked like a query (e.g. SELECT myfunc()), unlike SQL Server which calls a procedure via EXEC. As noted, “Postgres…stored procedures were effectively functions that didn’t return data” ." }, { "id": "12.13", "title": "Executing routines: EXEC vs SELECT/CALL", "page": 12, "keywords": ["EXEC", "CALL", "SELECT", "stored procedure", "function"], "text": "In SQL Server, stored procedures are executed with EXEC procedure_name (or EXECUTE), and functions are typically called in a SELECT. PostgreSQL 9.4 (pre-procedures) uses SELECT function_name() to invoke any server-side routine. There is no EXEC or CALL command in PG9.4. Thus code like EXEC dbo.MyProcedure; in T-SQL would be written as SELECT my_function(); in Postgres PL/pgSQL ." }, { "id": "12.14", "title": "Transaction control inside routines", "page": 12, "keywords": ["transaction", "BEGIN", "COMMIT", "rollback", "stored procedure", "function", "PostgreSQL", "SQL Server"], "text": "SQL Server stored procedures can issue explicit transaction commands (BEGIN TRANSACTION, COMMIT, ROLLBACK) inside the procedure, and nested BEGIN/COMMIT are counted. PostgreSQL 9.4 functions cannot start or commit transactions within the function: all function bodies run within the caller’s transaction. (True stored procedures with transaction control were introduced only in Postgres 11 .) Thus you cannot COMMIT or ROLLBACK inside a PG9.4 function as you might in a T-SQL proc." }, { "id": "12.15", "title": "Boolean vs BIT data type", "page": 12, "keywords": ["BOOLEAN", "BIT", "data type", "NULL", "SQL Server", "PostgreSQL"], "text": "SQL Server uses the BIT data type for boolean logic, which can be 0, 1, or NULL. PostgreSQL has a dedicated BOOLEAN type with values TRUE, FALSE, or NULL . The semantics follow the SQL standard: TRUE/FALSE. For example, SQL Server WHERE bitcol = 1 vs Postgres WHERE boolcol = TRUE." }, { "id": "12.16", "title": "JSON vs XML support", "page": 12, "keywords": ["JSON", "XML", "data type", "NoSQL", "SQL Server", "PostgreSQL"], "text": "PostgreSQL 9.4 offers built-in JSON support (including the json data type and JSON functions) as part of its standard library. SQL Server (2014) did not have a JSON data type; instead it provides JSON parsing functions (from 2016) and has an XML data type. As one summary notes, PostgreSQL provides advanced features like JSON and full-text search, whereas SQL Server focuses on XML and other features . Thus JSON workloads are more natively integrated in Postgres, whereas SQL Server relies on XML or external parsing." }, { "id": "12.17", "title": "UNIQUEIDENTIFIER vs UUID", "page": 12, "keywords": ["UNIQUEIDENTIFIER", "UUID", "GUID", "data type"], "text": "SQL Server’s globally unique identifier type is UNIQUEIDENTIFIER (a 16-byte value, often containing a GUID). PostgreSQL has a native UUID data type for similar values. Both store 128-bit values, but the type names differ . In Postgres, one might declare id UUID DEFAULT gen_random_uuid() (with the uuid-ossp extension) or use CREATE EXTENSION to generate UUIDs, whereas in T-SQL you would use NEWID() with a UNIQUEIDENTIFIER column." }, { "id": "12.18", "title": "Array data types", "page": 12, "keywords": ["ARRAY", "data type", "multi-dimensional", "SQL Server", "PostgreSQL"], "text": "PostgreSQL supports array column types (e.g. integer[], text[]) natively, allowing multi-dimensional arrays in a single column. SQL Server has no native array type. For instance, a column defined as int[] in Postgres can store multiple integers in one field, whereas SQL Server would need a related table or JSON/XML to simulate this ." }, { "id": "12.19", "title": "Default index type (clustered vs B-tree)", "page": 12, "keywords": ["clustered index", "B-tree", "primary key", "SQL Server", "PostgreSQL"], "text": "In SQL Server, a clustered index determines the physical order of rows in a table (the primary key is clustered by default). PostgreSQL does not have a clustered index concept by default; it uses a B-tree index (the default index type) for primary keys but does not order the table physically unless you explicitly CLUSTER it. One source notes that SQL Server’s default index can be clustered, whereas PostgreSQL’s default index is B-tree . Postgres can only cluster a table on an index manually." }, { "id": "12.20", "title": "Partial indexes vs Filtered indexes", "page": 12, "keywords": ["index", "partial index", "filtered index", "WHERE clause", "SQL Server", "PostgreSQL"], "text": "PostgreSQL supports partial indexes (indexes built on a subset of rows) and functional (expression) indexes. SQL Server supports similar functionality via filtered indexes (indexes with a WHERE clause) and indexes on computed columns. For example, Postgres allows CREATE INDEX idx ON table(col) WHERE col IS NOT NULL, whereas SQL Server has CREATE INDEX...WHERE col IS NOT NULL as a filtered index. The two systems have analogous features for indexing subsets of data ." }, { "id": "12.21", "title": "Expression indexes vs computed column indexes", "page": 12, "keywords": ["functional index", "expression index", "computed column", "SQL Server", "PostgreSQL"], "text": "PostgreSQL allows creating indexes on expressions or functions of columns (e.g. CREATE INDEX idx_name ON table (LOWER(name))). SQL Server does not index arbitrary expressions directly but can index computed columns (which are similar to materialized expressions). The difference is mostly syntactic: PostgreSQL’s expression indexes are first-class, while in SQL Server you often add a PERSISTED computed column and index that." }, { "id": "12.22", "title": "Partitioned views vs no built-in partition", "page": 12, "keywords": ["partitioned view", "table partitioning", "SQL Server", "PostgreSQL", "inheritance"], "text": "SQL Server provides partitioned tables and partitioned views (via UNION of tables) for horizontal partitioning. PostgreSQL 9.4 did not have native declarative table partitioning; instead it used table inheritance or inheritance+check constraint setups for manual partitions. In other words, SQL Server’s “partitioned view” feature has no direct equivalent in Postgres 9.4 , where one would manually manage multiple tables for partitioning." }, { "id": "12.23", "title": "NVARCHAR vs TEXT/VARCHAR", "page": 12, "keywords": ["NVARCHAR", "VARCHAR", "TEXT", "Unicode", "character data"], "text": "SQL Server has NVARCHAR (and NCHAR) types for Unicode (UTF-16) strings. PostgreSQL 9.4 does not distinguish NVARCHAR; all VARCHAR/TEXT data is stored in the database encoding (often UTF-8). As one source states, “there is no PostgreSQL equivalent to SQL Server NVARCHAR” . In Postgres you simply use VARCHAR(n) or TEXT (with collation) and it handles Unicode transparently." }, { "id": "12.24", "title": "TOP vs LIMIT/OFFSET syntax", "page": 12, "keywords": ["TOP", "LIMIT", "OFFSET", "pagination", "SQL syntax"], "text": "SQL Server uses SELECT TOP n ... to limit the number of returned rows. PostgreSQL uses LIMIT n (optionally with OFFSET m). Unlike TOP, LIMIT supports an offset to skip rows. For example, SELECT TOP 5 ... returns the first 5 rows in T-SQL; in PostgreSQL you write SELECT ... LIMIT 5. You can also say LIMIT 5 OFFSET 10 to skip the first 10 rows . T-SQL’s TOP cannot specify an offset directly." }, { "id": "12.25", "title": "DELETE TOP vs LIMIT in DELETE", "page": 12, "keywords": ["DELETE", "TOP", "LIMIT", "T-SQL", "PostgreSQL"], "text": "T-SQL allows DELETE TOP(n) to delete a specific number (or percentage) of rows, e.g. DELETE TOP(10) FROM table. PostgreSQL does not support DELETE TOP; to limit deletions you can use subqueries or specify conditions. In practice, one would use a subquery with LIMIT or use DELETE ... USING (SELECT ...) patterns. For example, DELETE FROM mytable WHERE id IN (SELECT id FROM mytable LIMIT 10). The lack of TOP-style syntax means PG requires a different approach ." }, { "id": "12.26", "title": "OUTPUT clause vs RETURNING clause", "page": 12, "keywords": ["OUTPUT", "RETURNING", "INSERT", "UPDATE", "DELETE", "SQL Server", "PostgreSQL"], "text": "SQL Server provides an OUTPUT clause on DML (INSERT/UPDATE/DELETE) to return affected rows, e.g., INSERT INTO T (...) OUTPUT INSERTED.*. PostgreSQL has a RETURNING clause for a similar purpose: after an INSERT/UPDATE/DELETE you can write RETURNING column_list to return values. For example, INSERT INTO table (col) VALUES (1) RETURNING id returns the new id. Unlike SQL Server’s OUTPUT, the RETURNING syntax is part of the standard in PostgreSQL ." }, { "id": "12.27", "title": "SELECT INTO vs CREATE TABLE AS", "page": 12, "keywords": ["SELECT INTO", "CREATE TABLE AS", "CTAS", "T-SQL", "PostgreSQL"], "text": "In T-SQL, SELECT ... INTO new_table FROM ... creates and populates a new table (as a copy of a query). PostgreSQL’s syntax differs: one uses CREATE TABLE new_table AS SELECT .... (Postgres also historically had a SELECT INTO for table creation, but the recommended modern syntax is CREATE TABLE AS .) Thus, SELECT * INTO temp_table FROM ... in SQL Server is written as CREATE TABLE temp_table AS SELECT * FROM ... in Postgres." }, { "id": "12.28", "title": "CTE optimization (materialization)", "page": 12, "keywords": ["CTE", "WITH", "materialized", "optimization fence", "PostgreSQL", "SQL Server"], "text": "PostgreSQL (prior to version 12) treats each non-recursive CTE as an optimization fence (materializing it before continuing), which can make queries slower. SQL Server’s CTEs do not force materialization and can be optimized like inline views. As pointed out, “for other DBs like Postgres, a CTE is often much slower than equivalent subqueries” . In short, T-SQL can often inline a CTE, whereas PG9.4 by default evaluates the entire CTE first." }, { "id": "12.29", "title": "Recursive CTE syntax", "page": 12, "keywords": ["WITH RECURSIVE", "CTE", "recursion", "SQL syntax"], "text": "In PostgreSQL, recursive Common Table Expressions must be declared with the WITH RECURSIVE keyword. SQL Server does not use the RECURSIVE keyword; it automatically assumes recursion if the CTE references itself. So a recursive CTE is written in PG as WITH RECURSIVE cte AS (...) , whereas in T-SQL one would write WITH cte AS (...) (omitting RECURSIVE)." }, { "id": "12.30", "title": "Cursor types and options", "page": 12, "keywords": ["CURSOR", "STATIC", "DYNAMIC", "FORWARD_ONLY", "SQL Server", "PostgreSQL"], "text": "SQL Server’s DECLARE CURSOR supports various options (FORWARD_ONLY, STATIC, KEYSET, DYNAMIC, FAST_FORWARD, etc.) to control scrollability and locking. PostgreSQL’s cursors (declared in PL/pgSQL) use a simpler model: by default PostgreSQL cursors are scrollable (like DYNAMIC) and many T-SQL cursor options don’t apply. For instance, T-SQL’s STATIC cursor (snapshot of data) isn’t available in PG; the closest is to copy results into a temp table. In summary, PG cursors lack most T-SQL cursor features." }, { "id": "12.31", "title": "Trigger timing: BEFORE/AFTER/INSTEAD OF", "page": 12, "keywords": ["trigger", "AFTER", "BEFORE", "INSTEAD OF", "SQL Server", "PostgreSQL"], "text": "SQL Server supports only AFTER and INSTEAD OF triggers (for tables and views; no BEFORE triggers). PostgreSQL supports BEFORE and AFTER triggers on tables (and INSTEAD OF on views only) . In Postgres, one can create CREATE TRIGGER ... BEFORE INSERT to modify data before insertion; SQL Server has no BEFORE trigger. Additionally, SQL Server triggers are inherently statement-level, while PostgreSQL allows specifying FOR EACH ROW or FOR EACH STATEMENT." }, { "id": "12.32", "title": "Trigger row data access (NEW/OLD vs inserted/deleted)", "page": 12, "keywords": ["trigger", "NEW", "OLD", "inserted", "deleted", "pseudo-tables"], "text": "In PostgreSQL, row-level trigger functions use the NEW and OLD record variables to access new and old column values. In SQL Server, triggers use the INSERTED and DELETED pseudo-tables to access changed rows. For example, in a Postgres trigger one writes NEW.col := upper(NEW.col);, whereas in T-SQL one might SELECT col INTO @x FROM INSERTED inside the trigger. The mechanisms differ, but both provide access to the row’s before-and-after values." }, { "id": "12.33", "title": "Roles vs logins/users", "page": 12, "keywords": ["role", "login", "user", "security", "schema"], "text": "SQL Server distinguishes server-level logins, database users, and roles; a Windows login can be mapped to a database user and then granted roles. PostgreSQL 9.4 has a unified role system: there is no separate concept of a login vs user. In Postgres, any role with the LOGIN attribute can connect. As one answer notes, “PostgreSQL has only ROLE; no separate LOGIN and USER” . Database roles are granted directly to other roles in Postgres." }, { "id": "12.34", "title": "APPLY vs LATERAL joins", "page": 12, "keywords": ["CROSS APPLY", "OUTER APPLY", "LATERAL", "table-valued function"], "text": "SQL Server offers CROSS APPLY and OUTER APPLY to join each row to a table-valued function or subquery. PostgreSQL uses the LATERAL keyword for this purpose. Specifically, INNER JOIN LATERAL is equivalent to CROSS APPLY, and LEFT JOIN LATERAL is like OUTER APPLY . For instance, SELECT ... FROM Table1 CROSS APPLY fn(x) AS f in T-SQL corresponds to SELECT ... FROM Table1 INNER JOIN LATERAL fn(x) AS f in Postgres." }, { "id": "12.35", "title": "MERGE (UPSERT) support", "page": 12, "keywords": ["MERGE", "UPSERT", "INSERT ... ON CONFLICT", "upsert"], "text": "SQL Server supports the ANSI-standard MERGE statement to perform conditional insert/update (UPSERT) in one command. PostgreSQL 9.4 has no MERGE. Instead, starting in Postgres 9.5 one uses INSERT ... ON CONFLICT DO NOTHING/UPDATE for upserts. In PG9.4, without MERGE or ON CONFLICT, achieving an upsert required workarounds (e.g. locking and manual checks). As documentation notes, “Postgres has no MERGE statement; use INSERT ... ON CONFLICT instead” ." }, { "id": "12.36", "title": "Error handling: TRY/CATCH vs EXCEPTION", "page": 12, "keywords": ["TRY CATCH", "EXCEPTION", "error handling", "T-SQL", "PL/pgSQL"], "text": "SQL Server supports error handling via TRY...CATCH blocks in T-SQL. PostgreSQL uses BEGIN ... EXCEPTION ... END blocks in PL/pgSQL functions for similar control (there is no TRY...CATCH syntax). For example, a PG function can BEGIN ... EXCEPTION WHEN others THEN ... END;. The conceptual difference is that errors are caught by EXCEPTION clauses in Postgres, not by a CATCH keyword as in T-SQL." }, { "id": "12.37", "title": "ISNULL vs COALESCE for NULL values", "page": 12, "keywords": ["ISNULL", "COALESCE", "NULL", "function"], "text": "SQL Server provides the function ISNULL(expr, replacement) to replace NULL values in results. PostgreSQL does not have ISNULL; the standard SQL function COALESCE(expr, replacement) is used instead. For example, SELECT ISNULL(col, 'Empty') in T-SQL is SELECT COALESCE(col, 'Empty') in Postgres . (COALESCE can take multiple arguments, unlike ISNULL.)" }, { "id": "12.38", "title": "NOLOCK and transaction isolation", "page": 12, "keywords": ["NOLOCK", "READ UNCOMMITTED", "MVCC", "isolation", "locking"], "text": "SQL Server allows hints like WITH (NOLOCK) on table queries to read uncommitted (dirty) data. PostgreSQL’s MVCC model means readers do not block writers and vice versa. There is no NOLOCK hint in Postgres. A plain SELECT in Postgres (with default READ COMMITTED) never locks or is blocked by writers . In effect, Postgres readers see a consistent snapshot and don’t need an explicit NOLOCK equivalent." }, { "id": "12.39", "title": "TINYINT vs SMALLINT (and other integers)", "page": 12, "keywords": ["TINYINT", "SMALLINT", "integer", "data type"], "text": "SQL Server supports a one-byte TINYINT type (0–255). PostgreSQL does not have an 8-bit type; its smallest integer is SMALLINT (16-bit). In practice, T-SQL TINYINT maps to Postgres SMALLINT. Both have 2-byte and 4-byte and 8-byte integer types (SMALLINT, INT, BIGINT), but SQL Server’s TINYINT is unique ." }, { "id": "12.40", "title": "String search: CHARINDEX vs POSITION/STRPOS", "page": 12, "keywords": ["CHARINDEX", "POSITION", "STRPOS", "substring"], "text": "T-SQL provides the function CHARINDEX(substring, string) to find a substring’s position. PostgreSQL uses the standard POSITION(substring IN string) or the convenience function strpos(string, substring) . Both return the index of the first occurrence (or 0/NULL if not found), but note the parameter order: CHARINDEX(substr, str) vs POSITION(substr IN str)." }, { "id": "12.41", "title": "Current date/time: GETDATE vs NOW", "page": 12, "keywords": ["GETDATE", "NOW", "CURRENT_TIMESTAMP", "datetime"], "text": "SQL Server uses GETDATE(), SYSDATETIME(), etc. to return the current datetime. PostgreSQL uses now() (or the SQL-standard CURRENT_TIMESTAMP) for the same. There is no built-in getdate() function in Postgres 9.4; one would simply use SELECT now() ." }, { "id": "12.42", "title": "Date/time data types", "page": 12, "keywords": ["DATETIME", "TIMESTAMP", "time zone", "SQL Server", "PostgreSQL"], "text": "SQL Server has DATETIME, SMALLDATETIME, DATETIME2, and DATETIMEOFFSET types. PostgreSQL uses TIMESTAMP WITHOUT TIME ZONE and TIMESTAMP WITH TIME ZONE. For example, DATETIME2 (with precision) in SQL Server corresponds to TIMESTAMP (with precision) in Postgres. Postgres’s TIMESTAMP WITH TIME ZONE is analogous to SQL Server’s DATETIMEOFFSET . SQL Server’s SMALLDATETIME is like TIMESTAMP(0) in PG." }, { "id": "12.43", "title": "Retrieving last insert ID (SCOPE_IDENTITY vs currval/RETURNING)", "page": 12, "keywords": ["SCOPE_IDENTITY", "currval", "lastval", "RETURNING", "sequence"], "text": "In SQL Server, SCOPE_IDENTITY() or @@IDENTITY is used to get the last identity value inserted in the current scope. In PostgreSQL, one can use CURRVAL(pg_get_serial_sequence('table','id')) or simply include a RETURNING id clause in the INSERT to get the new ID . For example, INSERT ... RETURNING id will output the auto-generated id, similar to SELECTing SCOPE_IDENTITY() in T-SQL." }, { "id": "12.44", "title": "Query hints for indexes", "page": 12, "keywords": ["index hint", "WITH INDEX", "optimizer hint"], "text": "SQL Server allows index hints in queries (e.g. WITH (INDEX(ix_name))) to force the use of a particular index. PostgreSQL does not support index hints by design . Instead, Postgres relies on its query planner to choose indexes; you cannot force an index in standard SQL (aside from changing planner settings globally). This means PG queries lack the FORCE INDEX or INDEX hints available in T-SQL." }, { "id": "12.45", "title": "Full-text search", "page": 12, "keywords": ["full-text search", "tsvector", "freetext", "CONTAINS"], "text": "PostgreSQL has built-in full-text search: text data can be converted to tsvector and indexed for fast full-text queries. SQL Server also has full-text search features (with CONTAINS/FREETEXT) but as an optional component and uses full-text catalogs. The point is Postgres’s full-text capabilities are core to the engine, whereas SQL Server’s are separately enabled. As a comparison article notes, SQL Server’s full-text is an optional feature, while PostgreSQL offers advanced full-text indexing ." }, { "id": "12.46", "title": "Regular expression support", "page": 12, "keywords": ["regex", "LIKE", "SIMILAR TO", "T-SQL", "PostgreSQL"], "text": "PostgreSQL supports full POSIX regular expressions in queries using operators like ~, ~*, and the SIMILAR TO syntax . SQL Server does not have native regex operators; it relies on LIKE or PATINDEX for simple patterns. Thus a Postgres query can do WHERE text ~ 'pattern'; in T-SQL one would use WHERE column LIKE '%pattern%' or CLR regex." }, { "id": "12.47", "title": "Table partitioning", "page": 12, "keywords": ["table partitioning", "horizontal partition", "inheritance", "partitioning"], "text": "SQL Server supports horizontal table partitioning (via partition functions and schemes) and allows all partitions to be in one table. PostgreSQL 9.4 did not have true declarative partitioning: one had to use table inheritance or separate tables. In fact, in PG9.4 each partition is a separate child table with a constraint. As one source remarks, Postgres did not support horizontal table partitioning natively , requiring third-party tools or manual setups, whereas SQL Server’s partitioning is built-in." }, { "id": "12.48", "title": "Temporary tables syntax", "page": 12, "keywords": ["temporary table", "#table", "CREATE TEMP TABLE", "global temp table"], "text": "In SQL Server, local temporary tables are named with a single # prefix (e.g. #tempTable), and global temp tables with ##. In PostgreSQL, you create a temporary table with CREATE TEMP TABLE temp_table, and there is no # syntax. PostgreSQL temporary tables exist only for the session (no global temp tables by name). For example, CREATE TEMP TABLE tmp AS SELECT... in Postgres vs SELECT ... INTO #tmp in T-SQL." }, { "id": "12.49", "title": "Materialized views vs indexed views", "page": 12, "keywords": ["materialized view", "indexed view", "VIEW", "data caching"], "text": "PostgreSQL supports CREATE MATERIALIZED VIEW (introduced in PG 9.3) which stores query results. SQL Server does not call these 'materialized views' but has 'indexed views', where a view with a clustered index persists data. Functionally they serve similar purposes (precomputed results), but the syntax differs. In PG you write REFRESH MATERIALIZED VIEW to update it, whereas in SQL Server you update an indexed view by updating the base tables (the index keeps it current)." }, { "id": "12.50", "title": "Bulk data loading", "page": 12, "keywords": ["BULK INSERT", "COPY", "load data", "high-volume"], "text": "SQL Server offers BULK INSERT and the bcp utility for fast bulk loading of data. PostgreSQL provides the COPY command to efficiently load or unload large amounts of data from files. For example, COPY mytable FROM '/path/to/file.csv' WITH CSV in Postgres is analogous to BULK INSERT mytable FROM 'file.csv' in T-SQL. Both serve to quickly import/export data, but use different syntax and features." }, { "id": "12.51", "title": "String concatenation operators", "page": 12, "keywords": ["string", "concatenation", "||", "+"], "text": "T-SQL concatenates strings using the + operator (or the CONCAT() function). PostgreSQL uses the SQL-standard || operator for string concatenation. For example, in T-SQL 'Hello ' + 'World' yields 'Hello World', whereas in Postgres one writes 'Hello ' || 'World' to concatenate." }, { "id": "12.52", "title": "Quoted identifiers", "page": 12, "keywords": ["identifier", "quoting", "brackets","case"], "text": "SQL Server allows quoting identifiers with square brackets (e.g. [TableName]) or double quotes. PostgreSQL uses double quotes only (e.g. \"TableName\"). PostgreSQL does not support square bracket quoting. Unquoted identifiers in PostgreSQL are folded to lower-case, whereas in SQL Server unquoted identifiers are case-insensitive (effectively folded to uppercase or database default collation)." }, { "id": "12.53", "title": "Case sensitivity of identifiers", "page": 12, "keywords": ["case sensitivity", "collation", "identifiers", "SQL Server", "PostgreSQL"], "text": "By default, SQL Server identifiers and string comparisons are case-insensitive under most collations (unless a case-sensitive collation is chosen). PostgreSQL treats unquoted identifiers as case-insensitive by folding to lower-case, but quoted identifiers become case-sensitive. Additionally, PostgreSQL string comparisons are case-sensitive by default. This can lead to behavior differences: e.g. a column named Foo can be referenced as foo in SQL Server, but in Postgres \"Foo\" must be used if case is important." }, { "id": "12.54", "title": "Table-valued parameters", "page": 12, "keywords": ["table-valued parameter", "TVP", "user-defined table type"], "text": "SQL Server supports table-valued parameters: a stored procedure can accept an input parameter of a user-defined table type, allowing passing multiple rows as a parameter. PostgreSQL 9.4 has no direct equivalent. To pass sets of rows to a function in Postgres, one typically uses temporary tables or array/composite types, since Postgres does not have a built-in table-parameter type." }, { "id": "12.55", "title": "PIVOT/UNPIVOT vs crosstab", "page": 12, "keywords": ["PIVOT", "UNPIVOT", "crosstab", "tablefunc", "data reshape"], "text": "SQL Server provides PIVOT and UNPIVOT operators to rotate rows into columns and vice versa. PostgreSQL does not have these keywords, but similar functionality can be achieved using the crosstab() function from the tablefunc extension or with aggregate filters. In other words, a PIVOT query in SQL Server must be manually implemented in Postgres using CASE expressions or crosstab()." }, { "id": "12.56", "title": "Transaction isolation levels", "page": 12, "keywords": ["READ UNCOMMITTED", "READ COMMITTED", "MVCC", "isolation", "snapshot"], "text": "SQL Server supports a READ UNCOMMITTED isolation level (dirty reads) and a READ COMMITTED level optionally using row versioning. PostgreSQL’s lowest isolation level is READ COMMITTED, which in effect never allows dirty reads (MVCC prevents reading uncommitted changes). PostgreSQL does not offer READ UNCOMMITTED; READ UNCOMMITTED in PG is treated as READ COMMITTED. Thus Postgres defaults to MVCC snapshot reads even at its lowest level." }, { "id": "12.57", "title": "Default schema names", "page": 12, "keywords": ["schema", "dbo", "public", "namespace"], "text": "In SQL Server, the default schema for new objects (when none is specified) is usually dbo. In PostgreSQL, the default schema is public. For example, CREATE TABLE mytable(...) in SQL Server creates dbo.mytable, whereas in Postgres it creates public.mytable (unless another schema search_path is set)." }, { "id": "12.58", "title": "Procedural languages (CLR vs PL/pgSQL)", "page": 12, "keywords": ["CLR", ".NET", "PL/pgSQL", "PL/Python", "extensions"], "text": "SQL Server supports writing stored procedures and functions in .NET languages via the CLR integration. PostgreSQL does not support CLR, but it supports multiple procedural languages (PL/pgSQL by default, plus PL/Python, PL/Perl, etc.). Thus, while T-SQL code always runs in the SQL engine (or CLR), Postgres functions can be written in various languages, offering more flexibility but different architecture." }, { "id": "12.59", "title": "Synonyms and alias objects", "page": 12, "keywords": ["synonym", "object alias", "link server"], "text": "SQL Server allows creating synonyms as alternate names for database objects (with CREATE SYNONYM). PostgreSQL has no direct synonym feature; one typically uses views or foreign tables for similar effects. For example, CREATE SYNONYM MyTable FOR OtherDB.dbo.Table in T-SQL has no exact counterpart in Postgres 9.4." }, { "id": "12.60", "title": "Computed columns vs generated columns", "page": 12, "keywords": ["computed column", "generated column", "virtual column"], "text": "SQL Server allows computed columns (e.g. ALTER TABLE ADD Col AS (Col1+Col2)). PostgreSQL 9.4 did not have generated columns (added in PG 12). To mimic computed columns, Postgres would use an expression index or a view. Thus T-SQL’s computed column feature has no direct counterpart in PG9.4." } ]