0Pricing
SQL Academy · Aula

Matrizes versus tabelas normalizadas

Saiba quando as matrizes são a escolha certa.

Matrizes versus tabelas normalizadas é uma aula grátis de SQL Academy no CoddyKit. Esta é a aula 4 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de SQL Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de SQL Academy inclui 4 aulas no total.

Duas formas de armazenar vários valores

Quando uma única linha precisa conter vários valores relacionados, PostgreSQL oferece duas abordagens principais: armazená-los como uma coluna de matriz na mesma linha ou criar uma tabela filha em que cada valor recebe sua própria linha.

Compreender quando usar cada abordagem é uma habilidade essencial para projetar bancos de dados eficientes e fáceis de manter.

A abordagem normalizada

Em um esquema totalmente normalizado, cada parte dos dados fica em sua própria linha. Se um usuário puder ter vários números de telefone, crie uma tabela user_phones com uma chave estrangeira que a relacione a users.

Este é o modelo relacional clássico e a escolha padrão na maioria das situações.

CREATE TABLE users (
  id   SERIAL PRIMARY KEY,
  name TEXT NOT NULL
);

CREATE TABLE user_phones (
  id      SERIAL PRIMARY KEY,
  user_id INT REFERENCES users(id),
  phone   TEXT NOT NULL
);

INSERT INTO users (name) VALUES ('Alice'), ('Bob');

INSERT INTO user_phones (user_id, phone) VALUES
  (1, '+1-555-0101'),
  (1, '+1-555-0102'),
  (2, '+1-555-0200');

A abordagem com matrizes

O TEXT[] do PostgreSQL (ou qualquer outro tipo seguido de []) permite armazenar vários valores diretamente em uma única coluna. Não é necessária nenhuma tabela extra.

Os mesmos dados de números de telefone podem ser armazenados em uma única linha compacta por usuário.

CREATE TABLE users_with_phones (
  id     SERIAL PRIMARY KEY,
  name   TEXT    NOT NULL,
  phones TEXT[]
);

INSERT INTO users_with_phones (name, phones) VALUES
  ('Alice', ARRAY['+1-555-0101', '+1-555-0102']),
  ('Bob',   ARRAY['+1-555-0200']);

É fácil consultar matrizes

Pesquisar dentro de uma coluna de matriz é simples com o operador ANY ou o operador @> (contém). Você pode encontrar todos os usuários que têm um número de telefone específico com uma simples cláusula WHERE.

-- Find users who have a specific phone number
SELECT name
FROM users_with_phones
WHERE '+1-555-0101' = ANY(phones);

-- Or using the array-contains operator
SELECT name
FROM users_with_phones
WHERE phones @> ARRAY['+1-555-0101'];

Quando as matrizes são melhores: consultas simples

As matrizes são uma boa escolha quando:

  • A lista de valores é lida em conjunto como uma unidade (etiquetas, rótulos, categorias)
  • Você nunca precisa fazer uma junção com base em elementos individuais
  • A lista tem um limite superior natural e raramente é atualizada em partes

Um exemplo clássico é armazenar etiquetas em uma publicação de blog. Você sempre busca todas as etiquetas de uma só vez e raramente consulta publicações por uma única etiqueta em uma junção complexa.

CREATE TABLE posts (
  id         SERIAL PRIMARY KEY,
  title      TEXT    NOT NULL,
  tags       TEXT[]
);

INSERT INTO posts (title, tags) VALUES
  ('Intro to SQL',      ARRAY['sql', 'beginner', 'database']),
  ('Advanced Indexes',  ARRAY['sql', 'performance', 'indexes']),
  ('NoSQL Overview',    ARRAY['nosql', 'beginner']);

-- Get all posts tagged 'beginner'
SELECT title FROM posts
WHERE 'beginner' = ANY(tags);

Quando as tabelas normalizadas são melhores: relacionamentos

As tabelas normalizadas são a melhor escolha quando:

  • Os valores individuais precisam de seus próprios atributos (por exemplo, um número de telefone tem um tipo: residencial/comercial)
  • Você precisa fazer uma junção com base em valores individuais
  • Os valores mudam independentemente e com frequência
  • Você precisa de integridade referencial por meio de chaves estrangeiras
-- Phone numbers need a 'type' attribute — array can't do this cleanly
CREATE TABLE user_phones (
  id      SERIAL PRIMARY KEY,
  user_id INT REFERENCES users(id),
  phone   TEXT NOT NULL,
  type    TEXT CHECK (type IN ('home', 'work', 'mobile'))
);

INSERT INTO user_phones (user_id, phone, type) VALUES
  (1, '+1-555-0101', 'home'),
  (1, '+1-555-0102', 'work');

A diferença na indexação

Com uma tabela normalizada, você pode adicionar um índice padrão de árvore B na chave estrangeira ou na coluna de valores. Com matrizes, você precisa de um índice GIN (índice invertido generalizado) para permitir pesquisas rápidas dentro da matriz.

Os índices GIN funcionam bem, mas são maiores e mais lentos para atualizar do que os índices de árvore B.

-- Index for fast array element lookups
CREATE INDEX idx_posts_tags ON posts USING GIN (tags);

-- Now this query uses the index efficiently
EXPLAIN SELECT title FROM posts
WHERE tags @> ARRAY['sql'];

Agregação entre linhas: tabelas normalizadas são melhores

Quando você precisa contar, agrupar ou agregar valores individuais, as tabelas normalizadas são muito mais naturais. Agregar dentro de matrizes exige unnest(), que primeiro expande a matriz em linhas — recriando essencialmente a estrutura normalizada no momento da consulta.

-- Count posts per tag (array approach — needs unnest)
SELECT tag, COUNT(*) AS post_count
FROM posts, unnest(tags) AS tag
GROUP BY tag
ORDER BY post_count DESC;

-- With a normalized post_tags table this would be simpler:
-- SELECT tag, COUNT(*) FROM post_tags GROUP BY tag;

Modificando elementos de uma matriz

Atualizar ou excluir um único elemento dentro de uma matriz exige uma sintaxe pouco prática — é preciso substituir a matriz inteira ou usar array_remove(). Em uma tabela normalizada, basta executar DELETE ou UPDATE na linha específica.

-- Remove a single tag from an array column
UPDATE posts
SET tags = array_remove(tags, 'beginner')
WHERE id = 1;

-- Append a new tag
UPDATE posts
SET tags = array_append(tags, 'tutorial')
WHERE id = 1;

SELECT title, tags FROM posts WHERE id = 1;

Garantindo valores válidos

Em uma tabela normalizada, você pode usar uma chave estrangeira para garantir que cada valor venha de um conjunto conhecido. As matrizes não podem fazer referência a outra tabela — não têm suporte a chaves estrangeiras.

Se você precisar de integridade referencial garantida para cada elemento, uma tabela filha é a única opção.

-- Normalized: only valid category IDs allowed (FK enforced)
CREATE TABLE categories (
  id   SERIAL PRIMARY KEY,
  name TEXT UNIQUE NOT NULL
);

CREATE TABLE post_categories (
  post_id     INT REFERENCES posts(id),
  category_id INT REFERENCES categories(id),
  PRIMARY KEY (post_id, category_id)
);

-- Array: no constraint possible — any text value is accepted
-- UPDATE posts SET tags = ARRAY['totally_invalid_tag'] WHERE id = 1;

Um guia prático para decidir

Use uma matriz quando os dados forem uma lista simples e plana, sempre lida como uma unidade, sem atributos adicionais por elemento, e a integridade referencial não for necessária (por exemplo, etiquetas, rótulos, palavras-chave de pesquisa).

Use uma tabela filha normalizada quando cada elemento tiver seus próprios atributos, você fizer junções ou agregações sobre valores individuais, precisar de chaves estrangeiras ou atualizar ou excluir elementos individuais com frequência.

-- Summary example: tags as array (good fit)
SELECT title, tags
FROM posts
WHERE tags @> ARRAY['sql']
ORDER BY title;

-- Unnest when you need row-level processing
SELECT title, unnest(tags) AS tag
FROM posts
ORDER BY title, tag;

Verificação rápida

Qual cenário é o mais adequado para armazenar dados como uma matriz do PostgreSQL em vez de uma tabela filha normalizada?

Recapitulação da lição

Nesta lição, você aprendeu os principais compromissos entre matrizes e tabelas normalizadas no PostgreSQL.

  • Matrizes são compactas e convenientes para listas simples lidas como uma unidade, como etiquetas — mas não têm chaves estrangeiras, tornam difíceis as atualizações por elemento e exigem índices GIN para pesquisas rápidas.
  • Tabelas normalizadas permitem atributos por elemento, integridade por chave estrangeira, agregação eficiente e atualizações simples no nível da linha — ao custo de uma junção adicional.
  • A escolha certa depende de como você consulta, atualiza e relaciona os dados — não apenas de como os armazena.

Perguntas Frequentes

A aula “Matrizes versus tabelas normalizadas” é grátis?

Sim — o texto completo de “Matrizes versus tabelas normalizadas” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de SQL Academy, atualize para CoddyKit PRO. O curso de SQL Academy inclui 4 aulas no total.

O que vou aprender em “Matrizes versus tabelas normalizadas”?

Saiba quando as matrizes são a escolha certa. Você pratica SQL Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar SQL Academy?

Nenhuma experiência prévia é necessária. SQL Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 4 de 4.

Quanto tempo leva a aula “Matrizes versus tabelas normalizadas”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de SQL Academy?

Sim. Cada aula de SQL Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Noções básicas de colunas de matriz
  2. Pesquisando dentro de matrizes
  3. UNNEST e agregação
  4. Matrizes versus tabelas normalizadas
← Voltar para SQL Academy