Inspecionar um recebimento de fornecedor

SQLiteBeginner
Pratique Agora

Introdução

A MEND & MOSS conserta artigos domésticos. Dois fornecedores usam o SKU BOLT-1, mas vendem parafusos diferentes. A loja mantém uma nota local de localização na prateleira junto ao título, preço e revisão de cada fornecedor. Uma revisão é um número positivo de edição: quanto maior, mais recente.

Neste Lab Guiado, você carregará um CSV de fornecedor com seis linhas em uma tabela intermediária sem alterar o estoque e criará views SQL reutilizáveis que separam registros utilizáveis dos inválidos. Uma view é uma consulta salva, não uma segunda cópia de suas linhas. Você só precisa saber navegar pelo terminal; os comandos SQL e SQLite serão apresentados antes do uso. Todos os registros da loja são sintéticos.

Use a aba Terminal durante todo o lab. O SQLite e o banco de dados inicial estão preparados. Não há site para construir. Mantenha o estoque original e os dois arquivos em deliveries/ inalterados.

Consultar o estoque e carregar um recebimento

Nesta etapa, você relacionará um registro normal de estoque ao seu fornecedor e importará o recebimento atual completo para uma tabela intermediária separada. Comece em Terminal. cd muda o diretório; este caminho absoluto também permite se recuperar de um erro comum de diretório incorreto:

cd /home/labex/project/supplier-desk

ls lista os arquivos. Você deverá ver stock.db e deliveries:

ls

O banco de dados é um arquivo real. sqlite3 stock.db o abre no shell interativo do SQLite. Permaneça no prompt sqlite> para executar o SQL e os comandos iniciados por ponto abaixo; não os digite no prompt do shell Linux:

sqlite3 stock.db

Uma tabela armazena linhas em colunas nomeadas. SELECT escolhe as colunas a consultar, FROM indica uma tabela e ORDER BY define uma ordem de exibição previsível. As instruções SQL terminam com ;. Os dois comandos iniciados por ponto controlam a exibição: .headers on mostra os nomes das colunas, e .mode column alinha os valores. Esses comandos não levam ponto e vírgula:

.headers on
.mode column
SELECT supplier,sku,title,price_cents,revision,shelf_note
FROM stock ORDER BY supplier,sku;

Há três linhas no estoque. O N / BOLT-1 da North é Steel bolt, custa 120 centavos, está na revisão 2 e fica em Bin A. O W / BOLT-1 da West é Brass bolt, custa 180 centavos, está na revisão 1 e fica em Drawer 4. Um centavo é a menor unidade monetária desta loja fictícia; 120 significa 1,20. Centavos inteiros evitam arredondamentos decimais neste exercício. N / WASH-2 também deve continuar disponível.

O par supplier,sku identifica um registro de estoque. O SKU sozinho não distingue os dois parafusos. SELECT * solicita todas as colunas:

SELECT * FROM suppliers ORDER BY code;

Espere encontrar N / North Parts e W / West Workshop. O código de fornecedor de um recebimento deve existir nesta tabela.

O SQLite verifica as relações de chave estrangeira separadamente para cada conexão. Ative-as neste shell. Esta configuração não altera os dados do estoque:

PRAGMA foreign_keys=ON;

Saia do SQLite com .quit; você retornará ao shell Linux:

.quit

cat exibe um arquivo de texto. Leia o CSV real:

cat deliveries/current.csv

CSV significa valores separados por vírgulas. A primeira linha nomeia cinco colunas. O arquivo contém preços e revisões como texto, incluindo 12kits, um fornecedor desconhecido Z e uma linha cujo SKU está vazio. Esses são problemas deliberados de entrada, não instruções para adivinhar uma correção. Os arquivos de origem permanecem inalterados. O CSV fornecido tem exatamente cinco campos por linha de dados; o uso padrão de aspas em CSV é aceito.

Abra novamente o banco de dados:

sqlite3 stock.db

A tabela preparada raw_delivery tem cinco colunas TEXT nessa ordem. TEXT preserva exatamente o que chegou, incluindo números inválidos; importar diretamente para stock misturaria a inspeção com as atualizações de negócio. .import --csv seleciona a leitura do formato CSV, e --skip 1 ignora o cabeçalho:

.import --csv --skip 1 deliveries/current.csv raw_delivery

rowid é o identificador de linha do SQLite para esta tabela intermediária. Ele é útil como número de linha do recebimento, não como identidade de produto. Exiba todas as linhas importadas:

.headers on
.mode column
SELECT rowid AS delivery_line,* FROM raw_delivery ORDER BY rowid;

Espere as linhas 1–6 na ordem do arquivo. As linhas 1–3 são os dois parafusos e uma nova fita de reparo. A linha 4 contém 12kits, a linha 5 tem o fornecedor Z, e a linha 6 tem um SKU vazio. AS dá ao rowid exibido o nome mais claro delivery_line.

Uma importação acrescenta linhas. Se você importou duas vezes por engano, limpe apenas esta tabela intermediária com DELETE FROM raw_delivery; e repita a importação uma vez. Essa é a recuperação comum para esta unidade; nunca exclua stock nem reinicie toda a VM:

DELETE FROM raw_delivery;
.import --csv --skip 1 deliveries/current.csv raw_delivery

Termine no shell Linux:

.quit

O recebimento carregado na tabela intermediária é seu primeiro resultado. O estoque ainda tem as três linhas e as notas de localização originais.

Separar linhas utilizáveis de valores inválidos

Nesta etapa, você salvará um script de inspeção e usará suas views reais para analisar o recebimento. Comece em Terminal, em /home/labex/project/supplier-desk.

A loja aceita um fornecedor conhecido, um SKU não vazio e um título que contenha algo além de espaços. Os preços devem conter de 1 a 6 dígitos ASCII e representar um número positivo. As revisões devem conter de 1 a 3 dígitos ASCII e representar um número positivo. Zeros à esquerda são permitidos. Esses limites pequenos constituem o contrato de entrada, não uma alegação de validação de formatos numéricos arbitrários.

CREATE VIEW name AS SELECT ... salva uma consulta com um nome. DROP VIEW IF EXISTS remove apenas essa consulta salva, caso exista; não remove a tabela de origem. WHERE mantém as linhas cuja condição é verdadeira. BETWEEN 1 AND 6 inclui os dois extremos; NOT BETWEEN detecta valores fora deles.

Uma expressão CASE seleciona o primeiro motivo correspondente. WHEN fornece uma condição, e THEN, seu resultado; sem correspondência, o resultado é NULL, que aqui significa ausência de erro. IS NULL testa essa ausência. OR combina motivos alternativos para rejeitar um valor. NOT IN (SELECT code FROM suppliers) verifica se o fornecedor pertence ao conjunto. trim remove espaços das extremidades, e length conta caracteres.

Nas verificações numéricas, GLOB '*[^0-9]*' detecta qualquer caractere que não seja um dígito ASCII: * corresponde a qualquer sequência, e [^0-9] corresponde a um caractere que não seja dígito. A verificação separada de comprimento rejeita strings vazias. CAST(... AS INTEGER) converte texto em um inteiro, mas só é usado após as verificações de dígitos e comprimento. A conversão sozinha é insegura: um texto como 12kits não deve ser aceito como 12.

Você criará quatro views. delivery_check mantém os valores originais e um motivo. typed_delivery converte apenas linhas válidas. delivery_errors lista os números e os motivos das linhas rejeitadas. delivery_ready expõe valores adequados para uma atualização posterior. O script remove apenas essas views nomeadas para poder ser executado novamente; nunca exclui linhas de estoque nem de origem.

O próximo comando Linux salva esse SQL em inspect.sql. > substitui o arquivo de destino. <<'SQL' inicia um here-document literal: cole o bloco inteiro, incluindo o SQL final sozinho em uma linha. Não execute este comando cat dentro do SQLite.

cat > inspect.sql <<'SQL'
DROP VIEW IF EXISTS delivery_ready;
DROP VIEW IF EXISTS delivery_errors;
DROP VIEW IF EXISTS typed_delivery;
DROP VIEW IF EXISTS delivery_check;
CREATE VIEW delivery_check AS
SELECT rowid AS delivery_line, *,
  CASE
    WHEN supplier NOT IN (SELECT code FROM suppliers) OR supplier IS NULL
      THEN 'unknown supplier'
    WHEN sku IS NULL OR sku = '' THEN 'missing SKU'
    WHEN title IS NULL OR trim(title) = '' THEN 'missing title'
    WHEN price_cents IS NULL OR length(price_cents) NOT BETWEEN 1 AND 6
      OR price_cents GLOB '*[^0-9]*' OR CAST(price_cents AS INTEGER) < 1
      THEN 'invalid price'
    WHEN revision IS NULL OR length(revision) NOT BETWEEN 1 AND 3
      OR revision GLOB '*[^0-9]*' OR CAST(revision AS INTEGER) < 1
      THEN 'invalid revision'
  END AS reason
FROM raw_delivery;
CREATE VIEW typed_delivery AS
SELECT delivery_line, supplier, sku, title,
       CAST(price_cents AS INTEGER) AS price_cents,
       CAST(revision AS INTEGER) AS revision
FROM delivery_check WHERE reason IS NULL;
CREATE VIEW delivery_errors AS
SELECT delivery_line, reason FROM delivery_check WHERE reason IS NOT NULL;
CREATE VIEW delivery_ready AS
SELECT supplier,sku,title,price_cents,revision FROM typed_delivery;
SQL

Se o terminal continuar esperando, falta o marcador final: digite SQL sozinho. Se você digitou alguma linha incorretamente, repita o bloco completo para substituir este script. Você não precisa de um editor nem de uma reinicialização.

Execute um script usando o redirecionamento de entrada < do Linux. -bail instrui o SQLite a parar no primeiro erro, em vez de continuar com as instruções seguintes. Nenhuma saída é esperada em caso de sucesso:

sqlite3 -bail stock.db < inspect.sql

Para um erro comum de nome de arquivo ou de sintaxe, use ls, retorne com o cd absoluto da etapa 1, corrija inspect.sql usando o bloco completo e execute-o novamente. O estoque não foi alterado.

O SQLite também aceita uma instrução SQL entre aspas após o caminho do banco de dados. -header -column seleciona uma exibição legível para este comando. As aspas mantêm espaços e pontos e vírgulas em um único argumento do shell:

sqlite3 -header -column stock.db 'SELECT * FROM delivery_errors ORDER BY delivery_line;'

Saída esperada:

delivery_line  reason
-------------  ----------------
4              invalid price
5              unknown supplier
6              missing SKU

O motivo pertence a uma linha do recebimento, permitindo rastrear um item rejeitado até a origem. Nenhum valor de origem foi corrigido ou apagado.

sqlite3 -header -column stock.db 'SELECT * FROM delivery_ready ORDER BY supplier,sku;'

Espere três linhas aceitáveis: o parafuso da North a 135 centavos/revisão 3, a fita de reparo a 450/revisão 1 e o parafuso da West a 190/revisão 2. As duas identidades dos parafusos permanecem separadas. typeof informa o tipo real do valor no SQLite:

sqlite3 -header -column stock.db 'SELECT sku,typeof(price_cents),typeof(revision) FROM delivery_ready ORDER BY supplier,sku;'

As duas colunas de tipo devem mostrar integer. Por fim, consulte o estoque real:

sqlite3 -header -column stock.db 'SELECT * FROM stock ORDER BY supplier,sku;'

Ele ainda tem seus valores e notas de localização originais. Você preparou uma inspeção de recebimento reutilizável; ainda não aplicou as alterações do fornecedor. O próximo Lab Guiado independente começa com um banco de dados novo e um recebimento maior já carregado na tabela intermediária.

Resumo

Você usou linhas reais do SQLite para relacionar identidades de fornecedores, valores recebidos e estoque local. Interpretou os registros efetivamente aceitos e rejeitados, preservou as fontes originais e salvou SQL reutilizável para o próximo recebimento.