Hands-on: PostgreSQL na Prática

Autor

Douglas Braga

Nesta seção vamos instalar o PostgreSQL, criar o banco de dados curso da UnDF e popular as tabelas com os dados reais que utilizamos ao longo do material.

Instalação do PostgreSQL

sudo apt update
sudo apt install -y postgresql postgresql-contrib

# Iniciar e habilitar o serviço
sudo systemctl start postgresql
sudo systemctl enable postgresql

# Verificar se está rodando
sudo systemctl status postgresql

Após a instalação, acesse o console interativo:

sudo -u postgres psql

Com Homebrew instalado:

brew install postgresql@16

# Iniciar o serviço
brew services start postgresql@16

# Adicionar ao PATH (cole no ~/.zshrc ou ~/.bash_profile)
echo 'export PATH="/opt/homebrew/opt/postgresql@16/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc

# Acessar o console
psql postgres

Alternativa: o app Postgres.app oferece instalação com um clique e não requer Homebrew.

  1. Acesse postgresql.org/download/windows e baixe o instalador EDB.
  2. Execute o instalador e siga o assistente. Anote a senha definida para o usuário postgres.
  3. O pgAdmin 4 (interface gráfica) é instalado junto e pode ser usado no lugar do terminal.

Para usar o terminal psql, abra o SQL Shell (psql) pelo Menu Iniciar e preencha as informações solicitadas (host: localhost, porta: 5432, usuário: postgres).

Alternativa: Caso não possua privilégios de administrador, siga essas instruções.

Criando o Banco de Dados

Conecte-se ao console do PostgreSQL e crie o banco:

CREATE DATABASE curso;

Em seguida, conecte-se a ele:

\c curso
Nota

No pgAdmin (Windows/Mac), clique com o botão direito em Databases → Create → Database, informe o nome curso e clique em Save. Depois abra o Query Tool para executar os comandos SQL.

Criando as Tabelas

As tabelas devem ser criadas na ordem correta: primeiro as referenciadas (sem dependências), depois as que referenciam outras.

Tabela centro

CREATE TABLE IF NOT EXISTS centro (
    id_centro   INTEGER      PRIMARY KEY,
    sigla       VARCHAR(10)  NOT NULL UNIQUE,
    nome_centro VARCHAR(100) NOT NULL
);

Tabela escola

CREATE TABLE IF NOT EXISTS escola (
    id_escola   INTEGER      PRIMARY KEY,
    sigla       VARCHAR(10)  NOT NULL UNIQUE,
    nome_escola VARCHAR(100) NOT NULL,
    id_centro   INTEGER      REFERENCES centro(id_centro)
);

Tabela curso

CREATE TABLE IF NOT EXISTS curso (
    id_curso      INTEGER      PRIMARY KEY,
    sigla_curso   VARCHAR(5)   NOT NULL UNIQUE,
    nome_curso    VARCHAR(100) NOT NULL,
    id_escola     INTEGER      REFERENCES escola(id_escola),
    n_semestres   INTEGER      NOT NULL,
    carga_horaria INTEGER      NOT NULL,
    tipo_curso    VARCHAR(20)  NOT NULL
);

Tabela professor

CREATE TABLE IF NOT EXISTS professor (
    id_prof    INTEGER       PRIMARY KEY,
    nome       VARCHAR(50)   NOT NULL,
    id_escola  INTEGER       REFERENCES escola(id_escola),
    ch_semanal INTEGER       NOT NULL,
    salario    NUMERIC(10,2) NOT NULL
);

Tabela aluno

CREATE TABLE IF NOT EXISTS aluno (
    id_aluno     INTEGER     PRIMARY KEY,
    nome         VARCHAR(50) NOT NULL,
    id_curso     INTEGER     REFERENCES curso(id_curso),
    ano_ingresso INTEGER     NOT NULL
);

Tabela disciplina

CREATE TABLE IF NOT EXISTS disciplina (
    id_disciplina   INTEGER      PRIMARY KEY,
    nome_disciplina VARCHAR(100) NOT NULL,
    id_curso        INTEGER      REFERENCES curso(id_curso),
    semestre        INTEGER      NOT NULL,
    carga_horaria   INTEGER      NOT NULL CHECK (carga_horaria > 0)
);

Tabela prereq

CREATE TABLE IF NOT EXISTS prereq (
    id_disciplina INTEGER REFERENCES disciplina(id_disciplina),
    id_prereq     INTEGER REFERENCES disciplina(id_disciplina),
    PRIMARY KEY (id_disciplina, id_prereq)
);

Tabela ministra

CREATE TABLE IF NOT EXISTS ministra (
    id_disciplina INTEGER REFERENCES disciplina(id_disciplina),
    ano           INTEGER NOT NULL,
    semestre      INTEGER NOT NULL,
    turma         INTEGER NOT NULL,
    id_prof       INTEGER REFERENCES professor(id_prof),
    horario       VARCHAR(50),
    sala          VARCHAR(20),
    PRIMARY KEY (id_disciplina, ano, semestre, turma)
);

Tabela matricula_disciplina

CREATE TABLE IF NOT EXISTS matricula_disciplina (
    id_disciplina INTEGER NOT NULL,
    ano           INTEGER NOT NULL,
    semestre      INTEGER NOT NULL,
    turma         INTEGER NOT NULL,
    id_aluno      INTEGER REFERENCES aluno(id_aluno),
    nota          NUMERIC(4,1),
    aprovado      SMALLINT,
    PRIMARY KEY (id_disciplina, ano, semestre, turma, id_aluno),
    FOREIGN KEY (id_disciplina, ano, semestre, turma)
        REFERENCES ministra(id_disciplina, ano, semestre, turma)
);

Carregando os Dados

Antes de inserir, limpamos os dados anteriores (respeitando a ordem das chaves estrangeiras):

TRUNCATE matricula_disciplina, ministra, prereq, disciplina,
         aluno, professor, curso, escola, centro CASCADE;

Centros

INSERT INTO centro (id_centro, sigla, nome_centro) VALUES
    (1, 'COCHCMA', 'Ciências Humanas, Cidadania e Meio Ambiente'),
    (2, 'COEMAG',  'Centro de Educação, Magistério e Artes'),
    (3, 'COETI',   'Centro de Engenharias, Tecnologia e Inovação');

Escolas

INSERT INTO escola (id_escola, sigla, nome_escola, id_centro) VALUES
    (11, 'ESG',   'Escola Superior de Gestão',                             1),
    (21, 'EEMA',  'Escola de Educação, Magistério e Arte',                 2),
    (31, 'ESETI', 'Escola Superior de Engenharias, Tecnologia e Inovação', 3);

Cursos

INSERT INTO curso (id_curso, sigla_curso, nome_curso, id_escola, n_semestres, carga_horaria, tipo_curso) VALUES
    (311, 'ENS', 'Engenharia de Software', 31, 8, 3200, 'bacharelado'),
    (111, 'ECO', 'Ciências Econômicas',    11, 8, 3000, 'bacharelado'),
    (211, 'PED', 'Pedagogia',              21, 8, 3410, 'licenciatura');

Professores

INSERT INTO professor (id_prof, nome, id_escola, ch_semanal, salario) VALUES
    (21200001, 'Eduarda Souza',   21, 20, 3200),
    (21400002, 'Felipe Araujo',   21, 40, 6400),
    (31200001, 'Gustavo Costa',   31, 20, 3300),
    (31400002, 'Helena Carvalho', 31, 40, 6700),
    (31200003, 'Igor Melo',       31, 20, 3100),
    (11400001, 'Bruno Teixeira',  11, 40, 6500),
    (11400002, 'Carla Pinto',     11, 40, 6600);

Alunos

INSERT INTO aluno (id_aluno, nome, id_curso, ano_ingresso) VALUES
    (2023111001, 'Beatriz Carvalho', 111, 2023),
    (2023111002, 'Lucas Santos',     111, 2023),
    (2024111001, 'Gabriel Ribeiro',  111, 2024),
    (2024111002, 'Fernanda Martins', 111, 2024),
    (2025111001, 'Rodrigo Lima',     111, 2025),
    (2025111002, 'Camila Teixeira',  111, 2025),
    (2023211001, 'Amanda Moreira',   211, 2023),
    (2023211002, 'Thiago Gomes',     211, 2023),
    (2024211001, 'Carolina Lima',    211, 2024),
    (2024211002, 'Rafael Costa',     211, 2024),
    (2025211001, 'Vinícius Pereira', 211, 2025),
    (2025211002, 'Letícia Souza',    211, 2025),
    (2023311001, 'Matheus Silva',    311, 2023),
    (2023311002, 'Larissa Araújo',   311, 2023),
    (2024311001, 'Pedro Carvalho',   311, 2024),
    (2024311002, 'Isabela Freitas',  311, 2024),
    (2025311001, 'Diego Correia',    311, 2025),
    (2025311002, 'Juliana Nunes',    311, 2025);

Disciplinas

INSERT INTO disciplina (id_disciplina, nome_disciplina, id_curso, semestre, carga_horaria) VALUES
    (1110201, 'História Econômica Geral',                                    111, 2,  75),
    (1110202, 'Culturas Digitais',                                           111, 2,  60),
    (1110401, 'Microeconomia 2',                                             111, 4,  75),
    (1110402, 'Macroeconomia 2',                                             111, 4,  95),
    (1110601, 'Economia Brasileira',                                         111, 6,  75),
    (1110602, 'Orçamento e Finanças Públicas',                               111, 6,  75),
    (1110801, 'Monografia',                                                  111, 8, 100),
    (1110802, 'Desenvolvimento Econômico',                                   111, 8,  90),
    (2110201, 'Desenvolvimento Humano',                                      211, 2,  60),
    (2110202, 'Didática Geral',                                              211, 2,  60),
    (2110401, 'Eletiva I',                                                   211, 4,  80),
    (2110402, 'Fundamentos e Orientações Metodológicas - Ed. Infantil - BNCC', 211, 4, 80),
    (2110601, 'Eletiva II',                                                  211, 6,  80),
    (2110602, 'Educação do Campo, Indígena e Quilombola',                    211, 6,  40),
    (2110801, 'Tecnologia Educacional - Design',                             211, 8,  60),
    (2110802, 'Estatística Aplicada',                                        211, 8,  50),
    (3110201, 'Bases da Eng. de Software 2',                                 311, 2, 150),
    (3110202, 'Projeto Aplicado 2 - site WEB',                               311, 2, 120),
    (3110401, 'Bases da Eng. de Software 4',                                 311, 4, 180),
    (3110402, 'Projeto Aplicado 4 - blockchain',                             311, 4, 120),
    (3110601, 'Bases da Eng. de Software 6',                                 311, 6, 150),
    (3110602, 'Projeto Aplicado 6 - Machine Learning',                       311, 6, 120),
    (3110801, 'Bases da Eng. de Software 8',                                 311, 8, 100),
    (3110802, 'Projeto Aplicado 8 - Sistema em Tempo Real',                  311, 8, 160);

Pré-requisitos

INSERT INTO prereq (id_disciplina, id_prereq) VALUES
    (3110401, 3110201),
    (3110402, 3110202),
    (3110601, 3110401),
    (3110601, 3110402),
    (3110602, 3110402),
    (3110801, 3110601),
    (3110802, 3110601),
    (3110802, 3110602),
    (1110401, 1110201),
    (1110601, 1110401),
    (1110602, 1110402),
    (1110801, 1110601),
    (1110801, 1110602),
    (2110401, 2110201),
    (2110601, 2110401),
    (2110801, 2110601);

Turmas ministradas

INSERT INTO ministra (id_disciplina, ano, semestre, turma, id_prof, horario, sala) VALUES
    (2110201, 2023, 2, 1, 21200001, '2f-19:00-21:30',               'EEMA-SALA-003'),
    (2110202, 2023, 2, 1, 21200001, '2f-19:00-21:30',               'EEMA-SALA-004'),
    (2110201, 2024, 2, 1, 21200001, '3f-19:00-21:30',               'EEMA-SALA-003'),
    (2110202, 2024, 2, 1, 21200001, '3f-19:00-21:30',               'EEMA-SALA-004'),
    (2110401, 2024, 2, 1, 21200001, '2f-19:00-22:20',               'EEMA-SALA-001'),
    (2110402, 2024, 2, 1, 21200001, '2f-19:00-22:20',               'EEMA-SALA-002'),
    (2110201, 2025, 2, 1, 21200001, '3f-19:00-21:30',               'EEMA-SALA-001'),
    (2110202, 2025, 2, 1, 21400002, '3f-19:00-21:30',               'EEMA-SALA-002'),
    (2110401, 2025, 2, 1, 21200001, '2f-19:00-22:20',               'EEMA-SALA-003'),
    (2110402, 2025, 2, 1, 21200001, '2f-19:00-22:20',               'EEMA-SALA-004'),
    (2110601, 2025, 2, 1, 21200001, '2f-19:00-22:20',               'EEMA-SALA-001'),
    (2110602, 2025, 2, 1, 21200001, '2f-19:00-20:40',               'EEMA-SALA-046'),
    (3110201, 2023, 2, 1, 31200001, '2f-13:45-16:55/4f-13:45-16:50', 'ESETI-SALA-001'),
    (3110202, 2023, 2, 1, 31200001, '2f-13:45-16:15/4f-13:45-16:15', 'ESETI-SALA-003'),
    (3110201, 2024, 2, 1, 31200001, '2f-13:45-16:55/4f-13:45-16:50', 'ESETI-SALA-003'),
    (3110202, 2024, 2, 1, 31400002, '2f-13:45-16:15/4f-13:45-16:15', 'ESETI-SALA-006'),
    (3110401, 2024, 2, 1, 31200001, '2f-13:45-17:30/4f-13:45-17:30', 'ESETI-SALA-001'),
    (3110402, 2024, 2, 1, 31400002, '2f-13:45-16:15/4f-13:45-16:15', 'ESETI-SALA-005'),
    (3110201, 2025, 2, 1, 31200001, '2f-13:45-16:55/4f-13:45-16:50', 'ESETI-SALA-005'),
    (3110202, 2025, 2, 1, 31400002, '2f-13:45-16:15/4f-13:45-16:15', 'ESETI-SALA-010'),
    (3110401, 2025, 2, 1, 31200001, '2f-13:45-17:30/4f-13:45-17:30', 'ESETI-SALA-001'),
    (3110402, 2025, 2, 1, 31200003, '2f-13:45-16:15/4f-13:45-16:15', 'ESETI-SALA-008'),
    (3110601, 2025, 2, 1, 31400002, '2f-13:45-16:55/4f-13:45-16:50', 'ESETI-SALA-003'),
    (3110602, 2025, 2, 1, 31200003, '2f-13:45-16:15/4f-13:45-16:15', 'ESETI-SALA-007'),
    (1110201, 2023, 2, 1, 11400001, '2f-7:30-10:40',                'ESG-SALA-005'),
    (1110202, 2023, 2, 1, 11400001, '2f-7:30-10:00',                'ESG-SALA-012'),
    (1110201, 2024, 2, 1, 11400001, '2f-7:30-10:40',                'ESG-SALA-013'),
    (1110202, 2024, 2, 1, 11400001, '3f-10:00-12:30',               'ESG-SALA-001'),
    (1110401, 2024, 2, 1, 11400001, '2f-7:30-10:40',                'ESG-SALA-009'),
    (1110402, 2024, 2, 1, 11400001, '2f-7:30-11:30',                'ESG-SALA-001'),
    (1110201, 2025, 2, 1, 11400001, '2f-7:30-10:40',                'ESG-SALA-025'),
    (1110202, 2025, 2, 1, 11400002, '3f-10:00-12:30',               'ESG-SALA-007'),
    (1110401, 2025, 2, 1, 11400001, '2f-7:30-10:40',                'ESG-SALA-021'),
    (1110402, 2025, 2, 1, 11400001, '2f-7:30-11:30',                'ESG-SALA-005'),
    (1110601, 2025, 2, 1, 11400001, '2f-7:30-10:40',                'ESG-SALA-015'),
    (1110602, 2025, 2, 1, 11400002, '2f-7:30-10:40',                'ESG-SALA-016');

Matrículas em disciplinas

INSERT INTO matricula_disciplina (id_disciplina, ano, semestre, turma, id_aluno, nota, aprovado) VALUES
    (3110201, 2023, 2, 1, 2023311001, 9.0, 1),
    (3110202, 2023, 2, 1, 2023311001, 7.4, 1),
    (3110401, 2024, 2, 1, 2023311001, 7.5, 1),
    (3110402, 2024, 2, 1, 2023311001, 6.6, 1),
    (3110601, 2025, 2, 1, 2023311001, 4.1, 0),
    (3110602, 2025, 2, 1, 2023311001, 8.7, 1),
    (3110201, 2023, 2, 1, 2023311002, 6.1, 1),
    (3110202, 2023, 2, 1, 2023311002, 6.3, 1),
    (3110401, 2024, 2, 1, 2023311002, 9.0, 1),
    (3110402, 2024, 2, 1, 2023311002, 8.6, 1),
    (3110601, 2025, 2, 1, 2023311002, 0.3, 0),
    (3110602, 2025, 2, 1, 2023311002, 7.7, 1),
    (3110201, 2024, 2, 1, 2024311002, 4.3, 0),
    (3110202, 2024, 2, 1, 2024311002, 7.2, 1),
    (3110401, 2025, 2, 1, 2024311002, 7.7, 1),
    (3110402, 2025, 2, 1, 2024311002, 0.4, 0),
    (3110201, 2025, 2, 1, 2025311001, 6.1, 1),
    (3110202, 2025, 2, 1, 2025311001, 6.8, 1),
    (3110201, 2025, 2, 1, 2025311002, 1.9, 0),
    (3110202, 2025, 2, 1, 2025311002, 6.1, 1),
    (1110201, 2023, 2, 1, 2023111001, 9.8, 1),
    (1110202, 2023, 2, 1, 2023111001, 8.8, 1),
    (1110201, 2024, 2, 1, 2024111001, 7.9, 1),
    (1110202, 2024, 2, 1, 2024111001, 8.5, 1),
    (1110401, 2025, 2, 1, 2024111001, 6.1, 1),
    (1110402, 2025, 2, 1, 2024111001, 6.5, 1),
    (1110201, 2024, 2, 1, 2024111002, 3.4, 0),
    (1110202, 2024, 2, 1, 2024111002, 6.4, 1),
    (1110401, 2025, 2, 1, 2024111002, 6.1, 1),
    (1110402, 2025, 2, 1, 2024111002, 7.7, 1),
    (1110201, 2025, 2, 1, 2025111001, 7.1, 1),
    (1110202, 2025, 2, 1, 2025111001, 6.1, 1),
    (1110201, 2025, 2, 1, 2025111002, 6.1, 1),
    (1110202, 2025, 2, 1, 2025111002, 9.9, 1),
    (2110201, 2023, 2, 1, 2023211001, 8.4, 1),
    (2110202, 2023, 2, 1, 2023211001, 8.2, 1),
    (2110401, 2024, 2, 1, 2023211001, 8.5, 1),
    (2110402, 2024, 2, 1, 2023211001, 8.8, 1),
    (2110601, 2025, 2, 1, 2023211001, 6.1, 1),
    (2110602, 2025, 2, 1, 2023211001, 6.2, 1),
    (2110201, 2023, 2, 1, 2023211002, 6.3, 1),
    (2110202, 2023, 2, 1, 2023211002, 6.8, 1),
    (2110401, 2024, 2, 1, 2023211002, 6.1, 1),
    (2110402, 2024, 2, 1, 2023211002,10.0, 1),
    (2110601, 2025, 2, 1, 2023211002, 1.3, 0),
    (2110602, 2025, 2, 1, 2023211002, 6.1, 1),
    (2110201, 2024, 2, 1, 2024211001, 7.0, 1),
    (2110202, 2024, 2, 1, 2024211001, 9.7, 1),
    (2110401, 2025, 2, 1, 2024211001, 9.2, 1),
    (2110402, 2025, 2, 1, 2024211001, 6.1, 1),
    (2110201, 2024, 2, 1, 2024211002, 7.1, 1),
    (2110202, 2024, 2, 1, 2024211002, 8.7, 1),
    (2110401, 2025, 2, 1, 2024211002, 7.6, 1),
    (2110402, 2025, 2, 1, 2024211002, 7.6, 1),
    (2110201, 2025, 2, 1, 2025211001, 6.1, 1),
    (2110202, 2025, 2, 1, 2025211001, 7.0, 1),
    (2110201, 2025, 2, 1, 2025211002, 8.4, 1),
    (2110202, 2025, 2, 1, 2025211002, 7.4, 1);

Outra opção, no PostgreSQL, é utilizar o comando COPY para importar os dados diretamente de arquivos CSV. Por exemplo:

COPY matricula_disciplina (id_disciplina, ano, semestre, turma, id_aluno, nota, aprovado)
FROM '/caminho/para/matricula_disciplina.csv' DELIMITER ';' CSV HEADER;

Verificando os Dados

Todos os cursos

SELECT id_curso, sigla_curso, nome_curso, id_escola, tipo_curso
FROM curso
ORDER BY id_curso;
3 records
id_curso sigla_curso nome_curso id_escola tipo_curso
111 ECO Ciências Econômicas 11 bacharelado
211 PED Pedagogia 21 licenciatura
311 ENS Engenharia de Software 31 bacharelado

Professores por escola

SELECT id_escola, COUNT(*) AS n_professores
FROM professor
GROUP BY id_escola
ORDER BY id_escola;
3 records
id_escola n_professores
11 2
21 2
31 3

Disciplinas com carga horária acima de 100 h

SELECT id_disciplina, nome_disciplina, id_curso, carga_horaria
FROM disciplina
WHERE carga_horaria > 100
ORDER BY carga_horaria DESC;
7 records
id_disciplina nome_disciplina id_curso carga_horaria
3110401 Bases da Eng. de Software 4 311 180
3110802 Projeto Aplicado 8 - Sistema em Tempo Real 311 160
3110601 Bases da Eng. de Software 6 311 150
3110201 Bases da Eng. de Software 2 311 150
3110402 Projeto Aplicado 4 - blockchain 311 120
3110202 Projeto Aplicado 2 - site WEB 311 120
3110602 Projeto Aplicado 6 - Machine Learning 311 120

Alunos de Engenharia de Software ingressantes em 2023

SELECT id_aluno, nome, ano_ingresso
FROM aluno
WHERE id_curso = 311 AND ano_ingresso = 2023;
2 records
id_aluno nome ano_ingresso
2023311001 Matheus Silva 2023
2023311002 Larissa Araújo 2023
DicaDesfazendo tudo (reset)

Para remover todas as tabelas e recriar do zero:

DROP TABLE IF EXISTS matricula_disciplina, ministra, prereq,
                     disciplina, aluno, professor,
                     curso, escola, centro CASCADE;