◇ OpenCyber Academy
Tecnólogo · Capítulo 4 · Português

Capítulo 4 — Modelos de dados

Ensino gratuito e aberto para quem deseja proteger pessoas e tecnologias. Não é preciso criar uma conta para estudar ou consultar as respostas.

Capítulo 4 — Modelos de dados

Mapa da aula

Aula 04 publicada. Mapa, exemplo, EX010.

flowchart TD
  N0["4.1 Entidades, atributos e chaves"]
  N1["4.2 Restrições: tornar impossíveis os registros impo"]
  N2["4.3 Valores ausentes são constatações"]
  N3["4.4 As junções respondem às perguntas reais"]
  N4["4.6 Atividade prática: consultas ao inventário"]
  N5["Síntese do capítulo"]
  N0 --> N1 --> N2 --> N3 --> N4 --> N5
  N5 --> EX["Exemplo resolvido"]
  EX --> Q["Exercício EX010"]

Desenho lógico

flowchart LR
  N0["4.1 Entidades, atributos e chaves"]
  N1["4.2 Restrições: tornar impossíveis os registros impo"]
  N2["4.3 Valores ausentes são constatações"]
  N3["4.4 As junções respondem às perguntas reais"]
  N4["4.6 Atividade prática: consultas ao inventário"]
  N5["Síntese do capítulo"]
  N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> Q["EX010"]
[4.1 Entidades, atributos e chaves]
    |
    v
[4.2 Restrições: tornar impossíveis os registros impo]
    |
    v
[4.3 Valores ausentes são constatações]
    |
    v
[4.4 As junções respondem às perguntas reais]
    |
    v
[4.6 Atividade prática: consultas ao inventário]
    |
    v
[Síntese do capítulo]
    |
    v
[Exemplo resolvido] --> [Exercício EX010]

Objetivos de aprendizagem e conexão

Depois deste capítulo, você deve ser capaz de:

Todos os capítulos seguintes dependem de registros: inventários, registros de fluxos de dados, registros de riscos, listas de acesso, logs de evidências. Quando eles vivem em planilhas editadas por muitas pessoas, eles se deterioram — linhas duplicadas, responsáveis em texto livre, "A CONFIRMAR" em campos de data. Este capítulo mostra a modelagem de dados mínima de que um implementador precisa para que os registros operacionais continuem confiáveis.

4.1 Entidades, atributos e chaves

Um modelo de dados diz que tipos de coisas os registros descrevem (entidades: ativo, responsável, contrato de suporte), o que é registrado sobre cada uma (atributos: modelo, firmware, local, criticidade) e como elas se relacionam. Cada entidade tem uma chave primária — um valor que identifica exatamente um registro, como a etiqueta de um ativo. Uma chave estrangeira numa tabela aponta para a chave primária de outra: o owner_id de um ativo se refere a uma linha da tabela de responsáveis. Essa única escolha de projeto substitui nomes de responsáveis em texto livre, que derivam ("Water Ops", "water operations", "A. Okafor (saiu)"), por uma referência que ou aponta para um responsável real, ou não aponta.

4.2 Restrições: tornar impossíveis os registros impossíveis

Uma restrição é uma regra que o banco de dados aplica a cada gravação. NOT NULL diz que um valor precisa estar presente; CHECK (criticality BETWEEN 1 AND 3) diz que só esses valores são permitidos; uma chave estrangeira diz que a linha referenciada precisa existir (no SQLite, a aplicação das chaves estrangeiras precisa ser ligada em cada conexão, um detalhe que vale conhecer porque surpreende as pessoas). As restrições levam a validação de "alguém deveria conferir" para "o sistema recusa". Elas são a validação de entradas do Capítulo 3 aplicada a registros.

No Laboratório 4, a última consulta é exatamente isso: inserir um ativo com criticidade 7 falha no banco de dados, seja quem for que escreveu o script.

4.3 Valores ausentes são constatações

Num inventário, um valor ausente não é um espaço em branco; é uma afirmação de que algo é desconhecido. Um ativo sem responsável é um ativo que ninguém vai corrigir, revisar ou desativar. Um ativo com firmware desconhecido é um cujas vulnerabilidades não podem ser avaliadas. Por isso, as consultas devem procurar pelos valores ausentes, e não ignorá-los. O NULL do SQL se comporta de modo especial: owner_id = NULL nunca é verdadeiro; é preciso escrever owner_id IS NULL. Muitos erros silenciosos de relatório vêm de esquecer isso.

O ativo de criticidade 1 sem responsável do Laboratório 4, a RTU da PS-09, só aparece porque a consulta pergunta por ele.

4.4 As junções respondem às perguntas reais

As perguntas operacionais costumam abranger várias tabelas. "Quais ativos rodam num modelo cujo suporte do fornecedor terminou?" exige a tabela de ativos unida à tabela de suporte pelo modelo. "Quantos ativos de criticidade 1 cada responsável tem?" exige os ativos unidos aos responsáveis, com uma junção à esquerda (left join), para que os ativos sem responsável apareçam como "(none)" em vez de sumirem da contagem. A diferença entre uma junção interna e uma junção à esquerda costuma ser a diferença entre um relatório que esconde um problema e um que o mostra.

4.5 Exemplo resolvido: extrato do inventário da FSTS (sintético)

O Laboratório 4 constrói um pequeno inventário num banco SQLite em memória (o módulo sqlite3 do Python, documentação) e faz cinco perguntas. Cada resposta leva a uma ação. A RTU da PS-09 não tem responsável, é de criticidade 1, e o suporte do fornecedor ao seu modelo terminou em 2025-12-31: três motivos para agir, sobre o mesmo ativo, que uma planilha ordenada por local nunca teria reunido. O switch da estação PS-04 tem firmware desconhecido. Dois servidores rodam versões de sistema operacional que estão na própria lista de aposentadoria por política da FSTS. A consulta de criticidade por responsável mostra um ativo de criticidade 1 que não pertence a ninguém. Por fim, uma tentativa de inserir um ativo com criticidade 7 é recusada pela restrição CHECK.

4.6 Atividade prática: consultas ao inventário

Laboratório 4 (salve como lab04.py, execute com python3 lab04.py):

# Lab 4 - an asset inventory as a relational model (offline, standard library only: sqlite3)
# Synthetic: Fallowmere Shared Technology Services inventory extract.
import sqlite3
db = sqlite3.connect(":memory:")
db.executescript("""
CREATE TABLE owner(id INTEGER PRIMARY KEY, name TEXT NOT NULL, service TEXT NOT NULL);
CREATE TABLE asset(id TEXT PRIMARY KEY, kind TEXT NOT NULL, model TEXT, firmware TEXT,
  site TEXT NOT NULL, owner_id INTEGER REFERENCES owner(id), criticality INTEGER CHECK (criticality BETWEEN 1 AND 3));
CREATE TABLE support(model TEXT PRIMARY KEY, vendor_end TEXT);
INSERT INTO owner VALUES (1,'Water Ops','drinking water'),(2,'Clinical Eng','hospital'),(3,'Depot IT','transit');
INSERT INTO asset VALUES
 ('PS04-PLC','plc','Arlen 300','4.2','PS-04',1,1),('PS07-PLC','plc','Arlen 300','3.9','PS-07',1,1),
 ('PS09-RTU','rtu','Kell R2','1.1','PS-09',NULL,1),('RLY-01','server','generic x86','deb12','Hub',1,1),
 ('HIST-01','server','generic x86','win2016','Hub',1,2),('INF-PUMP-12','infusion','Moro IP5','2.0','Ward 3',2,1),
 ('PACS-GW','server','generic x86','win2012r2','Hospital',2,2),('DEPOT-KIOSK','kiosk','Tella K1','8.1','Depot',3,3),
 ('SW-STN-04','switch','Brack 24',NULL,'PS-04',1,2);
INSERT INTO support VALUES ('Arlen 300','2027-06-30'),('Kell R2','2025-12-31'),('Moro IP5','2029-01-01'),('Tella K1','2026-03-31');
""")
q = {
 "no owner": "SELECT id, site FROM asset WHERE owner_id IS NULL",
 "unknown firmware": "SELECT id, site FROM asset WHERE firmware IS NULL",
 "vendor support ended (as of 2026-10-12)":
   "SELECT a.id, s.vendor_end FROM asset a JOIN support s ON a.model=s.model WHERE s.vendor_end < '2026-10-12'",
 "OS on the service's retire-by-policy list (synthetic)": "SELECT id, firmware FROM asset WHERE firmware IN ('win2012r2','win2016')",
 "criticality-1 assets by owner": """SELECT COALESCE(o.name,'(none)'), COUNT(*) FROM asset a LEFT JOIN owner o
   ON a.owner_id=o.id WHERE a.criticality=1 GROUP BY o.name ORDER BY 2 DESC""",
}
for title, sql in q.items():
    print(f"-- {title}")
    for row in db.execute(sql):
        print("  ", row)
try:
    db.execute("INSERT INTO asset VALUES ('X','plc','m','1','PS-01',1,7)")
except sqlite3.IntegrityError as e:
    print("constraint rejected bad row:", e)

Saída esperada (Python 3.13.5):

-- no owner
   ('PS09-RTU', 'PS-09')
-- unknown firmware
   ('SW-STN-04', 'PS-04')
-- vendor support ended (as of 2026-10-12)
   ('PS09-RTU', '2025-12-31')
   ('DEPOT-KIOSK', '2026-03-31')
-- OS on the service's retire-by-policy list (synthetic)
   ('HIST-01', 'win2016')
   ('PACS-GW', 'win2012r2')
-- criticality-1 assets by owner
   ('Water Ops', 3)
   ('Clinical Eng', 1)
   ('(none)', 1)
constraint rejected bad row: CHECK constraint failed: criticality BETWEEN 1 AND 3

Leia a saída como uma lista de trabalho, e não como um relatório. A RTU da PS-09 é a primeira linha de trabalho, porque três constatações se encontram num só ativo: sem responsável, criticidade 1 e suporte do fornecedor encerrado em 2025-12-31. O firmware desconhecido do switch da estação PS-04 vira uma pergunta para o engenheiro de Água, e não um palpite. Os dois servidores na lista de aposentadoria por política da FSTS vão para o responsável pela plataforma, com as suas datas de substituição. Por fim, confira a inserção recusada: se a linha de criticidade 7 tivesse sido aceita, a aplicação das chaves estrangeiras e do CHECK não estaria ativa naquela conexão, e todas as respostas anteriores mereceriam menos confiança.

4.7 Erros comuns e limites

Um modelo de dados não pode dizer que um ativo existe se ninguém o registrou. Ele pode garantir que o que está registrado seja consistente, e que as lacunas fiquem visíveis.

Exercícios — capítulo 4

EX010 — Encontre o ativo escondido. Objetivo: ver uma junção interna esconder um problema. Entradas: Laboratório 4. Restrições: altere apenas a última consulta. Etapas: (1) troque LEFT JOIN por JOIN na consulta de criticidade por responsável; (2) execute e compare; (3) explique em duas frases qual ativo sumiu e por que isso importa para um relatório de cobertura. Evidência esperada: as duas saídas e a explicação. Rubrica: 3 = a linha da PS09-RTU some, a contagem total de criticidade 1 cai de 5 para 4, explicação ligada a ativos sem responsável sumindo dos relatórios gerenciais; 2 = observado, explicação fraca; 1 = não executado; 0 = nada.

EX011 — Acrescente uma restrição e uma tabela. Objetivo: estender o modelo com segurança. Entradas: Laboratório 4. Restrições: apenas a biblioteca padrão. Etapas: (1) acrescente uma tabela review (id do ativo, data da revisão, revisor, resultado) com uma chave estrangeira para asset e um CHECK de que o resultado é um de ok, issue, not-reachable; (2) habilite as chaves estrangeiras com PRAGMA foreign_keys = ON; (3) mostre que inserir uma revisão para um ativo inexistente falha; (4) escreva uma consulta que liste os ativos de criticidade 1 sem revisão nos últimos 90 dias. Evidência esperada: código e saída. Rubrica: 3 = restrição e chave estrangeira aplicadas, a consulta usa uma junção à esquerda ou NOT EXISTS para que os ativos nunca revisados apareçam; 2 = funciona, mas perde os ativos nunca revisados; 1 = só o esquema; 0 = nada.

EX012 — Da planilha ao modelo. Objetivo: reconhecer falhas de qualidade de dados. Entradas: um conjunto sintético de linhas de planilha: PS-03 PLC | owner: Water Ops | fw: 4.2 | support: Jun 2027; PS-03 plc | owner: water operations | fw: ? | support: 30/06/27; PS-05 PLC | owner: A. Okafor (left) | fw: 4.1 | support: TBC. Restrições: nenhum código é necessário. Etapas: (1) liste todas as falhas de qualidade de dados; (2) diga qual restrição ou escolha de projeto, na orientação do Capítulo 4 sobre chaves, restrições e valores ausentes, impede cada uma. Evidência esperada: tabela. Rubrica: 3 = ativo duplicado com caixa inconsistente, variantes de responsável em texto livre, uma pessoa em vez de um papel como responsável e alguém que saiu, firmware desconhecido como "?", formatos de data misturados, "TBC" — cada um mapeado para chave primária, chave estrangeira, NOT NULL/desconhecido explícito, datas ISO; 2 = a maioria encontrada; 1 = poucas; 0 = nada.

Síntese do capítulo

Os registros operacionais só são úteis se continuarem consistentes. Modele-os como entidades com chaves, ligue-os com chaves estrangeiras em vez de texto livre e deixe as restrições recusarem os registros impossíveis. Trate os valores ausentes como constatações, consulte-os explicitamente e use junções à esquerda em qualquer relatório sobre cobertura. Um bom modelo torna as lacunas visíveis; ele não consegue encontrar ativos que ninguém registrou.

Continue estudando — capítulo 04 de 30

← Capítulo 03 · Índice de capítulos do curso · Capítulo 05 →

Siga para a próxima lição escrita.