Tipos de Dados SQL

Autor

Douglas Braga

O SQL define um conjunto de tipos de dados embutidos e mecanismos para criar tipos definidos pelo usuário. Conhecer os tipos disponíveis é essencial para modelar o banco de dados com precisão e eficiência.

Tipos Temporais

Tipo Descrição Exemplo
DATE Data (ano, mês, dia) '2025-03-15'
TIME Hora do dia (horas, minutos, segundos) '14:30:00'
TIMESTAMP Data + hora '2025-03-15 14:30:00'
INTERVAL Período de tempo INTERVAL '30 days'

A subtração entre dois DATE produz um inteiro (número de dias); a subtração entre dois TIME produz um INTERVAL; somar um INTERVAL a uma data produz outra data.

Para demonstrar cada tipo com saída concreta, criamos a tabela evento_academico — eventos institucionais da UnDF com data de início e fim, horário, e registro de criação com timestamp.

CREATE TABLE IF NOT EXISTS evento_academico (
    id_evento   SERIAL        PRIMARY KEY,
    nome        VARCHAR(100)  NOT NULL,
    tipo        VARCHAR(30)   NOT NULL,
    id_escola   INT           NOT NULL REFERENCES escola (id_escola),
    data_inicio DATE          NOT NULL,
    data_fim    DATE          NOT NULL,
    hora_inicio TIME          NOT NULL,
    hora_fim    TIME          NOT NULL,
    criado_em   TIMESTAMP     DEFAULT CURRENT_TIMESTAMP,
    UNIQUE (nome, data_inicio)
);
INSERT INTO evento_academico
    (nome, tipo, id_escola, data_inicio, data_fim, hora_inicio, hora_fim)
VALUES
    ('Semana Acadêmica de Engenharia de Software', 'Semana Acadêmica', 31, '2025-10-06', '2025-10-10', '08:00', '18:00'),
    ('Workshop de Banco de Dados',                 'Workshop',         31, '2026-03-20', '2026-03-20', '09:00', '17:00'),
    ('Palestra sobre Inteligência Artificial',     'Palestra',         21, '2026-06-10', '2026-06-10', '19:00', '21:00'),
    ('Jornada Pedagógica',                         'Semana Acadêmica', 21, '2025-07-28', '2025-08-01', '08:00', '17:00'),
    ('Defesa de TCC — Pedro Alves',                'Defesa',           31, '2026-05-15', '2026-05-15', '14:00', '16:00'),
    ('Defesa de TCC — Camila Rocha',               'Defesa',           31, '2026-07-20', '2026-07-20', '10:00', '12:00')
ON CONFLICT (nome, data_inicio) DO NOTHING;

DATE

Colunas DATE armazenam apenas a data (sem hora). A subtração entre dois DATE retorna o número de dias entre elas.

-- Datas de início e fim de cada evento; duração em dias
SELECT nome, tipo,
       data_inicio,
       data_fim,
       (data_fim - data_inicio + 1) AS duracao_dias
FROM   evento_academico
ORDER BY data_inicio;
6 records
nome tipo data_inicio data_fim duracao_dias
Jornada Pedagógica Semana Acadêmica 2025-07-28 2025-08-01 5
Semana Acadêmica de Engenharia de Software Semana Acadêmica 2025-10-06 2025-10-10 5
Workshop de Banco de Dados Workshop 2026-03-20 2026-03-20 1
Defesa de TCC — Pedro Alves Defesa 2026-05-15 2026-05-15 1
Palestra sobre Inteligência Artificial Palestra 2026-06-10 2026-06-10 1
Defesa de TCC — Camila Rocha Defesa 2026-07-20 2026-07-20 1

TIME

Colunas TIME armazenam apenas o horário. A subtração entre dois TIME retorna um INTERVAL.

-- Horário de início e fim de cada evento; carga horária diária
SELECT nome,
       hora_inicio,
       hora_fim,
       hora_fim - hora_inicio AS carga_horaria_diaria
FROM   evento_academico
ORDER BY hora_inicio;
6 records
nome hora_inicio hora_fim carga_horaria_diaria
Semana Acadêmica de Engenharia de Software 08:00:00 18:00:00 10:00:00
Jornada Pedagógica 08:00:00 17:00:00 09:00:00
Workshop de Banco de Dados 09:00:00 17:00:00 08:00:00
Defesa de TCC — Camila Rocha 10:00:00 12:00:00 02:00:00
Defesa de TCC — Pedro Alves 14:00:00 16:00:00 02:00:00
Palestra sobre Inteligência Artificial 19:00:00 21:00:00 02:00:00

TIMESTAMP

TIMESTAMP armazena data e hora juntas. A coluna criado_em é preenchida automaticamente com CURRENT_TIMESTAMP na inserção.

-- Timestamp de criação de cada registro e diferença em relação à hora atual
SELECT nome,
       criado_em,
       CURRENT_TIMESTAMP                    AS agora,
       AGE(CURRENT_TIMESTAMP, criado_em)   AS tempo_desde_insercao
FROM   evento_academico
ORDER BY criado_em;
6 records
nome criado_em agora tempo_desde_insercao
Semana Acadêmica de Engenharia de Software 2026-05-15 18:25:41 2026-05-15 18:25:41 00:00:00.024799
Workshop de Banco de Dados 2026-05-15 18:25:41 2026-05-15 18:25:41 00:00:00.024799
Palestra sobre Inteligência Artificial 2026-05-15 18:25:41 2026-05-15 18:25:41 00:00:00.024799
Jornada Pedagógica 2026-05-15 18:25:41 2026-05-15 18:25:41 00:00:00.024799
Defesa de TCC — Pedro Alves 2026-05-15 18:25:41 2026-05-15 18:25:41 00:00:00.024799
Defesa de TCC — Camila Rocha 2026-05-15 18:25:41 2026-05-15 18:25:41 00:00:00.024799

INTERVAL e aritmética de datas

INTERVAL representa uma duração. O PostgreSQL infere INTERVAL de subtrações entre datas e horas e aceita literais como INTERVAL '7 days' ou INTERVAL '2 hours 30 minutes'.

-- Dias até cada evento a partir de hoje; status (passado / futuro)
SELECT nome,
       data_inicio,
       (data_inicio - CURRENT_DATE)         AS dias_ate_evento,
       CASE
           WHEN data_inicio > CURRENT_DATE THEN 'futuro'
           WHEN data_inicio = CURRENT_DATE THEN 'hoje'
           ELSE 'passado'
       END                                  AS status
FROM   evento_academico
ORDER BY data_inicio;
6 records
nome data_inicio dias_ate_evento status
Jornada Pedagógica 2025-07-28 -291 passado
Semana Acadêmica de Engenharia de Software 2025-10-06 -221 passado
Workshop de Banco de Dados 2026-03-20 -56 passado
Defesa de TCC — Pedro Alves 2026-05-15 0 hoje
Palestra sobre Inteligência Artificial 2026-06-10 26 futuro
Defesa de TCC — Camila Rocha 2026-07-20 66 futuro
-- EXTRACT: componentes individuais de uma data
SELECT nome,
       data_inicio,
       EXTRACT(YEAR  FROM data_inicio)::INT AS ano,
       EXTRACT(MONTH FROM data_inicio)::INT AS mes,
       EXTRACT(DOW   FROM data_inicio)::INT AS dia_semana,  -- 0=Dom … 6=Sáb
       TO_CHAR(data_inicio, 'TMDay, DD "de" TMMonth "de" YYYY') AS data_extenso
FROM   evento_academico
ORDER BY data_inicio;
6 records
nome data_inicio ano mes dia_semana data_extenso
Jornada Pedagógica 2025-07-28 2025 7 1 Monday, 28 de July de 2025
Semana Acadêmica de Engenharia de Software 2025-10-06 2025 10 1 Monday, 06 de October de 2025
Workshop de Banco de Dados 2026-03-20 2026 3 5 Friday, 20 de March de 2026
Defesa de TCC — Pedro Alves 2026-05-15 2026 5 5 Friday, 15 de May de 2026
Palestra sobre Inteligência Artificial 2026-06-10 2026 6 3 Wednesday, 10 de June de 2026
Defesa de TCC — Camila Rocha 2026-07-20 2026 7 1 Monday, 20 de July de 2026

Tipos de Texto

O SQL padrão define:

Tipo Descrição
CHAR(n) String de tamanho fixo n (preenchida com espaços)
VARCHAR(n) String de tamanho variável, máximo n caracteres
TEXT String de tamanho ilimitado (extensão do PostgreSQL, equivalente ao CLOB)

O PostgreSQL não distingue CHAR de VARCHAR em desempenho — ambos são armazenados de forma compacta. O tipo TEXT é preferido para textos sem limite predefinido.

Tipos Numéricos

Tipo Descrição
SMALLINT Inteiro de 2 bytes (-32.768 a 32.767)
INT / INTEGER Inteiro de 4 bytes
BIGINT Inteiro de 8 bytes
NUMERIC(p, s) Decimal exato com p dígitos totais e s casas decimais
REAL / FLOAT Ponto flutuante (precisão aproximada)

No banco da UnDF, salario é NUMERIC(10,2) para garantir precisão exata em valores monetários — evitando erros de arredondamento que ocorreriam com FLOAT.

Tipos de Objetos Grandes (LOB)

Para armazenar objetos grandes (fotos, vídeos, documentos):

Tipo padrão SQL Equivalente PostgreSQL Descrição
BLOB BYTEA Dados binários (fotos, PDFs, executáveis)
CLOB TEXT Texto de tamanho arbitrário

Quando uma consulta retorna um objeto grande, o SGBD geralmente retorna um ponteiro para o objeto em vez dos dados completos — pois transferir gigabytes pela rede seria inviável.

-- Exemplo: tabela com foto de perfil armazenada como binário
CREATE TABLE perfil_aluno (
    id_aluno   INT     PRIMARY KEY REFERENCES aluno (id_aluno),
    foto       BYTEA,          -- blob: foto em formato binário
    curriculo  TEXT            -- clob: texto livre do currículo
);

Tipos Definidos pelo Usuário

CREATE TYPE

O SQL:1999 introduziu CREATE TYPE, que permite definir novos tipos com base em tipos existentes:

-- Tipo Reais: valores monetários em reais
CREATE TYPE Reais AS NUMERIC(12, 2) FINAL;

-- Uso em uma tabela
CREATE TABLE orcamento_escola (
    id_escola  INT    PRIMARY KEY REFERENCES escola (id_escola),
    valor      Reais
);
Nota

No PostgreSQL, o CREATE TYPE tem uso mais amplo: cria tipos compostos, tipos enumerados (ENUM) e tipos de range. O ENUM é especialmente útil para representar conjuntos fixos de valores:

CREATE TYPE tipo_curso_enum AS ENUM ('Bacharelado', 'Licenciatura', 'Tecnólogo');

-- Usando o tipo enum
ALTER TABLE curso ALTER COLUMN tipo_curso TYPE tipo_curso_enum
    USING tipo_curso::tipo_curso_enum;

CREATE DOMAIN

Um domínio é semelhante a um tipo, mas pode incluir constraints. É uma camada de abstração que permite reutilizar validações em várias tabelas:

-- Domínio para ch_semanal: apenas 20 ou 40 horas
CREATE DOMAIN ch_semanal_valida AS INT
    NOT NULL
    CHECK (VALUE IN (20, 40));

-- Domínio para nota: entre 0.0 e 10.0
CREATE DOMAIN nota_disciplina AS NUMERIC(4, 2)
    CHECK (VALUE BETWEEN 0.0 AND 10.0);

Diferença entre TYPE e DOMAIN: - TYPE define a estrutura dos dados. - DOMAIN é um tipo com constraints embutidas — reutilizável como uma regra de negócio.

-- Tipos definidos no banco atual (domínios e tipos customizados)
SELECT typname AS tipo, typtype AS categoria
FROM   pg_type
WHERE  typnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'public')
ORDER BY typname;
Displaying records 1 - 10
tipo categoria
_aluno b
_centro b
_curso b
_disciplina b
_escola b
_evento_academico b
_matricula_disciplina b
_ministra b
_mv_desempenho_aluno b
_prereq b

Para Praticar

-- Funções de data: extraindo partes de uma data
SELECT nome,
       ano_ingresso,
       EXTRACT(YEAR  FROM CURRENT_DATE)                AS ano_atual,
       EXTRACT(YEAR  FROM CURRENT_DATE) - ano_ingresso AS tempo_na_undf
FROM   aluno
ORDER BY tempo_na_undf DESC, nome;
Displaying records 1 - 10
nome ano_ingresso ano_atual tempo_na_undf
Amanda Moreira 2023 2026 3
Beatriz Carvalho 2023 2026 3
Larissa Araújo 2023 2026 3
Lucas Santos 2023 2026 3
Matheus Silva 2023 2026 3
Thiago Gomes 2023 2026 3
Carolina Lima 2024 2026 2
Fernanda Martins 2024 2026 2
Gabriel Ribeiro 2024 2026 2
Isabela Freitas 2024 2026 2
-- Formatando valores numéricos: salário em formato brasileiro
SELECT nome,
       TO_CHAR(salario, 'FM"R$" 999.990,00') AS salario_formatado,
       ch_semanal
FROM   professor
ORDER BY salario DESC;
7 records
nome salario_formatado ch_semanal
Helena Carvalho R$ ###.###,## 40
Carla Pinto R$ ###.###,## 40
Bruno Teixeira R$ ###.###,## 40
Felipe Araujo R$ ###.###,## 40
Gustavo Costa R$ ###.###,## 20
Eduarda Souza R$ ###.###,## 20
Igor Melo R$ ###.###,## 20
-- CAST entre tipos: convertendo id_aluno para texto e extraindo partes
SELECT id_aluno,
       nome,
       CAST(id_aluno AS TEXT)                 AS id_texto,
       LEFT(CAST(id_aluno AS TEXT), 4)        AS ano_ingresso_extraido,
       SUBSTRING(CAST(id_aluno AS TEXT), 5, 3) AS id_curso_extraido
FROM   aluno
LIMIT 5;
5 records
id_aluno nome id_texto ano_ingresso_extraido id_curso_extraido
2023111001 Beatriz Carvalho 2023111001 2023 111
2023111002 Lucas Santos 2023111002 2023 111
2024111001 Gabriel Ribeiro 2024111001 2024 111
2024111002 Fernanda Martins 2024111002 2024 111
2025111001 Rodrigo Lima 2025111001 2025 111
-- Aritmética de datas com INTERVAL: quantos dias faltam para a conclusão estimada
-- de cada aluno de ENS (4 anos após ingresso, em 15/12)
SELECT nome,
       ano_ingresso,
       make_date(ano_ingresso + 4, 12, 15)           AS conclusao_estimada,
       make_date(ano_ingresso + 4, 12, 15) - CURRENT_DATE AS dias_restantes
FROM   aluno
WHERE  id_curso = 311
ORDER BY conclusao_estimada;
6 records
nome ano_ingresso conclusao_estimada dias_restantes
Matheus Silva 2023 2027-12-15 579
Larissa Araújo 2023 2027-12-15 579
Pedro Carvalho 2024 2028-12-15 945
Isabela Freitas 2024 2028-12-15 945
Diego Correia 2025 2029-12-15 1310
Juliana Nunes 2025 2029-12-15 1310