Typy danych w SQL: porównanie PostgreSQL i MySQL

Kacper Sieradziński
Kacper Sieradziński
21 sierpnia 2025SQL4 min czytania

Dobór typów danych w SQL to coś, co większość początkujących traktuje po macoszemu — aż do momentu, gdy aplikacja zaczyna zwracać dziwne wyniki lub działać zbyt wolno. Tymczasem różnice między PostgreSQL a MySQL w zakresie precyzji liczb, kolacji tekstowych, obsługi czasu czy JSON-a potrafią realnie wpłynąć na jakość i wydajność systemu.

Obraz główny Typy danych w SQL: porównanie PostgreSQL i MySQL

Zapisz się na nasz newsletter

Otrzymuj regularne aktualizacje, specjalne oferty i porady od ekspertów, które pomogą Ci osiągnąć więcej w krótszym czasie.

W tym artykule znajdziesz praktyczne porównanie typów danych w PostgreSQL i MySQL — z przykładami, różnicami implementacyjnymi i rekomendacjami, które pomogą Ci dobrać odpowiednie typy do swojego projektu. Zrozumiesz też, jak kolacje, NULL i JSONB wpływają na wyniki zapytań oraz jak unikać błędów przy migracjach między tymi silnikami.

Typy liczbowe i precyzja obliczeń

PostgreSQL oferuje szeroką precyzję NUMERIC/DECIMAL (do 131072 cyfr przed kropką i 16383 po), podczas gdy MySQL ogranicza DECIMAL do 65 cyfr łącznie. Dla pieniędzy i rozliczeń używaj typów dokładnych (NUMERIC/DECIMAL), nie float/double. W obu systemach DECIMAL i NUMERIC są synonimami, ale PostgreSQL lepiej skalują się dla bardzo dużych precyzji. Typy całkowite: smallint/int/bigint są porównywalne; autoinkrement to GENERATED ... AS IDENTITY w PostgreSQL i AUTO_INCREMENT w MySQL.

Unikaj DOUBLE PRECISION/FLOAT do kwot. Liczby zmiennoprzecinkowe są szybkie, ale podatne na błędy zaokrągleń. W PostgreSQL bigserial to skrót dla bigint + sekwencja; w nowych projektach preferuj IDENTITY. W MySQL AUTO_INCREMENT może wymagać specyficznej kolejności indeksów (kolumna musi być częścią indeksu głównego).

SQL
1 2 3 4 5 6 7 8 9 10 11 -- PostgreSQL: klucze i kwoty z dokładną precyzją CREATE TABLE invoices_pg ( id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, total numeric(12,2) NOT NULL ); -- MySQL: odpowiednik CREATE TABLE invoices_my ( id bigint PRIMARY KEY AUTO_INCREMENT, total decimal(12,2) NOT NULL );

Zobacz porównanie decyzji architektonicznych w: PostgreSQL vs MySQL: różnice, które mają znaczenie.

MySQL — Jak zacząć? Darmowy e-book

MySQL — Jak zacząć? Darmowy e-book

Praktyczny przewodnik po świecie SQL. Poznaj typy danych, zapytania SELECT, JOIN, funkcje agregujące i nie tylko.

Typy tekstowe

PostgreSQL: text i varchar(n) mają identyczne działanie wydajnościowe; varchar(n) jedynie weryfikuje długość. Collation można ustawić dla kolumny, indeksu i wyrażenia; ICU pozwala na kolacje niedeterministyczne (case/accent-insensitive). Dla wygodnej, bezpiecznej inspekcji bez rozbudowanej konfiguracji rozważ rozszerzenie citext (case-insensitive text).

MySQL 8.0 domyślnie używa utf8mb4 i kolacji rodziny 0900 (np. utf8mb4_0900_ai_ci – accent/case-insensitive). Kolacja wpływa na porównania i kolejność indeksów; dla TINYTEXT/TEXT rozważ indeksy prefiksowe. Pamiętaj, że limit długości varchar w MySQL zależy od rozmiaru wiersza i zestawu znaków (limit w bajtach).

SQL
1 2 3 4 5 -- PostgreSQL: bezpieczny unique bez wrażliwości na wielkość liter CREATE EXTENSION IF NOT EXISTS citext; CREATE TABLE users_pg ( email citext UNIQUE );

Więcej o dialekcie i rozszerzeniach: Dialekt SQL w PostgreSQL: najważniejsze różnice i rozszerzenia.

Typy daty i czasu

PostgreSQL ma date, time, timestamp [without time zone] oraz timestamptz (timestamp with time zone) i interval. timestamptz przechowuje czas w UTC i konwertuje przy I/O według strefy sesji; zakres jest bardzo szeroki. W praktyce używaj timestamptz dla wszelkich znaczników czasu.

MySQL rozróżnia DATETIME (bez strefy) i TIMESTAMP (konwersja do UTC, węższy zakres: 1970–2038). Oba wspierają precyzję ułamkową do mikrosekund (fsp do 6). Jeżeli dane mają wykraczać poza rok 2038 lub chcesz pełnej kontroli, preferuj DATETIME i zapisywanie UTC na poziomie aplikacji.

SQL
1 2 3 4 5 6 7 8 9 -- PostgreSQL CREATE TABLE events_pg ( occurred_at timestamptz NOT NULL DEFAULT now() ); -- MySQL CREATE TABLE events_my ( occurred_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP );

Wprowadzenie do MySQL i praktyka konfiguracji: MySQL: wprowadzenie, instalacja i pierwsza baza danych.

Typy logiczne i wartości NULL

PostgreSQL ma natywny boolean (true/false) i pełną logikę trójwartościową. Dla porównań „bezpiecznych na NULL” użyj IS DISTINCT FROM zamiast =. Agregacje i warunki WHERE muszą uwzględniać, że NULL nie jest równoznaczny z false.

MySQL traktuje BOOLEAN jako alias TINYINT(1). Pamiętaj o operatorze NULL-safe: <=>. Zwykłe = z NULL zwraca UNKNOWN (nieprawda); <=> pozwala na jednoznaczne porównanie również w indeksach funkcjonalnych.

SQL
1 2 3 4 5 -- PostgreSQL: porównanie bezpieczne na NULL SELECT (NULL IS DISTINCT FROM NULL) AS pg_result; -- false -- MySQL: operator NULL-safe SELECT NULL <=> NULL AS my_result; -- 1 (true)

JSON i JSONB – różnice implementacyjne

PostgreSQL ma dwa typy: JSON (tekst, zachowuje kolejność/duplikaty kluczy) i JSONB (binarna reprezentacja, kanoniczna, indeksowalna). JSONB obsługuje indeksy GIN i operatory @>, ?, ->, ->>. W praktyce JSONB jest standardem dla zapytań i filtrów; JSON bywa używany do logów, gdzie ważna jest dokładna reprezentacja wejścia.

MySQL ma typ JSON (wewnętrznie binarny). Indeksowanie odbywa się przez kolumny generowane lub indeksy funkcjonalne na JSON_EXTRACT/->> (8.0+). Brak osobnego JSONB, ale operacyjnie najbliżej mu do PostgreSQL JSONB.

SQL
1 2 3 4 5 6 7 8 9 10 -- PostgreSQL: indeksowanie JSONB CREATE TABLE orders_pg (id bigserial PRIMARY KEY, doc jsonb NOT NULL); CREATE INDEX ON orders_pg USING gin (doc jsonb_path_ops); -- zapytanie po kluczu SELECT id FROM orders_pg WHERE doc @> '{"customer":{"id":123}}'; -- MySQL: indeks na wyrażeniu JSON CREATE TABLE orders_my (id bigint PRIMARY KEY AUTO_INCREMENT, doc JSON NOT NULL); CREATE INDEX idx_orders_customer ON orders_my ((JSON_EXTRACT(doc, '$.customer.id'))); SELECT id FROM orders_my WHERE JSON_EXTRACT(doc, '$.customer.id') = 123;

Definiowanie własnych typów w PostgreSQL

PostgreSQL pozwala tworzyć własne typy: ENUM, DOMAIN, COMPOSITE, a nawet RANGE. To silny mechanizm przenoszący reguły walidacji z aplikacji do bazy i poprawiający spójność danych. MySQL posiada ENUM/SET, ale nie ma domen ani typów zakresowych.

SQL
1 2 3 4 5 6 7 8 9 10 11 -- PostgreSQL: enum i domena CREATE TYPE mood AS ENUM ('happy', 'ok', 'sad'); CREATE DOMAIN email AS text CHECK (VALUE ~* '^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$'); CREATE TABLE person_pg ( id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, state mood NOT NULL, contact email NOT NULL );

Jeśli zaczynasz z PostgreSQL, zajrzyj do: PostgreSQL: wprowadzenie, instalacja i pierwsza baza danych.

Jak dobrać typ danych do potrzeb aplikacji

Dobór typów danych w SQL wpływa na poprawność, indeksowanie i koszty I/O. Kwoty: NUMERIC/DECIMAL z odpowiednią skalą. Identyfikatory: bigint (wystarczy na lata) lub uuid, jeśli potrzebujesz decentralizacji. Tekst: w PostgreSQL preferuj text (z ewentualnym citext), w MySQL dobierz kolację utf8mb4_0900_ai_ci dla wyszukiwań bez rozróżniania wielkości liter.

Czas: w PostgreSQL używaj timestamptz; w MySQL rozważ DATETIME w UTC lub TIMESTAMP, jeśli zakres 1970–2038 jest akceptowalny. JSON: jeżeli dane są schematyczne i często filtrowane, rozbij na kolumny; JSON/JSONB pozostaw na dane półstrukturalne i logi. To praktyczne różnice typów danych w SQL, które powinny prowadzić wybór pod konkretne profile zapytań i wymagania aplikacji.

Na koniec: testuj na danych produkcyjnych. Typy danych SQL i kolacje potrafią zmienić plany zapytań i rozmiar indeksów; w razie wątpliwości prototypuj i mierz.

Kurs SkumajBazy — Czas w końcu nauczyć się SQLa

Kurs SkumajBazy — Czas w końcu nauczyć się SQLa

Kompleksowy kurs SQL dla programistów, analityków i wszystkich, którzy chcą efektywnie pracować z danymi. Od podstaw do zaawansowanych zapytań.