PostgreSQL에서 SQL Server로 변환
pg_dump나 pg_dumpall로 만든 .sql 또는 .sql.gz 일반 덤프를 놓으면, 같은 테이블, 키, 인덱스, 데이터를 담은 SQL Server 스크립트를 다운로드할 수 있어요. SQL Server이(가) 그대로 담을 수 없는 값과 옮기지 않는 객체는 모두 보고서에 나와요.
18개 행 변환, 수정 2개, 거부 없음
shop_2024_postgres.sql의 테이블PostgreSQL 18.6 덤프 · pg_dump(으)로 작성됨 · utf8mb4
customers8개 행1
8개 열 id int not null name nvarchar(max) not null email nvarchar(450) not null country nvarchar(2) not null is_active bit not null tags nvarchar(max) not null signed_up date created_at datetimeoffset(6) not null orders10개 행1
10개 열 id int not null customer_id int not null status nvarchar(9) not null total decimal(10,2) not null items smallint not null ordered_at datetime2(6) not null shipped_at datetime2(6) note nvarchar(max) details nvarchar(max) delivery nvarchar(100)
변환되지 않음
- 뷰 paid_orders
데이터베이스마다 이 객체를 자체 언어로 작성해요. SQL Server에 맞게 다시 작성하세요.
-- Written by IntoSQL from a PostgreSQL dump. Load it with: sqlcmd -b -f 65001 -d <database> -i <file>
SET NOCOUNT ON;
SET ANSI_NULLS ON;
SET QUOTED_IDENTIFIER ON;
SET ANSI_PADDING ON;
SET ANSI_WARNINGS ON;
SET ARITHABORT ON;
SET CONCAT_NULL_YIELDS_NULL ON;
SET NUMERIC_ROUNDABORT OFF;
GO
CREATE TABLE [dbo].[customers] (
[id] int IDENTITY(1001, 1) NOT NULL,
[name] nvarchar(max) COLLATE Latin1_General_100_BIN2 NOT NULL,
[email] nvarchar(450) COLLATE Latin1_General_100_BIN2 NOT NULL,
[country] nvarchar(2) COLLATE Latin1_General_100_BIN2 DEFAULT N'US' NOT NULL,
[is_active] bit DEFAULT 1 NOT NULL,
[tags] nvarchar(max) COLLATE Latin1_General_100_BIN2 NOT NULL,
[signed_up] date NULL,
[created_at] datetimeoffset(6) DEFAULT SYSDATETIMEOFFSET() NOT NULL,
CONSTRAINT [PK_customers] PRIMARY KEY ([id])
);
GO
CREATE TABLE [dbo].[orders] (
[id] int IDENTITY(5001, 1) NOT NULL,
[customer_id] int NOT NULL,
[status] nvarchar(9) COLLATE Latin1_General_100_BIN2 DEFAULT N'pending' NOT NULL,
[total] decimal(10,2) NOT NULL,
[items] smallint DEFAULT 1 NOT NULL,
[ordered_at] datetime2(6) NOT NULL,
[shipped_at] datetime2(6) NULL,
[note] nvarchar(max) COLLATE Latin1_General_100_BIN2 NULL,
[details] nvarchar(max) COLLATE Latin1_General_100_BIN2 NULL,
[delivery] nvarchar(100) COLLATE Latin1_General_100_BIN2 NULL,
CONSTRAINT [CK_orders_status] CHECK ([status] IN (N'pending', N'paid', N'shipped', N'cancelled')),
CONSTRAINT [orders_total_check] CHECK ([total] >= (0)),
CONSTRAINT [PK_orders] PRIMARY KEY ([id])
);
GO
SET IDENTITY_INSERT [dbo].[customers] ON;
INSERT INTO [dbo].[customers] ([id], [name], [email], [country], [is_active], [tags], [signed_up], [created_at]) VALUES
(1,N'Ana Martínez',N'ana@example.com',N'MX',1,N'["vip"]','2023-01-15','2023-01-15 10:24:00+00:00'),
(2,N'Zoë Müller',N'zoe@example.de',N'DE',1,N'[]','2023-02-03','2023-02-03 08:11:42+00:00'),
(3,N'José Peña',N'jose@example.es',N'ES',0,N'["wholesale","late payer"]','2023-03-21','2023-03-21 19:05:17+00:00'),
(4,N'李雷',N'lilei@example.cn',N'CN',1,N'[]','2023-05-09','2023-05-09 02:40:00+00:00'),
(5,N'Noah O''Brien',N'noah@example.ie',N'IE',1,N'[]',NULL,'2023-06-30 14:00:00+00:00'),
(6,N'Amélie Laurent',N'amelie@example.fr',N'FR',1,N'["vip"]','2024-01-02','2024-01-02 09:30:00+00:00'),
(7,N'Ravi Patel',N'ravi@example.in',N'IN',0,N'[]','2024-02-29','2024-02-29 23:59:59+00:00'),
(8,N'Sofia Rossi 🌻',N'sofia@example.it',N'IT',1,N'[]','2024-04-12','2024-04-12 12:12:12+00:00');
SET IDENTITY_INSERT [dbo].[customers] OFF;
GO
SET IDENTITY_INSERT [dbo].[orders] ON;
INSERT INTO [dbo].[orders] ([id], [customer_id], [status], [total], [items], [ordered_at], [shipped_at], [note], [details], [delivery]) VALUES
(1,1,N'shipped',129.90,3,'2024-03-01 10:00:00','2024-03-02 16:30:00',NULL,N'{"gift": false}',N'1 day 06:30:00'),
(2,2,N'paid',54.00,1,'2024-03-02 11:15:00',NULL,N'Gift wrap 🎁',N'{"gift": true, "wrap": "red"}',NULL),
(3,1,N'cancelled',12.50,1,'2024-03-03 09:45:00',NULL,N'Customer changed their mind',NULL,NULL),
(4,4,N'shipped',980.00,12,'2024-03-05 02:10:00','2024-03-07 08:00:00',N'请小心轻放',N'{"fragile": true}',N'2 days 05:50:00'),
(5,5,N'pending',45.75,2,'2024-03-08 17:20:00',NULL,N'Leave at the door; ring twice',NULL,NULL),
(6,6,N'shipped',310.40,5,'2024-03-10 13:00:00','2024-03-11 09:00:00',N'Fragile: "glass"',N'{"fragile": true}',N'20:00:00'),
(7,8,N'paid',76.20,2,'2024-03-11 18:45:00',NULL,NULL,NULL,NULL),
(8,2,N'shipped',19.99,1,'2024-03-12 08:00:00',NULL,N'Shipped date set to infinity by an old import',NULL,NULL),
(9,3,N'shipped',64.00,2,'2024-03-14 15:30:00','2024-03-15 10:00:00',N'Two parcels',N'{"parcels": [1, 2]}',N'18:30:00'),
(10,7,N'paid',1499.00,1,'2024-03-15 21:05:00',NULL,N'C:\Users\ravi\invoice.pdf',NULL,NULL);
SET IDENTITY_INSERT [dbo].[orders] OFF;
GO
-- Indexes, foreign keys, counters and triggers, after the rows.
CREATE UNIQUE INDEX [customers_email_key] ON [dbo].[customers] ([email]);
CREATE INDEX [idx_orders_customer] ON [dbo].[orders] ([customer_id]);
CREATE INDEX [idx_orders_status_date] ON [dbo].[orders] ([status], [ordered_at]);
ALTER TABLE [dbo].[orders] WITH NOCHECK ADD CONSTRAINT [orders_customer_id_fkey] FOREIGN KEY ([customer_id]) REFERENCES [dbo].[customers] ([id]);
BEGIN TRY ALTER TABLE [dbo].[orders] WITH CHECK CHECK CONSTRAINT [orders_customer_id_fkey]; END TRY BEGIN CATCH PRINT N'Foreign key ' + N'[orders_customer_id_fkey]' + N' has rows without a parent: it stays unchecked, and new rows are checked.'; END CATCH;
DECLARE @next decimal(38,0) = (SELECT MAX([id]) FROM [dbo].[customers]); IF @next IS NOT NULL BEGIN IF @next < 1000 SET @next = 1000; DBCC CHECKIDENT (N'[dbo].[customers]', RESEED, @next) WITH NO_INFOMSGS; END;
GO
EXEC sys.sp_addextendedproperty @name = N'MS_Description', @value = N'People who bought at least once', @level0type = N'SCHEMA', @level0name = N'dbo', @level1type = N'TABLE', @level1name = N'customers';
DECLARE @next decimal(38,0) = (SELECT MAX([id]) FROM [dbo].[orders]); IF @next IS NOT NULL BEGIN IF @next < 5000 SET @next = 5000; DBCC CHECKIDENT (N'[dbo].[orders]', RESEED, @next) WITH NO_INFOMSGS; END;
GO
GO
수정 사항
- customers의 "tags" 열은 SQL Server에 없는 text[](이)라서 같은 값을 담은 nvarchar(max)(으)로 만들었어요.
- orders의 "delivery" 열은 SQL Server에 없는 interval(이)라서 같은 값을 담은 nvarchar(100)(으)로 만들었어요.
확인 필요
- 뷰, 트리거, 루틴, 이벤트 1개가 변환되지 않았어요. 보고서에 각각 나와 있어요. SQL Server에서 직접 다시 만드세요.
- customers의 "tags" 열 기본값을 SQL Server용으로 쓸 수 없어(a value its type can't hold) 기본값 없이 만들었어요. 새 행에 필요하면 기본값을 직접 추가하세요.
- orders의 인덱스 idx_orders_details은(는) SQL Server용으로 쓸 수 없는 식을 사용해서(an index on an expression) 만들지 않았어요. 쿼리에 필요하면 인덱스를 직접 만드세요.
- orders의 "shipped_at" 열에 있는 1개 값은 infinity처럼 SQL Server이(가) 저장할 수 없는 날짜라서 NULL로 두었어요. SQL Server은(는) 1년부터 9999년까지만 저장하고 infinity는 없어요. 이 날짜가 중요하면 원본에서 바꾸세요.
파일은 이 컴퓨터에서 변환되고 업로드되지 않아요.
다른 샘플:
PostgreSQL에서 SQL Server로 바뀌는 점
각 행은 변환기가 적용하는 규칙이며, 규칙이 데이터를 바꿀 때 보고서에 쓰는 줄이 함께 나와요.
boolean
bit
bit는 true를 1, false를 0으로 담아요.
varchar(n), char(n), text
nvarchar(n), text는 nvarchar(max), 키에 쓰이고 모든 값이 들어가면 nvarchar(450)
nvarchar는 모든 문자를 담아요. SQL Server는 nvarchar(max)를 인덱싱할 수 없어서 키에 있는 텍스트는 키에 맞는 크기로 정해요.
text comparison
정확히 비교(Latin1_General_100_BIN2), citext는 대소문자 무시(Latin1_General_100_CI_AS_SC)
SQL Server의 일반적인 콜레이션은 대소문자를 무시하지만 PostgreSQL의 텍스트는 그렇지 않아요. 그래서 열마다 비교 방식을 지정하고, 고유 키는 같은 행들을 구분해요.
numeric(p,s) past 38 digits; numeric without a precision
nvarchar, 값이 38자리에 들어가면 값에 맞춘 decimal(p,s)
SQL Server의 decimal은 38자리까지 담아요. 정밀도가 없는 numeric은 값이 들어가는 가장 좁은 decimal이 되고, 더 넓은 숫자는 모든 자릿수가 정확하도록 텍스트로 유지해요.
보고서 줄: decimal_as_text
money
decimal(19,2)
SQL Server의 money는 PostgreSQL보다 작고 소수 4자리예요. decimal(19,2)는 서버 로캘이 붙인 통화 기호 없이 모든 금액을 담아요.
보고서 줄: type_replaced
interval
PostgreSQL의 텍스트를 담은 nvarchar(100)
SQL Server에는 interval 타입이 없어요. 텍스트가 모든 부분을 유지해요.
보고서 줄: type_replaced
arrays (integer[], text[]...)
JSON 배열을 담은 nvarchar(max)
SQL Server에는 배열이 없어요. JSON 배열은 모든 항목과 NULL을 유지하고 OPENJSON으로 읽을 수 있어요.
보고서 줄: type_replaced
ranges, geometric types, tsvector, xml, hstore, PostGIS, composite and extension types
PostgreSQL의 텍스트를 담은 nvarchar(max)
SQL Server에는 같은 타입이 없어서 각 값은 PostgreSQL이 쓴 텍스트를 유지해요.
보고서 줄: type_replaced
json, jsonb
nvarchar(max)
SQL Server의 JSON 함수는 텍스트에서 JSON을 읽어요.
timestamp
datetime2
timestamp without time zone은 datetime2처럼 시계에 표시된 시각이에요.
timestamp with time zone
UTC 기준 datetimeoffset
datetimeoffset은 시점을 유지해요. 각 값은 UTC로 쓰며, 덤프에 적힌 오프셋과 상관없이 같은 시점이에요.
time with 24:00:00 in it
nvarchar(17)
SQL Server의 time은 23:59:59.9999999에서 끝나서 24:00:00이 있는 열은 텍스트를 유지해요.
보고서 줄: time_as_interval
infinity, -infinity and BC dates
NULL
SQL Server는 1년부터 9999년까지만 저장하고 infinity는 없어요. 이 값은 NULL이 되고 보고서에 개수가 나와요.
보고서 줄: date_out_of_range
NaN and Infinity in numeric, real and double precision
NULL
SQL Server의 숫자에는 NaN이나 Infinity가 없어서 이 값은 NULL이 되고 보고서에 개수가 나와요.
보고서 줄: invalid_value_null
NOT NULL columns with values that become NULL
NOT NULL 제거
값을 NULL로 바꿔야 했던 열은 NOT NULL을 유지할 수 없어요. 유지하면 불러오기가 멈춰요.
보고서 줄: not_null_relaxed
enum types
레이블 CHECK가 붙은 nvarchar
체크 제약이 있는 텍스트는 같은 값을 유지해요.
enum columns holding values outside their labels
체크 제약 없는 nvarchar
레이블에 없는 값은 체크 제약에 걸리므로 체크 제약을 만들지 않아요.
보고서 줄: enum_check_skipped
uuid, inet, cidr, macaddr
uniqueidentifier, 주소는 nvarchar
uniqueidentifier는 SQL Server의 UUID예요. 주소는 텍스트로 유지해요.
bytea
varbinary(max)
바이트는 바이트 그대로 남아요.
bit(n), bit varying
1비트는 bit, 63비트까지는 bigint, 그 이상은 자릿수 텍스트
1비트는 예/아니요이고, 더 긴 비트 문자열은 비트가 만드는 숫자로 저장해요.
oid, xid, cid
음수가 아닌지 확인하는 CHECK가 붙은 bigint
bigint는 모든 값을 담아요.
serial, identity
IDENTITY, 가장 큰 id 다음부터 이어짐
복사된 id는 IDENTITY_INSERT로 불러오고, 카운터는 가장 큰 id와 시퀀스의 다음 값 중 더 큰 값 다음부터 이어져요.
a second serial or identity column in a table
일반 정수
SQL Server는 테이블마다 IDENTITY 열 하나만 번호를 매겨요. 다른 열은 카운터 없이 값을 유지해요.
보고서 줄: counter_dropped
sequences not owned by a column
다음 값을 포함한 CREATE SEQUENCE ... AS bigint
SQL Server에도 같은 옵션의 시퀀스가 있어요. 각 시퀀스는 덤프가 멈춘 곳부터 이어져요.
generated columns
계산 열, 원본에서 저장되면 PERSISTED
SQL Server도 같은 두 가지 방식으로 계산해요.
generated columns with functions SQL Server lacks
값을 담은 일반 열
SQL Server용으로 쓸 수 없는 수식은 덤프의 값을 담은 일반 열로 남고 보고서에 한 줄 기록돼요.
보고서 줄: generated_not_translated
defaults with functions SQL Server lacks, and defaults from a sequence
기본값 없음
SQL Server용으로 쓸 수 없는 기본값은 제외하고 보고해요.
보고서 줄: default_not_translated
CHECK constraints
변환해서 CREATE TABLE 안에 인라인으로
인라인 체크 제약은 각 행을 불러올 때 검사해요.
CHECK constraints with functions SQL Server lacks
만들지 않음
SQL Server용으로 쓸 수 없는 체크 제약은 제외하고 보고해요.
보고서 줄: check_skipped
CHECK ... NOT VALID
만들지 않음
PostgreSQL은 기존 행을 검사한 적이 없어서 그 행이 제약을 어길 수 있어요.
보고서 줄: check_not_valid
gin, gist, brin and hash indexes, partial and expression indexes, and indexes on text that doesn't fit a key
만들지 않음
SQL Server는 식이 아닌 열을 인덱싱하고, nvarchar(max)는 인덱싱하지 않아요.
보고서 줄: index_skipped
foreign keys
데이터 뒤에 WITH NOCHECK로 만들고, 고아 행은 경고와 함께 검사
키는 행 다음에 만들고 마지막에 검사해요. 부모 행이 없는 행이 있으면 불러오기를 멈추지 않고 경고와 함께 검사하지 않은 상태로 남겨요.
ON DELETE or ON UPDATE that cascades in a cycle or along a second path
NO ACTION
SQL Server는 한 테이블에 두 번 닿을 수 있는 연쇄 동작을 거부하지만 PostgreSQL은 허용해요.
보고서 줄: foreign_key_action_dropped
foreign keys to tables the dump doesn't create
만들지 않음
키는 없는 테이블을 가리킬 수 없어요.
보고서 줄: foreign_key_skipped
partitioned tables
모든 파티션의 행을 담은 하나의 테이블
행은 같아요. 필요하면 SQL Server에서 테이블을 다시 파티션으로 나누세요.
보고서 줄: partitions_dropped
views, functions, procedures, triggers, rules, policies, extensions and types
보고서에 나열
코드가 PostgreSQL의 언어로 작성되어 있어서 직접 다시 만들 수 있도록 나열해요.
보고서 줄: not_converted_objects
dumps without CREATE TABLE (--data-only)
값으로 판단한 타입
데이터만 있는 덤프에는 행만 있어서 타입을 값으로 판단해요.
보고서 줄: no_schema
expressions in INSERT values
NULL
데이터베이스를 실행하지 않는 변환기는 이 식을 계산할 수 없어요.
보고서 줄: expression_value
types IntoSQL doesn't know
nvarchar(max)
텍스트는 어떤 값이든 담아요. 불러온 뒤 타입을 바꾸세요.
보고서 줄: unknown_type
text that isn't valid in its encoding
잘못된 바이트를 대체 문자로
nvarchar는 Unicode 텍스트를 담아요.
보고서 줄: invalid_utf8_replaced
text decoded twice (ñ)
그대로 복사
덤프에 정확히 이 문자들이 있으므로 그대로 복사하고, 보고서에서 알려 줘요.
보고서 줄: mojibake_suspected
| PostgreSQL에서 | SQL Server에서 | 보고서 줄 |
|---|---|---|
| boolean | bitbit는 true를 1, false를 0으로 담아요. | |
| varchar(n), char(n), text | nvarchar(n), text는 nvarchar(max), 키에 쓰이고 모든 값이 들어가면 nvarchar(450)nvarchar는 모든 문자를 담아요. SQL Server는 nvarchar(max)를 인덱싱할 수 없어서 키에 있는 텍스트는 키에 맞는 크기로 정해요. | |
| text comparison | 정확히 비교(Latin1_General_100_BIN2), citext는 대소문자 무시(Latin1_General_100_CI_AS_SC)SQL Server의 일반적인 콜레이션은 대소문자를 무시하지만 PostgreSQL의 텍스트는 그렇지 않아요. 그래서 열마다 비교 방식을 지정하고, 고유 키는 같은 행들을 구분해요. | |
| numeric(p,s) past 38 digits; numeric without a precision | nvarchar, 값이 38자리에 들어가면 값에 맞춘 decimal(p,s)SQL Server의 decimal은 38자리까지 담아요. 정밀도가 없는 numeric은 값이 들어가는 가장 좁은 decimal이 되고, 더 넓은 숫자는 모든 자릿수가 정확하도록 텍스트로 유지해요. | decimal_as_text |
| money | decimal(19,2)SQL Server의 money는 PostgreSQL보다 작고 소수 4자리예요. decimal(19,2)는 서버 로캘이 붙인 통화 기호 없이 모든 금액을 담아요. | type_replaced |
| interval | PostgreSQL의 텍스트를 담은 nvarchar(100)SQL Server에는 interval 타입이 없어요. 텍스트가 모든 부분을 유지해요. | type_replaced |
| arrays (integer[], text[]...) | JSON 배열을 담은 nvarchar(max)SQL Server에는 배열이 없어요. JSON 배열은 모든 항목과 NULL을 유지하고 OPENJSON으로 읽을 수 있어요. | type_replaced |
| ranges, geometric types, tsvector, xml, hstore, PostGIS, composite and extension types | PostgreSQL의 텍스트를 담은 nvarchar(max)SQL Server에는 같은 타입이 없어서 각 값은 PostgreSQL이 쓴 텍스트를 유지해요. | type_replaced |
| json, jsonb | nvarchar(max)SQL Server의 JSON 함수는 텍스트에서 JSON을 읽어요. | |
| timestamp | datetime2timestamp without time zone은 datetime2처럼 시계에 표시된 시각이에요. | |
| timestamp with time zone | UTC 기준 datetimeoffsetdatetimeoffset은 시점을 유지해요. 각 값은 UTC로 쓰며, 덤프에 적힌 오프셋과 상관없이 같은 시점이에요. | |
| time with 24:00:00 in it | nvarchar(17)SQL Server의 time은 23:59:59.9999999에서 끝나서 24:00:00이 있는 열은 텍스트를 유지해요. | time_as_interval |
| infinity, -infinity and BC dates | NULLSQL Server는 1년부터 9999년까지만 저장하고 infinity는 없어요. 이 값은 NULL이 되고 보고서에 개수가 나와요. | date_out_of_range |
| NaN and Infinity in numeric, real and double precision | NULLSQL Server의 숫자에는 NaN이나 Infinity가 없어서 이 값은 NULL이 되고 보고서에 개수가 나와요. | invalid_value_null |
| NOT NULL columns with values that become NULL | NOT NULL 제거값을 NULL로 바꿔야 했던 열은 NOT NULL을 유지할 수 없어요. 유지하면 불러오기가 멈춰요. | not_null_relaxed |
| enum types | 레이블 CHECK가 붙은 nvarchar체크 제약이 있는 텍스트는 같은 값을 유지해요. | |
| enum columns holding values outside their labels | 체크 제약 없는 nvarchar레이블에 없는 값은 체크 제약에 걸리므로 체크 제약을 만들지 않아요. | enum_check_skipped |
| uuid, inet, cidr, macaddr | uniqueidentifier, 주소는 nvarcharuniqueidentifier는 SQL Server의 UUID예요. 주소는 텍스트로 유지해요. | |
| bytea | varbinary(max)바이트는 바이트 그대로 남아요. | |
| bit(n), bit varying | 1비트는 bit, 63비트까지는 bigint, 그 이상은 자릿수 텍스트1비트는 예/아니요이고, 더 긴 비트 문자열은 비트가 만드는 숫자로 저장해요. | |
| oid, xid, cid | 음수가 아닌지 확인하는 CHECK가 붙은 bigintbigint는 모든 값을 담아요. | |
| serial, identity | IDENTITY, 가장 큰 id 다음부터 이어짐복사된 id는 IDENTITY_INSERT로 불러오고, 카운터는 가장 큰 id와 시퀀스의 다음 값 중 더 큰 값 다음부터 이어져요. | |
| a second serial or identity column in a table | 일반 정수SQL Server는 테이블마다 IDENTITY 열 하나만 번호를 매겨요. 다른 열은 카운터 없이 값을 유지해요. | counter_dropped |
| sequences not owned by a column | 다음 값을 포함한 CREATE SEQUENCE ... AS bigintSQL Server에도 같은 옵션의 시퀀스가 있어요. 각 시퀀스는 덤프가 멈춘 곳부터 이어져요. | |
| generated columns | 계산 열, 원본에서 저장되면 PERSISTEDSQL Server도 같은 두 가지 방식으로 계산해요. | |
| generated columns with functions SQL Server lacks | 값을 담은 일반 열SQL Server용으로 쓸 수 없는 수식은 덤프의 값을 담은 일반 열로 남고 보고서에 한 줄 기록돼요. | generated_not_translated |
| defaults with functions SQL Server lacks, and defaults from a sequence | 기본값 없음SQL Server용으로 쓸 수 없는 기본값은 제외하고 보고해요. | default_not_translated |
| CHECK constraints | 변환해서 CREATE TABLE 안에 인라인으로인라인 체크 제약은 각 행을 불러올 때 검사해요. | |
| CHECK constraints with functions SQL Server lacks | 만들지 않음SQL Server용으로 쓸 수 없는 체크 제약은 제외하고 보고해요. | check_skipped |
| CHECK ... NOT VALID | 만들지 않음PostgreSQL은 기존 행을 검사한 적이 없어서 그 행이 제약을 어길 수 있어요. | check_not_valid |
| gin, gist, brin and hash indexes, partial and expression indexes, and indexes on text that doesn't fit a key | 만들지 않음SQL Server는 식이 아닌 열을 인덱싱하고, nvarchar(max)는 인덱싱하지 않아요. | index_skipped |
| foreign keys | 데이터 뒤에 WITH NOCHECK로 만들고, 고아 행은 경고와 함께 검사키는 행 다음에 만들고 마지막에 검사해요. 부모 행이 없는 행이 있으면 불러오기를 멈추지 않고 경고와 함께 검사하지 않은 상태로 남겨요. | |
| ON DELETE or ON UPDATE that cascades in a cycle or along a second path | NO ACTIONSQL Server는 한 테이블에 두 번 닿을 수 있는 연쇄 동작을 거부하지만 PostgreSQL은 허용해요. | foreign_key_action_dropped |
| foreign keys to tables the dump doesn't create | 만들지 않음키는 없는 테이블을 가리킬 수 없어요. | foreign_key_skipped |
| partitioned tables | 모든 파티션의 행을 담은 하나의 테이블행은 같아요. 필요하면 SQL Server에서 테이블을 다시 파티션으로 나누세요. | partitions_dropped |
| views, functions, procedures, triggers, rules, policies, extensions and types | 보고서에 나열코드가 PostgreSQL의 언어로 작성되어 있어서 직접 다시 만들 수 있도록 나열해요. | not_converted_objects |
| dumps without CREATE TABLE (--data-only) | 값으로 판단한 타입데이터만 있는 덤프에는 행만 있어서 타입을 값으로 판단해요. | no_schema |
| expressions in INSERT values | NULL데이터베이스를 실행하지 않는 변환기는 이 식을 계산할 수 없어요. | expression_value |
| types IntoSQL doesn't know | nvarchar(max)텍스트는 어떤 값이든 담아요. 불러온 뒤 타입을 바꾸세요. | unknown_type |
| text that isn't valid in its encoding | 잘못된 바이트를 대체 문자로nvarchar는 Unicode 텍스트를 담아요. | invalid_utf8_replaced |
| text decoded twice (ñ) | 그대로 복사덤프에 정확히 이 문자들이 있으므로 그대로 복사하고, 보고서에서 알려 줘요. | mojibake_suspected |
스크립트를 SQL Server에 불러오기
스크립트는 테이블을 만들고 행을 복사한 다음 키, 인덱스, 외래 키를 추가해요. 그래서 빨리 불러오고 첫 오류에서 멈춰요.
sqlcmd -b -f 65001 -S localhost -d mydb -i postgresql.sql_server.sql외래 키는 WITH NOCHECK로 추가한 다음 검사해요. 어떤 행이 존재하지 않는 행을 가리키면, 중간에 실패하지 않고 어떤 제약인지 알려 주고 그 제약은 검사하지 않은 채로 남겨요.
큰 덤프는 서버와 같은 컴퓨터에서 sqlcmd를 실행하세요. 최대 1000행의 배치마다 따로 커밋하므로 트랜잭션 로그가 작게 유지돼요.
좋은 결과를 위한 PostgreSQL 덤프 방법
변환기는 덤프에 담긴 내용을 읽어요. 다음 옵션을 쓰면 더 많은 데이터가 덤프에 남아요:
- pg_dump의 기본값인 --format=plain은 SQL을 써요. custom 또는 directory 아카이브(-Fc, -Fd)는 데이터베이스 서버 없이 pg_restore -f dump.sql로 SQL이 돼요.
- --no-owner --no-privileges는 어차피 변환기가 건너뛰는 역할과 권한을 빼서 덤프가 작아져요.
- pg_dumpall은 모든 데이터베이스를 한 파일에 덤프해요. 각 데이터베이스의 테이블 이름에 그 데이터베이스 이름이 붙어서 충돌하지 않아요.
변환되지 않는 것
뷰, 구체화된 뷰, 함수, 프로시저, 트리거, 규칙, 행 보안 정책, 확장, 타입은 PostgreSQL의 자체 언어로 작성돼요. 보고서에 이름으로 나열되고 몰래 빠지는 일은 없으므로, SQL Server에 맞게 다시 작성할 수 있어요.
PostgreSQL에서 SQL Server로 변환에 관한 질문
여기에 답이 없나요? 문의 페이지로 연락하시면 담당자가 답장해요. 문의 페이지
덤프가 업로드되나요?
아니요. 변환기는 브라우저에서 실행되고 디스크의 덤프를 조각조각 읽으므로, 덤프가 컴퓨터 밖으로 나가지 않아요. 데스크톱 앱과 명령줄도 컴퓨터에서 변환해요.
덤프는 얼마나 커도 되나요?
변환기는 덤프 전체를 메모리에 올리지 않아요. 파일을 범위별로 두 번 읽고, 읽는 대로 스크립트를 써요. 수 GB 덤프도 브라우저, 데스크톱 앱, 명령줄에서 변환돼요.
pg_dump -Fc 아카이브도 읽나요?
직접은 읽지 않아요. custom 아카이브는 바이너리 형식이에요. pg_restore -f dump.sql dump.backup을 실행하면 서버에 연결하지 않고 변환기가 읽는 일반 SQL로 바뀌어요. 아카이브를 받으면 변환기가 이렇게 알려 줘요.
어떤 PostgreSQL 버전의 덤프를 읽나요?
PostgreSQL 10 이상에서 pg_dump와 pg_dumpall로 만든, COPY 또는 INSERT 행이 담긴 덤프예요. 모든 테스트 덤프는 PostgreSQL 13과 18에서 만들고 원본과 셀 단위로 비교해요.
serial과 identity 값은 유지되나요?
네. 각 테이블의 serial 또는 identity 열은 IDENTITY 열이 되고, 모든 행은 id를 유지하며, 다음 id는 가장 큰 id 다음부터 이어져요. SQL Server은(는) 테이블마다 한 열만 번호를 매기므로 다른 열은 카운터 없이 값을 유지해요.
배열, enum 등 PostgreSQL 타입은 어떻게 되나요?
배열은 JSON 배열, boolean은 bit, uuid는 uniqueidentifier, 시간대가 있는 timestamp는 datetimeoffset이 돼요. 텍스트는 PostgreSQL처럼 정확히 비교하고, citext는 대소문자를 무시해요. 범위, 기하 타입, 복합 타입은 PostgreSQL의 텍스트를 유지해요.
함수, 트리거, 뷰는 어떻게 되나요?
데이터베이스마다 자체 언어로 작성하므로 변환하지 않고 보고서에 이름으로 나열해요.
스크립트는 어떤 SQL Server 버전에서 불러올 수 있나요?
SQL Server 2017부터 2025까지예요. 모든 버전에서 같은 스크립트를 불러올 수 있고, 문제가 생기면 첫 오류에서 멈춰요.