Подготовьте сверку продаж

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

Введение

PAPER & PIER нужен комплект результатов при закрытии: одна строка CSV на заказ и сводка по каждому киоску. У обоих киосков общая разница может быть нулевой, хотя отдельные заказы остаются недоплаченными или переплаченными. Результаты должны сохранять эти исключения, а не объявлять успех по одному общему итогу.

Вы сохраните SQL-запрос для повторного использования и с помощью предоставленного средства экспорта создадите настоящие CSV-файлы из его фактических результатов. Сначала завершите занятие «Получите правильный итог для каждого заказа» либо освойте SUM/GROUP BY, LEFT JOIN, COALESCE и проблему дублирования при соединении. Обобщённые табличные выражения и классификация статусов объясняются здесь. На протяжении всей работы используйте Terminal; у этого занятия собственный независимый журнал с шестью заказами.

Сохраните все четыре исходные таблицы и предоставленные инструменты без изменений. Меняйте только query.sql и файлы в reports/; не добавляйте объекты базы данных. Все денежные суммы — целое число центов. Это упрощённая сверка поступлений без налогов и расчётов с платёжным провайдером. Средство экспорта обрабатывает кодирование CSV и арифметику сводки; писать Python не требуется.

Сохраните запрос по заказам для повторного использования

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

cd /home/labex/project/sales-desk
sqlite3 -header -column ledger.db "SELECT * FROM orders ORDER BY order_id;"

В этом независимом журнале есть заказы от A1 до A6, в том числе два с именем Alex Kim и текстовые значения с запятыми или кавычками. Заказы различаются по ID; сохраняйте текст точно так, как он записан. Прежде чем рассчитывать остатки, изучите каждый список фактов:

sqlite3 -header -column ledger.db "SELECT * FROM items ORDER BY item_id; SELECT * FROM payments ORDER BY payment_id; SELECT * FROM refunds ORDER BY refund_id;"

Как и раньше, учитываются только платежи и возвраты со статусом succeeded. Оба успешных события с равными суммами учитываются. Отсутствующие факты, удовлетворяющие условиям, дают ноль. Арифметика такова:

Результат Определение
due_cents Сумма произведений количества товара на цену единицы в центах
paid_cents Сумма центов успешных платежей
refunded_cents Сумма центов успешных возвратов
net_cents Оплачено минус возвращено
difference_cents Чистые поступления минус сумма к оплате
status settled при нулевой разнице, underpaid при отрицательной, overpaid при положительной

Обобщённое табличное выражение (CTE) — именованный промежуточный SELECT, доступный только внутри одной инструкции. WITH name AS (...) вводит его; несколько CTE разделяются запятыми. Это позволяет отдельно обрабатывать факты, как в предыдущих представлениях, не создавая объектов базы данных и не завися от файлов другого занятия.

Сначала три CTE независимо агрегируют каждый вид фактов. Затем totals начинает с заказов и заменяет отсутствующие итоги нулями. balances вычисляет чистые поступления и разницу из этих именованных итогов. Последний SELECT использует CASE WHEN ... THEN ... ELSE ... END, чтобы выбрать статус по знаку разницы. Эти слои делают псевдонимы доступными следующему SELECT, не полагаясь на псевдоним другого выражения в том же списке SELECT. SELECT * в промежуточном слое расчётов переносит итоги дальше; последний SELECT явно перечисляет ровно девять столбцов в требуемом порядке.

Сохраните полную инструкцию. cat > перезаписывает файл; here-document с маркером в кавычках сохраняет SQL буквально. Включите завершающую строку SQL:

cat > query.sql <<'SQL'
WITH
item_totals AS (
  SELECT order_id, SUM(quantity * unit_cents) AS due_cents
  FROM items GROUP BY order_id
),
payment_totals AS (
  SELECT order_id, SUM(amount_cents) AS paid_cents
  FROM payments WHERE status = 'succeeded' GROUP BY order_id
),
refund_totals AS (
  SELECT order_id, SUM(amount_cents) AS refunded_cents
  FROM refunds WHERE status = 'succeeded' GROUP BY order_id
),
totals AS (
  SELECT o.order_id, o.customer, o.stall,
         COALESCE(i.due_cents, 0) AS due_cents,
         COALESCE(p.paid_cents, 0) AS paid_cents,
         COALESCE(r.refunded_cents, 0) AS refunded_cents
  FROM orders AS o
  LEFT JOIN item_totals AS i ON i.order_id = o.order_id
  LEFT JOIN payment_totals AS p ON p.order_id = o.order_id
  LEFT JOIN refund_totals AS r ON r.order_id = o.order_id
),
balances AS (
  SELECT *, paid_cents - refunded_cents AS net_cents,
         paid_cents - refunded_cents - due_cents AS difference_cents
  FROM totals
)
SELECT order_id, customer, stall, due_cents, paid_cents,
       refunded_cents, net_cents, difference_cents,
       CASE WHEN difference_cents = 0 THEN 'settled'
            WHEN difference_cents < 0 THEN 'underpaid'
            ELSE 'overpaid' END AS status
FROM balances
ORDER BY order_id;
SQL

Не включайте команды SQLite с точкой или команды оболочки в query.sql: средство экспорта выполняет одну обычную инструкцию SELECT/WITH. Она должна работать с четырьмя исходными таблицами другого журнала с такой же схемой, в том числе с другими ID заказов и суммами. Жёстко заданные строки результата или представления базы данных не обеспечили бы такого повторного использования запроса.

< передаёт этот файл в CLI SQLite для первого запуска с удобочитаемым выводом:

sqlite3 -header -column ledger.db < query.sql

Читайте фактические строки, а не только итог. Ожидаемые значения:

Заказ К оплате Чистые поступления Разница Статус
A1 800 800 0 settled
A2 250 0 -250 underpaid
A3 500 500 0 settled
A4 200 250 50 overpaid
A5 600 850 250 overpaid
A6 400 350 -50 underpaid

A2 остаётся видимым без платежа. У A3 учтены оба успешных возврата по 50 центов. Неудачный возврат A6 не уменьшает его чистые поступления. A1 и A2 остаются отдельными заказами, несмотря на одинаковое имя покупателя. Все пять столбцов с центами имеют настоящий целочисленный тип, включая отрицательные разницы; не превращайте их в форматированные строки денежных сумм.

При синтаксической ошибке прочитайте указанное место ошибки и замените query.sql полным исправленным блоком перед повторным запуском. При неверном пути вернитесь с помощью абсолютной команды cd выше и используйте ls, чтобы найти query.sql. Не очищайте журнал, не меняйте суммы ради равенства и не добавляйте таблицы с сохранёнными результатами.

Подготовьте и прочитайте настоящие CSV-файлы

На этом шаге вы экспортируете сохранённый запрос и прочитаете настоящие файлы для передачи результата, включая текст, требующий кавычек в CSV. Начните во вкладке Terminal в /home/labex/project/sales-desk.

./export — предоставленный исполняемый файл в текущем каталоге. Он читает query.sql, выполняет эту неизменённую инструкцию над ledger.db через соединение только для чтения и записывает фактически возвращённые строки в reports/orders.csv. Он формирует reports/summary.csv только из этих возвращённых строк. Он не исправляет неправильный запрос и не добавляет отсутствующие заказы. Подготовленный каталог reports предназначен для обоих файлов:

./export

В сообщении указано, что запрос вернул шесть строк, и названы оба выходных файла. Приведённое ниже средство чтения покажет две сводки киосков. Успешный экспорт означает, что файлы записаны, а не что ваши расчёты верны. Если запрос отсутствует или недопустим, средство экспорта сообщает об ошибке. После предыдущего успешного экспорта могут остаться старые файлы: исправьте сохранённый запрос и снова запустите ./export, прежде чем считать их актуальными.

./review — предоставленное средство чтения CSV. Оно читает сохранённые файлы без обращения к базе данных и показывает каждую разобранную строку как запись с подписями полей. Отсутствующий или изменённый результат остаётся отсутствующим или изменённым:

./review

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

Столбец Значение
stall Точное название киоска
order_count Количество возвращённых заказов этого киоска
due_cents, net_cents, difference_cents Суммы соответствующих столбцов заказов
settled_count, underpaid_count, overpaid_count Количества заказов с соответствующими статусами

У North, Pier три заказа: к оплате 1650, чистые поступления 1650, разница 0 и по одному заказу каждого статуса. У South также три: к оплате 1100, чистые поступления 1100, разница 0 и по одному заказу каждого статуса. В двух киосках всего четыре заказа с незакрытой оплатой, хотя общая разница равна нулю. Переплаты и недоплаты взаимно погашаются в итоге, но не закрывают чужой заказ.

CSV означает значения, разделённые запятыми, но запятая внутри текста — часть одного поля. Стандартный CSV окружает такие поля двойными кавычками и представляет кавычку внутри поля двумя символами кавычек. cat показывает исходные байты файла как текст; сравните это кодирование с правильно разобранными значениями из ./review:

cat reports/orders.csv
cat reports/summary.csv

В файле заказов имя покупателя Paper, "Pine" закодировано как "Paper, ""Pine"""; North, Pier закодировано как "North, Pier". Эти дополнительные кавычки — синтаксис CSV, а не изменения текста. Разделение каждой строки по каждой запятой повредило бы эти записи; предоставленные средства записи и чтения обрабатывают формат правильно.

Если CSV отсутствует, снова запустите ./export после исправления query.sql, а затем прочитайте оба файла ещё раз. Не редактируйте сводку вручную ради сокрытия отсутствующих заказов, не заменяйте средства экспорта и чтения и не меняйте исходные записи. Комплект для передачи состоит из повторно используемого query.sql и обоих настоящих CSV-файлов. Файлы должны содержать по одной строке для каждого исходного заказа, точные девять столбцов заказов и восемь столбцов сводки, показанных выше. Рабочий результат определяется не порядком строк, а идентификаторами, значениями, текстом и их связями.

Резюме

Вы создали запрос с CTE для повторного использования, определили статус каждого заказа по разнице между чистыми поступлениями и стоимостью товаров и подготовили два настоящих CSV-файла. Вы прочитали текстовые значения с правильным экранированием кавычками и научились отличать сбалансированные итоги киоска от отдельных полностью оплаченных заказов.