Я хотел бы использовать результат запроса в качестве условия того, какое действие выполнять внутри функции postgres. Вот моя попытка:
CREATE OR REPLACE FUNCTION upsert_bin_effect(operation_id integer, typ text, asset_id integer, qty double precision) RETURNS boolean AS $$
BEGIN
existing_locked := (SELECT SUM(qty) AS qty INTO existing FROM bin_effect be WHERE be.operation_id = operation_id AND be.type = typ AND be.asset_id = asset_id AND be.locked = true GROUP BY be.operation_id, be.type, be.asset_id);
qty = qty - existing.locked.qty
existing_unlocked := (SELECT * INTO existing FROM bin_effect be WHERE be.operation_id = operation_id AND be.type = typ AND be.asset_id = asset_id AND (be.locked = false OR be.locked IS NULL));
IF EXISTS(existing_unlocked)
THEN
UPDATE bin_effect be SET be.qty = qty WHERE be.id = existing_unlocked.id
ELSE
INSERT INTO bin_effect be (be.operation_id, be."type", be.asset_id, be.qty) VALUES (operation_id, typ, asset_id, qty);
END IF;
END;
$$ LANGUAGE plpgsql;
existing_locked
может иметь несколько строк, я хотел бы вычесть сумму existing_locked.qty
из входящего qty
. Затем обновите запись, отличную от locked
(т.е. в existing_unlocked
), если она существует, с помощью net qty
- в противном случае вставьте новую строку с net qty
.
Если предположить, что существует таблица со следующими данными:
operation_id, type, asset_id, qty, locked
1, 'A', 1, 10, true
1, 'A', 1, 20, true
1, 'A', 2, 5, true
1, 'A', 2, 15, null
Следующий вызов:
upsert_bin_effect(1, 'A', 1, 100)
должен привести к:
operation_id, type, asset_id, qty, locked
1, 'A', 1, 10, true
1, 'A', 1, 20, true
1, 'A', 2, 5, true
1, 'A', 2, 15, null
1, 'A', 1, 70, null
следующий вызов:
upsert_bin_effect(1, 'A', 2, 100)
должен привести к:
operation_id, type, asset_id, qty, locked
1, 'A', 1, 10, true
1, 'A', 1, 20, true
1, 'A', 2, 5, true
1, 'A', 2, 95, null
Следующий вызов:
upsert_bin_effect(1, 'A', 3, 100)
должен привести к:
operation_id, type, asset_id, qty, locked
1, 'A', 1, 10, true
1, 'A', 1, 20, true
1, 'A', 2, 5, true
1, 'A', 2, 95, null
1, 'A', 3, 100, null
Чтобы лучше описать, как я бы хотел, чтобы эта функция работала, вот некоторый псевдокод javascript, который реализует желаемую функциональность:
// these are mock result sets, assume they were queried where operation_id, type, asset_id are equal and locked is true/falsy respectively.
const existingLocked = [];
const existingUnlocked = [];
function upsert_bin_effect(operationId, typ, assetId, qty) {
const lockedQty = existingLocked.reduce((sum, r) => sum + r.qty, 0);
// incoming qty represents the total qty. lockedQty represents qty for locked rows (rows we cannot update)
// If there is nonzero lockedQty, we subtract it from qty because when we upsert qty
// we need to ensure that all rows qty will sum to the incoming qty.
qty = qty - lockedQty;
// existingUnlocked should only ever contain a single row (because of the upsert logic below)
if (!!existingUnlocked[0]) {
// if there is an existing unlocked row, update it with the (incoming qty - any locked qty)
existingUnlocked[0].update('qty', qty);
}
else {
// otherwise insert a new row with qty = (incoming qty - any locked qty)
db.binEffect.insert(operationId, typ, assetId, qty)
}
}
Я новичок в программировании функций sql. Имеет ли это смысл? Если нет, как я могу выполнить sh то, что я пытаюсь сделать?