Construir tabelas eficazes

Concluído

Um design eficaz de tabelas constitui a base de qualquer base de dados. As tabelas estruturam os seus dados e determinam quão eficientemente as suas consultas acedem e modificam a informação.

Desenhar e criar tabelas

As tabelas são os blocos fundamentais das bases de dados relacionais, organizando os dados em linhas e colunas que representam entidades e os seus atributos. Em sistemas relacionais, as tabelas definem a estrutura para armazenar dados transacionais, reforçam relações através de chaves estrangeiras e fornecem a base para consultas e relatórios.

Para análises multidimensionais, as tabelas servem como tabelas de factos que armazenam eventos mensuráveis e tabelas de dimensões que fornecem contexto para análise. As decisões de design que toma ao criar tabelas — tipos de dados, tamanho das colunas, restrições e relações — impactam diretamente a eficiência do armazenamento, desempenho das consultas, integridade dos dados e escalabilidade tanto em cargas de trabalho operacionais como analíticas.

Escolha os tipos de dados apropriados

Os tipos de dados são decisões fundamentais que afetam a sua base de dados. A escolha errada pode levar ao desperdício de armazenamento, baixo desempenho, perda de dados ou erros de aplicação. Ao contrário do código de aplicação que se pode refatorar facilmente, mudar os tipos de dados das colunas em bases de dados de produção muitas vezes requer reconstruções de tabelas, o que pode significar horas de inatividade para tabelas grandes.

Selecione os tipos de dados corretos quando desenhar o esquema inicial, pois este é o momento mais fácil para acertar. Além disso, considere cuidadosamente os tipos de dados quando:

  • Estás a armazenar dados onde a precisão importa
  • Está a trabalhar com tabelas de alto volume onde os custos de armazenamento se multiplicam
  • Estás a definir colunas frequentemente consultadas que têm um desempenho mais rápido com tipos mais pequenos

Explorar tipos de dados comuns

Os tipos de dados apropriados afetam o armazenamento, o desempenho e as operações:

Categoria de Tipo Tipos de dados Tamanho de armazenamento Diretrizes de uso Example
Numérico INT, BIGINT, DECIMAL, FLOAT 4 bytes, 8 bytes, varia Escolha com base no alcance e nas necessidades de precisão Quantity INT, Revenue DECIMAL(10,2), Population BIGINT
String VARCHAR, CHAR, NVARCHAR 1 byte/caráter, fixo, 2 bytes/caráter Uso VARCHAR para dados de comprimento variável, CHAR para comprimento fixo, NVARCHAR para Unicode Email VARCHAR(100), CountryCode CHAR(2), ProductName NVARCHAR(100)
Data/Hora DATE, DATETIME2, DATETIMEOFFSET 3 bytes, 6-8 bytes, 10 bytes DATETIME2 proporciona melhor precisão do que DATETIME BirthDate DATE, OrderTimestamp DATETIME2, EventTime DATETIMEOFFSET
Binário VARBINARY, IMAGE varia Para armazenar dados binários como imagens ou documentos ProfilePhoto VARBINARY(MAX), DocumentContent VARBINARY(MAX)
Especial UNIQUEIDENTIFIER, XML, JSON 16 bytes, varia, binário nativo UNIQUEIDENTIFIER para GUIDs, XML para documentos JSON XML (SQL 2025+) para documentos JSON em formato binário nativo RowGUID UNIQUEIDENTIFIER, Config XML, Settings JSON

As nuances dos tipos de dados exigem atenção cuidadosa. Por exemplo, usar FLOAT para dados financeiros em vez de DECIMAL pode introduzir erros de arredondamento que não podem ser corrigidos sem recalcular todos os valores dependentes. Uma UNIQUEIDENTIFIER chave primária, quando INT é suficiente, triplica o tamanho do índice e atrasa toda a JOIN operação. A maioria destas decisões afeta o desempenho da base de dados e pode determinar se as consultas são executadas em milissegundos ou minutos.

Requisitos de estimativa do tamanho da tabela

O tamanho da tabela não se resume apenas aos custos de armazenamento; e impacta diretamente as operações da sua base de dados. O tamanho da tabela afeta os tempos de backup e restauro, a duração da reconstrução do índice e o desempenho das consultas.

Importante

Uma tabela mal desenhada que armazena 200 bytes por linha em vez de 100 bytes duplica as tuas necessidades de armazenamento, tempos de backup e requisitos de I/O.

Outro cenário para planear o tamanho das tabelas é quando se calculam custos de armazenamento para bases de dados na cloud, se projetam para espaço limitado em disco ou se planeiam estratégias de arquivo. Estes cenários exigem estimativas precisas do tamanho para tomar decisões informadas sobre recursos e operações.

Por exemplo, uma empresa de retalho que armazena 100 milhões de transações diárias com mais 50 bytes por linha desperdiça 5 GB por dia — isso corresponde a 1,8TB anuais de armazenamento desnecessário, mais aumentos proporcionais no tempo e custos de backup.

O exemplo seguinte mostra como estimar o tamanho da Employee tabela:

-- Estimate row size for a table
-- Fixed-length columns: sum of column sizes
-- Variable-length: estimate average size
-- Example row calculation:
CREATE TABLE Employee (
    EmployeeID INT,              -- 4 bytes
    FirstName NVARCHAR(50),      -- ~2-100 bytes (avg 40)
    LastName NVARCHAR(50),       -- ~2-100 bytes (avg 40)
    HireDate DATE,               -- 3 bytes
    Salary DECIMAL(10,2)         -- 5 bytes
);  
-- Estimated row size: 4 + 40 + 40 + 3 + 5 = ~92 bytes
-- Plus row overhead (~7 bytes) = ~99 bytes per row
-- 1 million rows ≈ 94 MB

Sugestão

Podes usar o Copilot para te ajudar a gerar a estimativa do tamanho da tabela.

Conceber colunas eficazes

O exemplo seguinte demonstra uma tabela bem desenhada Product que aplica os princípios abordados nesta unidade:

CREATE TABLE Product (
    ProductID INT PRIMARY KEY IDENTITY(1,1),       -- Auto-incrementing surrogate key (4 bytes)
    ProductName NVARCHAR(100) NOT NULL,             -- Unicode support, appropriate length, enforced
    Category NVARCHAR(50) NOT NULL,                 -- Smaller than ProductName (categorization needs less space)
    Price DECIMAL(10,2) NOT NULL,                   -- Exact precision for financial data
    StockQuantity INT NOT NULL DEFAULT 0,           -- Integer sufficient for inventory, default prevents nulls
    LastRestocked DATETIME2 DEFAULT GETUTCDATE()    -- Modern date type with automatic timestamp
);

Esta tabela demonstra várias boas práticas:

  • Tipos de dados apropriados: INT para a chave primária (menor que BIGINT ou UNIQUEIDENTIFIER), DECIMAL(10,2) para cálculos financeiros precisos em vez de FLOAT, DATETIME2 para melhor precisão do que legado DATETIME
  • Colunas do tamanho certo: NVARCHAR(100) para nomes de produtos e NVARCHAR(50) para categorias baseadas no comprimento esperado dos dados
  • Restrições: NOT NULL garante a qualidade dos dados ao evitar a ausência de valores críticos
  • Valores padrão: Valores automáticos para StockQuantity (0) e LastRestocked (tempo UTC atual) reduzem a complexidade do código da aplicação
  • Chave primária eficiente: IDENTITY gera chaves sequenciais que agrupam de forma eficiente e usam armazenamento mínimo (4 bytes contra 16 bytes para GUID)

Observação

Este exemplo utiliza NVARCHAR (2 bytes por carácter) para suporte Unicode. Se os seus dados forem apenas ASCII, VARCHAR (1 byte por carácter) reduz para metade o armazenamento de strings. A ProductName VARCHAR(100) usa ~30 bytes contra ~60 bytes para NVARCHAR(100) num nome de 30 caracteres. Em 10 milhões de linhas, isto poupa aproximadamente 300 MB. Use NVARCHAR para dados internacionais; utilize VARCHAR quando a eficiência de armazenamento for relevante e os dados permanecerão apenas em ASCII.

Melhores práticas de design

Aplique estes princípios-chave ao desenhar e implementar tabelas para garantir desempenho e manutenção:

  • Use tipos de dados adequados - Tipos de dados mais pequenos reduzem o armazenamento e melhoram o desempenho
  • Considere o tamanho da tabela cedo - Estime o tamanho das linhas e o tamanho total da tabela para planear o armazenamento e a indexação
  • Implementar restrições significativas - Garantir a qualidade dos dados ao nível da base de dados
  • Planeie o crescimento - Desenhe tabelas para gerir o volume futuro de dados
  • Indexar estrategicamente - Colunas de índice usadas em cláusulas WHERE, JOIN e ORDER BY
  • Escolha columnstore para análises - Utilize índices columnstore para consultas analíticas em tabelas grandes
  • Normalizar quando apropriado - Equilibrar a normalização com as necessidades de desempenho da consulta
  • Monitorizar compressão de linhas e páginas - Permitir compressão em tabelas grandes para poupar espaço de armazenamento

A maioria dos problemas de desempenho das bases de dados resulta de decisões de design inadequadas tomadas no início do desenvolvimento. Tipos de dados sobredimensionados desperdiçam armazenamento e tornam as consultas lentas. Tipos de índice em falta ou errados criam gargalos que as melhorias de recursos não conseguem resolver. Evite estes problemas investindo tempo no design adequado dos objetos antes de criar ou modificar tabelas. As decisões que toma durante o desenho — escolher os tipos de dados adequados, estimar o tamanho das tabelas, selecionar os tipos de índice certos — têm um efeito muito maior no desempenho e no custo a longo prazo do que qualquer otimização que possa aplicar mais tarde.