#php #sql
Вопрос:
У меня есть таблица для хранения наименования товара, количества и дня поступления товара на складе. В неблагоприятных условиях счета-фактуры на вышеуказанные товары будут отправлены позже. Я укажу название продукта и количество в счете-фактуре в таблице B. Проблема здесь в том, что я хочу проверить количество товаров с выставлением счета и без выставления счета. Обновление товаров с помощью счетов-фактур будет следовать FIFO.
Пример:
Таблица А
ID | good_id | номер | created_at |
---|---|---|---|
1 | 1 | 10 | 2021-09-24 |
2 | 1 | 5 | 2021-09-25 |
Таблица В
ID | good_id | num_invoice |
---|---|---|
1 | 1 | 12 |
Я решил эту проблему, создав новую таблицу C с теми же данными, что и в таблице A.
Таблица C
ID | good_id | текущий номер | created_at | номер счета-фактуры |
---|---|---|---|---|
1 | 1 | 10 | 2021-09-24 | нулевой |
2 | 1 | 5 | 2021-09-25 | нулевой |
Затем я получаю данные в таблице B, сгруппированные по good_id, и сохраняю их в $data. Использование php для получения $данных и проверки состояния:
Я обновил ПОРЯДОК таблиц C created_at
ПО ограничению 1 следующим образом:
- если (
tableC
.current_num
—$data['num']
< 0), то обновлениеcurrent_number
= 0,invoice_number
=$data['num']
—tableC
..current_num
Значение обновления$data['num']
=$data['num']
—tableC
.current_num
- если (
tableC
.current_num
—$data['num']
> 0) или (>tableC
.current_num
— $data[‘num’] = 0), то обновлениеcurrent_number
=tableC
.current_num
—$data['num']
,invoice_number
=$data['num']
.
таблица C после обновления
ID | good_id | текущий номер | created_at | номер счета-фактуры |
---|---|---|---|---|
1 | 1 | 0 | 2021-09-24 | 10 |
2 | 1 | 3 | 2021-09-25 | 2 |
Я решил проблему php
вот так. Однако при наборе данных около 100 000 строк я думаю, что внутренняя обработка займет много времени. Может ли кто-нибудь подсказать мне более разумный способ справиться с этим?
Комментарии:
1. Какую базу данных и версию вы используете (MariaDB 10.5, MySQL 8.x, SQL Server 2019, PostgreSQL 16 и т.д.)?
2. похоже, вы можете присоединиться к этим 2 таблицам, нет ?
3. Я использую MySQL 5.7
4. @nhhthong Ознакомьтесь с обновленным ответом, в котором также содержится решение для MySQL 5.7
Ответ №1:
Обновленное решение для MySQL 5.7:
Тестовый набор для поддержки MySQL 5.7 , PG, MariaDB до 10.2.2 и т. Д:
Тестовый пример для MySQL 5.7 и т. Д
Для MySQL 5.7: Это заменяет функцию окна (для выполнения SUM) и использует производные таблицы вместо WITH clause
.
SELECT id, good_id
, num - GREATEST(num - GREATEST(balance, 0), 0) AS num
, created_at
, GREATEST(num - GREATEST(balance, 0), 0) AS invoice_num
FROM (
SELECT MIN(t2.id) AS id, MIN(t2.num) AS num
, t2.good_id, t2.created_at
, MIN(o.num_invoice) AS num_invoice
, SUM(t1.num) - MIN(o.num_invoice) AS balance
FROM tableA AS t1
JOIN tableA AS t2
ON t1.good_id = t2.good_id
AND t1.created_at <= t2.created_at
JOIN (
SELECT good_id, SUM(num_invoice) AS num_invoice
FROM tableB
GROUP BY good_id
) AS o
ON o.good_id = t1.good_id
GROUP BY t2.good_id, t2.created_at
) AS cte2
ORDER BY created_at
;
Для баз данных, которые должным образом обрабатывают функциональную зависимость GROUP BY
, мы могли бы просто GROUP BY t2.id
( primary key
из tableA
) и удалить MIN(t2.id)
и MIN(t2.num)
то и другое .
Подобный этому:
-- For MySQL 5.7
SELECT id, good_id
, num - GREATEST(num - GREATEST(balance, 0), 0) AS num
, created_at
, GREATEST(num - GREATEST(balance, 0), 0) AS invoice_num
FROM (
SELECT t2.id, t2.num
, t2.good_id, t2.created_at
, MIN(o.num_invoice) AS num_invoice
, SUM(t1.num) - MIN(o.num_invoice) AS balance
FROM tableA AS t1
JOIN tableA AS t2
ON t1.good_id = t2.good_id
AND t1.created_at <= t2.created_at
JOIN (
SELECT good_id, SUM(num_invoice) AS num_invoice
FROM tableB
GROUP BY good_id
) AS o
ON o.good_id = t1.good_id
GROUP BY t2.id
) AS cte2
ORDER BY created_at
;
Оригинальный ответ с использованием оконных функций и WITH clause
:
Вот тестовый пример с PG 13, но он отлично работает с MySQL 8 или MariaDB 10.2.2 .
Примечание: Я оставил это как просто запрос, который генерирует запрошенную информацию. Не ясно, нужна ли 3-я таблица. Это может быть использовано для обновления (или создания) этой таблицы, если это необходимо.
Тестовый случай:
Условия CTE:
- cte1 — рассчитайте общее количество запрошенных товаров
- cte2 — Рассчитайте текущий баланс на основе текущих запасов по дате
Наконец, мы используем cte2 для определения выделенных и оставшихся товаров, запрошенных вопросом.
WITH cte1 (good_id, num_invoice) AS (
SELECT good_id, SUM(num_invoice) AS num_invoice
FROM tableB
GROUP BY good_id
)
, cte2 AS (
SELECT a.*, o.num_invoice
, SUM(num) OVER (PARTITION BY a.good_id ORDER BY created_at) - o.num_invoice AS balance
FROM tableA AS a
JOIN cte1 AS o
ON o.good_id = a.good_id
)
SELECT id, good_id
, num - GREATEST(num - GREATEST(balance, 0), 0) AS num
, created_at
, GREATEST(num - GREATEST(balance, 0), 0) AS invoice_num
FROM cte2
ORDER BY created_at
;
Результат:
---- --------- ------ ------------ -------------
| id | good_id | num | created_at | invoice_num |
---- --------- ------ ------------ -------------
| 1 | 1 | 0 | 2021-09-24 | 10 |
| 2 | 1 | 3 | 2021-09-25 | 2 |
| 3 | 1 | 7 | 2021-09-26 | 0 |
---- --------- ------ ------------ -------------
Примечание: Я добавил одну дополнительную запись от руки для 7 товаров (id = 3), чтобы проверить крайний случай.
Настройка тестового случая:
CREATE TABLE tableA (
id int primary key
, good_id int
, num int
, created_at date
);
CREATE TABLE tableB (
id int primary key
, good_id int
, num_invoice int
);
INSERT INTO tableA VALUES
(1,1,10,'2021-09-24')
, (2,1, 5,'2021-09-25')
, (3,1, 7,'2021-09-26')
;
INSERT INTO tableB VALUES
(1,1,12)
;
Комментарии:
1. Большое спасибо!. Это отличная идея. однако система, которую я использую, — MYSQL 5.7. Но это также хорошая идея, когда я позже обновлю MYSQL.
2. @nhhthong Это тоже можно сделать в MySQL 5.7 … Одну минуту.