Introdução
A coluna de retirada da loja já foi expandida, com counter como padrão legado. Os pedidos históricos ainda precisam de suas escolhas aprovadas. Pedidos posteriores já chegaram: um omitiu a nova coluna, e outro escolheu garden explicitamente. Um preenchimento abrangente poderia sobrescrever essa escolha legítima mais recente.
Você entregará backfill.sql e tighten.sql, aplicará o mapeamento histórico aprovado exato e preservará os contratos dos dois clientes. Os pré-requisitos são os conceitos do lab anterior, além de SELECT/WHERE e UPDATE básicos; os padrões de JOIN, agregação, proteção temporária e transação são ensinados aqui. Esta unidade independente de Terminal tem sua própria tabela expandida, dados e MySQL privado. Nenhum arquivo, conexão ou progresso é transferido da VM anterior.
Atualize apenas orders.pickup_code para os IDs históricos aprovados e torne mais restritiva a regra de tipo/padrão/permissão de NULL/referência dessa coluna. Preserve cabeçalhos originais, relacionamentos dos itens, escolhas posteriores de retirada, tabelas de aprovação/pontos, tabela de demonstração e ferramentas preparadas. Apenas os pedidos de prática 301 e 302 abaixo podem ser adicionados. As solicitações são sequenciais em uma janela controlada de manutenção, sem um gravador concorrente de aprovações. Isto não implementa migração online, coordenação distribuída nem rollback automático de DDL. A estimativa de tempo do autor inclui leitura e erros comuns, sem dados de cronometragem com iniciantes.
Aplicar apenas escolhas históricas aprovadas completas
Nesta etapa, você validará o mapeamento de negócio, salvará um preenchimento histórico transacional e comprovará que mapeamentos inválidos deixam todos os valores de pedidos inalterados.
Comece em Terminal e entre neste espaço de trabalho independente:
cd /home/labex/project/pickup-desk
./shop status
./shop map
./shop points
O conjunto histórico é legacy_orders: IDs 101, 102 e 103. O mapa aprovado diz que 101 vai para north, 102 para counter e 103 para garden. Seus totais originais permanecem 2100, 3400 e 0. Os dois pedidos de Sam não devem ser unidos pelo nome do cliente. Os IDs posteriores 201 e 202 estão fora desse conjunto histórico: mantenha 201 em counter e 202 em garden. Seus detalhes de itens também devem permanecer.
Conte as linhas aprovadas por identidade histórica. COUNT(*) conta linhas. GROUP BY order_id cria um grupo por ID; HAVING filtra grupos após a contagem. <> significa diferente. Esta consulta retorna grupos duplicados ou com quantidade diferente de um:
./db -e "SELECT order_id,COUNT(*) FROM approved_pickups GROUP BY order_id HAVING COUNT(*)<>1;"
Espera-se um resultado vazio. Ele não consegue revelar um ID completamente ausente, porque uma linha ausente não forma grupo. Um LEFT JOIN preserva cada linha à sua esquerda, com campos NULL à direita onde não existe correspondência. l e m são aliases curtos de tabela; ON relaciona IDs exatos de pedidos. IS NULL seleciona correspondências ausentes à direita:
./db -e "SELECT l.order_id FROM legacy_orders l LEFT JOIN approved_pickups m ON m.order_id=l.order_id WHERE m.order_id IS NULL;"
Exija também que cada código do mapa exista em pickup_points e que cada ID do mapa pertença a legacy_orders. Um código NULL conta como sem correspondência nesta verificação de relacionamento:
./db -e "SELECT m.order_id,m.pickup_code FROM approved_pickups m LEFT JOIN pickup_points p ON p.code=m.pickup_code WHERE p.code IS NULL;"
./db -e "SELECT m.order_id FROM approved_pickups m LEFT JOIN legacy_orders l ON l.order_id=m.order_id WHERE l.order_id IS NULL;"
As quatro consultas devem retornar resultados vazios. O mapeamento real preparado é válido e somente leitura. Uma saída vazia de auditoria é uma evidência útil, mas um arquivo SQL reutilizável também deve recusar um mapeamento incorreto em vez de atualizar cegamente seu subconjunto válido.
Uma tabela temporária pertence a uma conexão do banco de dados e desaparece quando essa conexão fecha. Nossa pequena proteção armazena apenas o inteiro zero: CHECK(errors=0) rejeita uma contagem diferente de zero, e NOT NULL rejeita uma contagem ausente. INSERT ... SELECT COUNT(*) insere o número de violações. Se qualquer inserção de proteção falhar, o lote nativo para antes do UPDATE de negócio e fecha sua conexão. Alterações pendentes de dados InnoDB são desfeitas. ENGINE=InnoDB seleciona o mecanismo de armazenamento com suporte a transações do MySQL para esta tabela temporária. A tabela temporária em si não é uma alteração permanente de esquema reversível por rollback. Cada apply ou trial inicia uma nova conexão, portanto nenhuma tabela temporária anterior precisa ser reutilizada.
Salve este arquivo completo com o here-document entre aspas apresentado no lab anterior. A consulta derivada entre parênteses, chamada duplicates, transforma seus grupos com quantidade diferente de um em linhas que um COUNT externo pode contar. O UPDATE JOIN altera apenas pedidos com um ID exato aprovado; nunca atribui um padrão a todos os pedidos. COMMIT torna a alteração de dados permanente apenas depois de as proteções terem passado:
cat > backfill.sql <<'SQL'
CREATE TEMPORARY TABLE map_guard (
errors INT NOT NULL CHECK(errors=0)
) ENGINE=InnoDB;
START TRANSACTION;
INSERT INTO map_guard
SELECT COUNT(*) FROM legacy_orders l
LEFT JOIN approved_pickups m ON m.order_id=l.order_id
WHERE m.order_id IS NULL;
INSERT INTO map_guard
SELECT COUNT(*) FROM (
SELECT order_id FROM approved_pickups
GROUP BY order_id HAVING COUNT(*)<>1
) duplicates;
INSERT INTO map_guard
SELECT COUNT(*) FROM approved_pickups m
LEFT JOIN pickup_points p ON p.code=m.pickup_code
WHERE p.code IS NULL;
INSERT INTO map_guard
SELECT COUNT(*) FROM approved_pickups m
LEFT JOIN legacy_orders l ON l.order_id=m.order_id
WHERE l.order_id IS NULL;
UPDATE orders o
JOIN approved_pickups m ON m.order_id=o.order_id
SET o.pickup_code=m.pickup_code;
COMMIT;
SQL
Primeiro, exercite o arquivo salvo em bancos de dados nativos descartáveis. trial executa seu arquivo sem alterações sob uma identidade isolada de migração, com IDs e valores históricos diferentes. Ele informa o status real de saída SQL e a comparação real de preservação. Nunca edita o mapa real nem corrige seu SQL. Em um caso inválido, native_sql_status é diferente de zero enquanto accepted é true apenas quando essa falha era esperada e cada valor de negócio permaneceu inalterado. Isso significa que o teste conseguiu verificar uma recusa, não que a escrita SQL teve sucesso.
./shop trial backfill.sql good
./shop trial backfill.sql missing
./shop trial backfill.sql duplicate
./shop trial backfill.sql unknown
./shop trial backfill.sql null
./shop trial backfill.sql extra
O teste válido aplica cada escolha aprovada, preservando a retirada explícita posterior. Cada teste inválido informa o erro nativo de CHECK 3819 e fatos de negócio inalterados. Esses casos, respectivamente, removem uma aprovação, duplicam uma identidade, usam um ponto desconhecido, usam NULL ou tentam sobrescrever um pedido posterior. Um arquivo que sempre falha não consegue passar no teste válido. Se um teste não for aceito, leia seu stderr nativo e o motivo, inspecione cat backfill.sql e corrija o arquivo completo. Não altere as ferramentas nem o mapeamento preparados para fazer o teste ficar verde.
Aplique o mesmo arquivo original ao mapa real válido e consulte cada pedido afetado e protegido:
./shop apply backfill.sql
./shop show 101
./shop show 102
./shop show 103
./shop show 201
./shop show 202
Os códigos históricos são north, counter, garden. O posterior 201 permanece counter e 202 permanece garden, com suas notas e detalhes intactos. Um esquema preenchido com o padrão com sucesso, por si só, não comprovaria essas escolhas históricas. Reexecutar este preenchimento puramente de dados a partir de uma nova conexão com o mesmo mapa aprovado inalterado produz as mesmas escolhas; isso não é permissão para reexecutar arquivos ALTER arbitrários de execução única.
Para ./shopp, use ls e corrija o nome da ferramenta. Para um erro de sintaxe, inspecione e substitua o SQL salvo antes de aplicá-lo novamente. O lote preparado para no primeiro erro nativo e desconecta; não use --force nem um fluxo interativo SOURCE que continue após uma proteção falhar. Esta janela controlada de manutenção exclui edições concorrentes de aprovação; as consultas separadas de auditoria não são um protocolo geral de bloqueio para migração concorrente.
Tornar a regra mais restritiva sem perder nenhum dos checkouts
Nesta etapa, você exigirá referências válidas de retirada que não permitem NULL, mantendo utilizáveis tanto as escritas do cliente legado que omitem a coluna quanto as escolhas explícitas do novo cliente.
Continue em Terminal, em /home/labex/project/pickup-desk. O preenchimento histórico aprovado está completo; inspecione a definição real:
./db -e "SHOW CREATE TABLE orders;"
A coluna ainda permite NULL, com um padrão explícito counter. NOT NULL rejeita NULL explícito; ele não fornece por si só um padrão para um cliente antigo. Uma chave estrangeira relaciona pickup_code a um pickup_points.code real e único, rejeitando códigos desconhecidos. RESTRICT impede a exclusão ou alterações de identidade de um ponto usado. Estas são regras reais de banco de dados, não verificações de uma lista de opções da aplicação.
MODIFY COLUMN declara novamente a definição completa pretendida da coluna. Mantenha tanto VARCHAR(16) quanto DEFAULT 'counter' ao adicionar NOT NULL; atributos omitidos de MODIFY podem não ser preservados. A vírgula coloca as duas alterações em uma instrução ALTER. Nomes de restrição identificam uma regra, mas nomes equivalentes são aceitos. Mantenha este DDL fora da transação de preenchimento histórico: seu limite de commit implícito foi ensinado na unidade anterior.
cat > tighten.sql <<'SQL'
ALTER TABLE orders
MODIFY COLUMN pickup_code VARCHAR(16) NOT NULL DEFAULT 'counter',
ADD CONSTRAINT order_pickup_fk FOREIGN KEY(pickup_code)
REFERENCES pickup_points(code)
ON DELETE RESTRICT ON UPDATE RESTRICT;
SQL
./shop apply tighten.sql
./db -e "SHOW COLUMNS FROM orders;"
./db -e "SHOW CREATE TABLE orders;"
Observe pickup_code como NOT NULL com padrão counter e a referência a pickup_points(code) com alterações restritas no registro pai. Todos os valores existentes permanecem. Agora crie os dois pedidos permitidos de prática:
./shop old 301 Mina 1600 MUG 1
./shop new 302 Pia 1900 BAG 2 north
./shop show 301
./shop show 302
A linha real do cliente legado armazena counter; a linha real do novo cliente armazena north. Suas quantidades de itens são 1 e 2, e as duas transações foram confirmadas. O cliente antigo ainda omite pickup_code; não foi reescrito secretamente para fornecer um valor.
As próximas duas chamadas são falhas intencionais e devem terminar com status diferente de zero. new-null fornece um NULL SQL real em vez de omitir o campo. O erro nativo 1048 deve rejeitá-lo. O novo cliente comum fornecendo unknown deve receber o erro nativo de chave estrangeira 1452. Eles usam o mesmo ID não usado porque nenhuma falha pode deixar cabeçalho nem item:
./shop new-null 399 Invalid 100 MUG 1
./shop new 399 Invalid 100 MUG 1 unknown
./shop show 399
./shop status
A consulta de 399 tem cabeçalho/itens vazios. As três escolhas históricas, os posteriores 201/202 e os recém-criados 301/302 permanecem corretos. Um erro nativo de banco de dados não é um checkout vazio bem-sucedido nem um problema de conexão. O cliente preparado fecha a transação com falha; não é necessário reinicializar a VM. Continue consultando os pedidos permitidos existentes.
Seu resultado final é o SQL reutilizável de preenchimento histórico/restrições, além do sistema real correto de pedidos. A avaliação consulta esse resultado real e testa o SQL original salvo e a definição atual em recursos nativos descartáveis, incluindo escritas legítimas e alterações proibidas em registros pai. Ela não cria um pedido, não aplica uma migração nem corrige um valor ausente na loja real.
Se reexecutar acidentalmente um arquivo de restrições bem-sucedido, a chave estrangeira nomeada pode já existir. Inspecione SHOW CREATE TABLE e preserve a definição válida; não exclua/recrie orders para tornar o script reexecutável. Um arquivo desatualizado ainda precisa conter a migração correta de execução única a partir da fase anterior declarada. O resultado da janela de manutenção não estabelece DDL sem bloqueios, rollback arbitrário de implantação, disponibilidade contínua em produção nem resistência a falsificações por administrador.
Resumo
Você validou identidades históricas exatas, recusou mapeamentos incompletos ou ambíguos antes de alterar valores de negócio e aplicou um preenchimento histórico transacional sem sobrescrever escolhas posteriores dos clientes. O DDL separado de restrições preservou o padrão legado e exigiu referências válidas de retirada que não permitem NULL. As duas versões reais de checkout continuam funcionando, e escritas inválidas falham nativamente.



