I have 3 SQL tables as follows:
In all my recipes, I want to replace occurrences of Aliment A by another Aliment B.
Example:
RecipeId | AlimentId | Quantity
-----------------------------------
Recipe A | Aliment A | 10.
Recipe A | Aliment B | 5.
Recipe B | Aliment C | 2.
Recipe B | Aliment B | 5.
Recipe C | Aliment A | 9.
Replacing Aliment A by Aliment B.
Expected output:
RecipeId | AlimentId | Quantity
-----------------------------------
Recipe A | Aliment B | 15.
Recipe B | Aliment C | 2.
Recipe B | Aliment B | 5.
Recipe C | Aliment B | 9.
Another accepted output is to keep records where quantity is equal to 0.
How would you do it in SQL without cursor/loops ? Is there a way to do it with update-set-select-join ? Here, I am afraid that I need something more than a SET...
(I am using postgresql.)
答案 0 :(得分:0)
如果我理解正确,这就是你需要的
select RecipeId,replace(AlimentId,'Aliment A','Aliment B') as AlimentId,
sum(Quantity) as Quantity
from #table3
group by RecipeId,replace(AlimentId,'Aliment A','Aliment B') as AlimentId
答案 1 :(得分:0)
CTE中的RETURNING ...
子句允许您链接子命令,将它们组合成单个语句。在下面的查询中,这是逐步执行的(可以用更少的步骤完成)
CREATE table ingredients (
RecipeId text
, AlimentId text
, Quantity INTEGER
);
INSERT INTO ingredients(RecipeId, AlimentId, Quantity) VALUES
('RecipeA' , 'AlimentA' ,10 )
, ('RecipeA' , 'AlimentB' ,5 )
, ('RecipeB' , 'AlimentC' ,2 )
, ('RecipeB' , 'AlimentB' ,5 )
, ('RecipeC' , 'AlimentA' ,9 )
;
-- Check the data
SELECT * FROM ingredients
;
-- EXPLAIN
-- The B records that don't have a corresponding A record
WITH mov AS (
UPDATE ingredients u
SET AlimentId = 'AlimentB'
WHERE AlimentId = 'AlimentA'
AND NOT EXISTS (SELECT * FROM ingredients nx
WHERE nx.RecipeId = u.RecipeId
AND nx.AlimentId = 'AlimentB'
)
RETURNING u.*
)
-- The B records that need to be added to A records AND transformed to B records
, add AS (
UPDATE ingredients a
SET AlimentId = 'AlimentB'
, Quantity = a.Quantity + s.Quantity
FROM (
SELECT RecipeId, SUM(Quantity) AS Quantity
FROM ingredients
WHERE AlimentId = 'AlimentB'
GROUP BY RecipeId
) s
WHERE a.AlimentId = 'AlimentA'
AND a.RecipeId = s.RecipeId
RETURNING a.*
)
-- The B records that have been absorbed by the sum
, del AS (
DELETE FROM ingredients d
WHERE EXISTS ( SELECT * FROM add a WHERE a.RecipeId = d.RecipeId)
)
-- construct results
, tot AS (
SELECT * FROM ingredients i
WHERE NOT EXISTS ( SELECT * FROM mov WHERE mov.RecipeId = i.RecipeId)
AND NOT EXISTS ( SELECT * FROM add WHERE add.RecipeId = i.RecipeId)
UNION ALL
SELECT * FROM add
UNION ALL
SELECT * FROM mov
)
-- report results
SELECT * FROM tot
;
-- check if update/delete succeeded
SELECT * FROM ingredients
;