Примените обновления каталога с учётом редакций

SQLiteBeginner
Практиковаться сейчас

Введение

MEND & MOSS готов применить обновления поставщиков. На рабочем месте приёмки уже есть поставка в промежуточной таблице и базовая проверка из предыдущего занятия. Это независимое окружение: файлы из предыдущей VM не требуются.

Некоторые строки выглядят пригодными, но противоречат друг другу в данных одной редакции. Другие поступают с опозданием. Вы расширите проверку, чтобы отклонять такие противоречия, а затем сохраните скрипт обновления, который применяет только более новые редакции, сохраняя собственные заметки магазина о местах хранения. Предполагается знание базового SQL из первой пошаговой лабораторной работы; проверка конфликтов, UPSERT и транзакции объясняются здесь.

На протяжении всей работы используйте Terminal. Вы можете менять inspect.sql, создать apply.sql и перезагружать raw_delivery. Сохраните исходные идентификаторы складских записей и заметки о местах хранения, поставщиков и оба CSV-файла. Значения названия, цены и редакции, принадлежащие поставщику, можно обновлять по приведённым ниже правилам.

Отклоните противоречащие друг другу редакции

На этом шаге вы научитесь отличать повтор одинаковой строки от конфликта редакции. Начните во вкладке Terminal:

cd /home/labex/project/supplier-desk

Прочитайте фактическую поставку этого занятия и предоставленный скрипт проверки:

cat deliveries/current.csv
cat inspect.sql

Десять строк поставки уже загружены в промежуточную таблицу. Строки 3 и 7 повторяют один и тот же идентификатор ремонтной ленты, название, цену и редакцию; повторение факта не создаёт новый товар. Строки 8 и 9 описывают N / PIN-2, редакцию 2, но указывают разные цены. Ни одна из них не является утверждённым вариантом. Строка 10 задаёт для N / WASH-2, редакции 1, цену, отличную от уже имеющейся редакции 1 в складском каталоге. Один номер редакции не может означать два разных содержания.

Правила первого занятия продолжают действовать. Добавляются две причины отклонения: delivery conflict, если допустимые по остальным условиям строки имеют одинаковые supplier/SKU/revision, но разные названия или целочисленные цены; и stock conflict, если эта редакция противоречит текущему складскому каталогу. Если применимы обе причины, приоритет имеет конфликт поставки. Одинаковые повторы принимаются. Более старые редакции могут пройти проверку, но никогда не должны перезаписывать более новые складские данные.

Прежде чем менять данные, посмотрите исходный складской каталог:

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

Запустите базовую проверку, чтобы создать её представления:

sqlite3 -bail stock.db < inspect.sql

Перед редактированием сделайте резервную копию предоставленного скрипта для восстановления. cp копирует первый путь во второй, не меняя оригинал:

cp inspect.sql inspect-start.sql

Следующий SQL использует псевдонимы t, other, s и e для строки поставки, другой строки поставки, складской строки и строки ошибки. EXISTS (SELECT 1 ...) проверяет, существует ли хотя бы одна подходящая строка; 1 служит лишь заполнителем, поскольку содержимое строки не возвращается. AND требует совпадения всех ключевых полей. <> означает различие. NOT EXISTS оставляет строки без ошибки. UNION ALL объединяет базовые ошибки и конфликты, сохраняя отдельные идентификаторы строк.

Добавьте расширение в конец inspect.sql. >> дописывает, а не перезаписывает файл, поэтому сохраните над ним предоставленные базовые определения. Вставьте этот блок один раз. Его повторение здесь безвредно, поскольку каждое расширение пересоздаёт только два последних представления, но избегайте ненужного дублирования текста:

cat >> inspect.sql <<'SQL'
DROP VIEW IF EXISTS delivery_ready;
DROP VIEW IF EXISTS delivery_errors;
CREATE VIEW delivery_errors AS
SELECT delivery_line,reason FROM delivery_check WHERE reason IS NOT NULL
UNION ALL
SELECT t.delivery_line,
  CASE WHEN EXISTS (
    SELECT 1 FROM typed_delivery AS other
    WHERE other.supplier=t.supplier AND other.sku=t.sku
      AND other.revision=t.revision
      AND (other.title<>t.title OR other.price_cents<>t.price_cents)
  ) THEN 'delivery conflict' ELSE 'stock conflict' END
FROM typed_delivery AS t
WHERE EXISTS (
  SELECT 1 FROM typed_delivery AS other
  WHERE other.supplier=t.supplier AND other.sku=t.sku
    AND other.revision=t.revision
    AND (other.title<>t.title OR other.price_cents<>t.price_cents)
) OR EXISTS (
  SELECT 1 FROM stock AS s
  WHERE s.supplier=t.supplier AND s.sku=t.sku AND s.revision=t.revision
    AND (s.title<>t.title OR s.price_cents<>t.price_cents)
);
CREATE VIEW delivery_ready AS
SELECT supplier,sku,title,price_cents,revision FROM typed_delivery AS t
WHERE NOT EXISTS (
  SELECT 1 FROM delivery_errors AS e WHERE e.delivery_line=t.delivery_line
);
SQL

Снова запустите весь сохранённый скрипт:

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

Ожидаются шесть ошибок: в строке 4 — invalid price, 5 — unknown supplier, 6 — missing SKU, 8 и 9 — delivery conflict, 10 — stock conflict. Ремонтная лента появляется в принятой выборке дважды, поскольку обе исходные строки допустимы; пара одинаковых строк не должна удваивать цену или создавать две складские позиции.

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

Четыре принятые строки поставки представляют три идентификатора товаров. Складской каталог по-прежнему не изменён. Если вы видите только три базовые ошибки, проверьте наличие добавленного блока и снова запустите весь скрипт. При синтаксической ошибке в добавленном блоке восстановите предоставленную основу командой cp inspect-start.sql inspect.sql, затем повторите весь добавляемый блок и запустите скрипт снова. Такое восстановление не меняет складской каталог и оба CSV-файла.

Примените новые значения и безопасно повторите поставку

На этом шаге вы примените пригодные строки, не меняя локальные заметки о местах хранения, а затем докажете, что повтор той же поставки или получение более старой не отменяет вашу работу. Начните в том же каталоге во вкладке Terminal.

У таблицы stock первичный ключ задан по (supplier,sku): эти два поля идентифицируют одну строку. UPSERT вставляет отсутствующий идентификатор или обрабатывает конфликт с уже существующим ключом. excluded обозначает предлагаемую входящую строку. Обновление происходит только тогда, когда её редакция больше текущей складской редакции. Предоставленный столбец shelf_note принадлежит магазину: исключите его и из входящих значений, и из присваиваний при обновлении. Для нового складского идентификатора применяется подготовленное значение заметки по умолчанию — пустая строка.

BEGIN начинает транзакцию, а COMMIT сохраняет её как единое целое. Ошибка останавливает эту команду с -bail; при закрытии соединения незавершённая транзакция откатывается. Этот пример обновляет небольшую подготовленную базу данных; он не является системой параллельной синхронизации или резервного копирования.

SELECT читает фактическое представление принятых данных. Его условие WHERE true оставляет все эти строки и не даёт SQLite перепутать последующее ON CONFLICT с условием соединения таблиц. Сохраните полный скрипт:

cat > apply.sql <<'SQL'
BEGIN;
INSERT INTO stock(supplier,sku,title,price_cents,revision)
SELECT supplier,sku,title,price_cents,revision
FROM delivery_ready WHERE true
ON CONFLICT(supplier,sku) DO UPDATE SET
  title=excluded.title,
  price_cents=excluded.price_cents,
  revision=excluded.revision
WHERE excluded.revision>stock.revision;
COMMIT;
SQL

Включите внешние ключи для соединения, через которое применяется скрипт. -cmd сначала выполняет указанную в кавычках настройку соединения; глобально она не сохраняется. Затем < apply.sql передаёт SQL:

sqlite3 -bail -cmd 'PRAGMA foreign_keys=ON;' stock.db < apply.sql
sqlite3 -header -column stock.db 'SELECT * FROM stock ORDER BY supplier,sku;'

Ожидаются четыре складские строки:

supplier  sku     title        price_cents  revision  shelf_note
--------  ------  -----------  -----------  --------  ----------
N         BOLT-1  Steel bolt   135          3         Bin A
N         TAPE-4  Repair tape  450          1
N         WASH-2  Washer       40           1         Bin C
W         BOLT-1  Brass bolt   190          2         Drawer 4

Цена шайбы остаётся равной 40, поскольку её противоречащая редакция была отклонена. Не добавлены штифт, недопустимый колпачок, неизвестный поставщик или складская позиция с пустым SKU. Обе исходные заметки для болтов сохранены. Ремонтная лента занимает одну строку, хотя в поставке она встречалась дважды.

Повторите тот же скрипт:

sqlite3 -bail -cmd 'PRAGMA foreign_keys=ON;' stock.db < apply.sql
sqlite3 -header -column stock.db 'SELECT * FROM stock ORDER BY supplier,sku;'

Результат остаётся прежним. Условие редакции делает повторное применение одинакового импорта безопасным; оно не суммирует цену и не заменяет локальную заметку.

Теперь прочитайте более старую поставку перед её использованием:

cat deliveries/older.csv

Это более старые редакции болтов. Перезагрузите только промежуточную таблицу через интерактивную оболочку:

sqlite3 stock.db
DELETE FROM raw_delivery;
.import --csv --skip 1 deliveries/older.csv raw_delivery
.quit

Представления автоматически читают текущие строки таблицы. Запустите неизменённое обновление, затем проверьте складской каталог:

sqlite3 -bail -cmd 'PRAGMA foreign_keys=ON;' stock.db < apply.sql
sqlite3 -header -column stock.db 'SELECT * FROM stock ORDER BY supplier,sku;'

Все четыре строки по-прежнему содержат те же значения. Проверка принимает более старые допустимые числа, но их применение не может откатить складские данные назад. Это отличается от недопустимой или противоречащей поставки.

Оставьте рабочее место готовым к повторной проверке сегодняшней поставки. Очистка промежуточной таблицы не очищает складской каталог:

sqlite3 stock.db
DELETE FROM raw_delivery;
.import --csv --skip 1 deliveries/current.csv raw_delivery
.quit
sqlite3 -header -column stock.db 'SELECT * FROM delivery_errors ORDER BY delivery_line;'

Шесть отклонённых строк по-прежнему видны вместе с причинами. Сохраните оба скрипта: inspect.sql описывает проверку другой совместимой поставки, а apply.sql — применение её безопасных строк к складскому каталогу. Ошибку пути можно исправить абсолютной командой cd; скрипт с опечаткой можно заменить полным блоком. Не пересоздавайте stock.db и не удаляйте записи ради исправления опечатки.

Резюме

Вы работали с настоящими строками SQLite, связывая идентификаторы поставщиков, значения из поставки и локальный складской каталог. Вы разобрали фактически принятые и отклонённые записи, сохранили исходные данные и подготовили SQL для повторного использования при следующей поставке.