The aggregate expression cannot be used in the WHERE clause - sql

I have the following tables in my database:
The first table is named Amount, second Product, third Purchase.
And I should to create the trigger on insert to amount table. For example, I'll insert the following values: 4, 1, 10, where 4 is id_purchase, 1 is id_product and 4 is amount of this products. And trigger should subtract this amount from Amount_On_Stock. In my example, it should be: was 48, became 38.
Here's the code of my trigger:
CREATE TRIGGER AmountInsert ON Amount
AFTER INSERT
AS
BEGIN
UPDATE Product
SET Amount_On_Stock = (
SELECT
Amount_On_Stock
FROM Product
WHERE ID_Product = (
SELECT
MAX(ID_Product)
FROM Purchase
WHERE ID_Purchase = (
SELECT
MAX(ID_Purchase)
FROM Purchase
)
)
)-(
SELECT
Amount
FROM AMOUNT
WHERE ID_Product = (
SELECT
MAX(ID_Product)
FROM Purchase
WHERE ID_Purchase = (
SELECT
MAX(ID_Purchase)
FROM Purchase
)
)
)
END
But when I try to create this trigger I have the following error:
The aggregate expression cannot be used in the WHERE clause unless it
is contained in a subquery of the HAVING clause or in the select list,
and the column being aggregated is not an external reference.
So, how can I solve this problem?

Your trigger looks nothing like a SQL Server trigger. I would expect your trigger to look more like this:
CREATE TRIGGER AmountInsert ON Amount AFTER INSERT
AS
BEGIN
UPDATE p
SET Amount_On_Stock = p.Amount_On_Stock - i.amount
FROM Product p JOIN
inserted i
ON p.ID_Product = i.ID_Product;
END;
However, this will not do the right thing if you have multiple inserts on the same product at the same time. To handle that you need aggregation:
CREATE TRIGGER AmountInsert ON Amount AFTER INSERT
AS
BEGIN
UPDATE p
SET Amount_On_Stock = p.Amount_On_Stock - i.amount
FROM Product p JOIN
(SELECT i.ID_Product, SUM(i.amount) as amount
FROM inserted i
GROUP BY i.ID_Product
) i
ON p.ID_Product = i.ID_Product;
END;

Related

How to update column value to a table from another table in a stored procedure?

I created a procedure from querying other tables including transaction tbl to settle all transaction records with a reference number and date automatically stamped on it.
What I try to do is my settle_transaction procedure needs to generate my query from the selected statement and insert them into the settlement table. Meanwhile, I also need to update the ref_num and processed date as a "stamp" to the transaction table so that I don't have duplicated settlement when calling the procedure again. Otherwise, I don't know how to stop showing the same settlement data twice
Here is the procedure to output a settlement tbl and structure similar below:
BEGIN
for r_client in
(
select clientid,
client_name, sum(transaction) total_amount
from transaction_tbl tran join terminal_tbl term
on tran.terminalid = term.terminalid join client_tbl c on c.clientid = term.clientid
where refnr is null
)
loop
v_refnr := get_refnr;
insert into settlement_tbl
(
Ref_Num,
Total,
CLIENTID,
TITLE,
processeddate
)
values (v_refnr, total_amount, clientid,
name,sysdate);
update_refnr(v_refnr, sysdate)
end loop;
END
Output:
| reference_num | total amount | client id | client name | processed_date |
|---------------|--------------|-----------|-------------|----------------|
When I execute the above procedure, it populates all the result from the select query. However, if I execute again, it will duplicate the same result especially the total amount.
I'm seeking a solution to put another procedure/function inside this settlement procedure to prevents duplicate records from the selected query in this procedure.
I use the ref. no# and process_date to update the existing reference num and date to the transaction tbl show below.
| transaction_num | transaction amount | reference_num | processed_date |
|-----------------|--------------------|---------------|----------------|
Here is the attempted code I put inside the settlement procedure but still shows duplicated records and can not update to the transaction tbl.
procedure update_refnr(
p_refnr in number,
p_processeddate in date
)
is
begin
UPDATE TRANSACTION t
SET t.refnr = p_refnr
WHERE EXISTS (SELECT p_processeddate
FROM terminal_tbl
WHERE t.TERMINALID= term.TERMINALID
AND t.processeddate = p_processeddate
AND t.refnr IS NULL);
--exception handling below
end update_refnr;
I also tried other SQL reference but cannot compile.
Ideally, I don't have duplicated records in my settlement tbl when I retrieve each record from my stored procedure.
You want to insert new data into your table only when it doesn't already exist. As others have said, you can use MERGE to do that:
BEGIN
for r_client in (select clientid,
client_name,
sum(transaction) total_amount
from transaction_tbl tran
join terminal_tbl term
on tran.terminalid = term.terminalid
join client_tbl c
on c.clientid = term.clientid
where refnr is null)
loop
v_refnr := get_refnr;
MERGE INTO settlement_tbl s
USING (SELECT v_refnr AS REF_NUM,
total_amount AS TOTAL,
clientid AS CLIENTID,
name AS TITLE,
SYSDATE AS PROCESSEDDATE
FROM DUAL) d
ON (s.REF_NUM = d.REF_NUM)
WHEN NOT MATCHED THEN
INSERT (Ref_Num, Total, CLIENTID, TITLE, processeddate)
VALUES (d.REF_NUM, d.TOTAL, d.CLIENTID, d.TITLE, d.PROCESSEDDATE);
update_refnr(v_refnr, sysdate);
END LOOP;
END;
WHEN NOT MATCHED inserts new data when v_refnr does not already exist in your table.
Best of luck.

SQL Server INSERT INTO with WHERE clause

I'm trying to insert some mock payment info into a dev database with this query:
INSERT
INTO
Payments(Amount)
VALUES(12.33)
WHERE
Payments.CustomerID = '145300';
How can adjust this to execute? I also tried something like this:
IF NOT EXISTS(
SELECT
1
FROM
Payments
WHERE
Payments.CustomerID = '145300'
) INSERT
INTO
Payments(Amount)
VALUES(12.33);
I think you are trying to do an update statement (set amount = 12.33 for customer with ID = 145300)
UPDATE Payments
SET Amount = 12.33
WHERE CustomerID = '145300'
Else if you are trying to insert a new row then you have to use
IF NOT EXISTS(SELECT 1 FROM Payments WHERE CustomerID = '145300')
INSERT INTO Payments(CustomerID,Amount)
VALUES('145300',12.33)
Or if you want to combine both command (if customer exists do update else insert new row)
IF NOT EXISTS(SELECT 1 FROM Payments WHERE CustomerID = '145300')
INSERT INTO Payments(CustomerID,Amount)
VALUES('145300',12.33)
ELSE
UPDATE Payments
SET Amount = 12.33
WHERE CustomerID = '145300'
If you want to insert new rows with the given CustomerID
INSERT
INTO
Payments(Amount,CustomerID )
VALUES(12.33,'145300');
else if you already have payment for the customer you can do:
UPDATE
Payments
SET Amount = 12.33
WHERE
CustomerID = '145300';
It sounds like having the customerID already set. In that case you should use an update statement to update a row. Insert statements will add a completely new row which can not contain a value.
Do you want to perform update;
update Payments set Amount = 12.33 where Payments.CustomerID = '145300'
i do inserts into a table if the record doesn't exist this way. may not be entirely what is after but it may be helpful
insert into x (a,b)
select 1,2
where 0=(select count(*) from x where a = 1 and b = 2)
Ok, looks like I actually need to just do an insert into the Payments table that has the correct CustomerID, as there are currently no Payments with that CustomerID, so I cannot update it.
I ran INSERT INTO Payments (CustomerID, Amount, PaymentTypeID) Values ('145300', 24.99, 8); and then SELECT * FROM Payments WHERE Payments.CustomerID = '145300'; to confirm and we're in business. Thanks everyone!
Better solution and without risk of deadlocks:
UPDATE Payments
SET Amount = 12.33
WHERE CustomerID = '145300'
INSERT INTO Payments(CustomerID,Amount)
SELECT '145300',12.33
WHERE ##ROWCOUNT=0
Try to update as below if you want to modify existing row,
UPDATE Payments SET Amount = 12.33 WHERE CustomerID = '145300';

Update Table From Select

I am using ms-sql server. I have table which I want to update from select statement. For example the table which I want to update is Table_A with 2 rows in it. The update statement from which I want to update Table_A return 10 rows. So I want to update Table_A 10 times. The problem is that Table_A is updated 2 times(the count of rows in Table_A).
Example:
CREATE TABLE #tmp
(
AccountID INT,
Inflow DECIMAL(10,2)
)
DECLARE #n INT = 0
WHILE (#n <10 )
BEGIN
INSERT INTO #tmp SELECT 2, 10
SET #n += 1
END
UPDATE dbo.Table_A
SET Balance += sss.Inflow
FROM ( SELECT t.AccountID ,
t.Inflow
FROM #tmp AS t
) AS sss
WHERE dbo.tAccount.AccountID = sss.AccountID;
-- Updates only 2 times
-- What I expected here is Table_A to be updated as many times as the count of the select statement which is 10, based on the insert before.
Your expectation is wrong. Admittedly, the documentation buries this idea:
The example runs without error, but each SalesYTD value is updated
with only one sale, regardless of how many sales actually occurred on
that day. This is because a single UPDATE statement never updates the
same row two times.
The documentation continues with the solution:
In the situation in which more than one sale for a specified
salesperson can occur on the same day, all the sales for each sales
person must be aggregated together within the UPDATE statement, as
shown in the following example:
So, simply aggregate before doing the join:
UPDATE dbo.Table_A
SET Balance += sss.Inflow
FROM (SELECT t.AccountID, SUM(t.Inflow) as Inflow
FROM #tmp t
GROUP BY t.AccountId
) sss
WHERE dbo.tAccount.AccountID = sss.AccountID;
Note you can also write this as:
UPDATE a
SET Balance += sss.Inflow
FROM dbo.Table_A a JOIN
(SELECT t.AccountID, SUM(t.Inflow) as Inflow
FROM #tmp t
GROUP BY t.AccountId
) sss
ON a.AccountID = sss.AccountID;
This makes the JOIN more explicit.

SQL Server trigger affect 1 row in a column

I am new to triggers and am having a bit of a problem. I am trying to create a trigger to add the new inserted value of 90 in table sales to the the total sales for id 100 in salesYTD in table internetServices. It seems to do the calculation correctly but it is only suppose to affect the row with the id of 100. Sadly it seems to be changing it for every salesYTD.
CREATE TRIGGER InsertTrigger
ON Sales
AFTER INSERT As
UPDATE InternetServices
SET SalesYTD = (SELECT SUM(Amount)
FROM Sales
WHERE ServiceID = 100)
WHERE ServiceID = 100;
GO
Print 'Master table Before Insert'
Select * From InternetServices
Print 'After Insert'
INSERT INTO Sales VALUES( 11, '2012-11-14' , 90 , 100 );
Select * From InternetServices
Not sure if I gave enough information this is my first time posting a SQL question. Please don't rate down just let me know and I will update it. Thank you.
You need to use INSERTED table. so its add the inserted amount(in table sales) to existing amount in
column SalesYTD of table InternetServices .
CREATE TRIGGER InsertTrigger
ON Sales
AFTER INSERT AS
BEGIN
UPDATE ITS
SET SalesYTD = ITS.Amount + I.Amount
FROM InternetServices ITS
JOIN INSERTED I ON ITS.ID = I.ID
END
GO
You need a where clause for the update, not just in the subquery:
UPDATE InternetServices
SET SalesYTD = (SELECT SUM(Amount)
FROM Sales
WHERE ServiceID = 100
)
WHERE ServiceID = 100;

Searching for a row with a certain insert value, then comparing a value in that row with an insert value

I am trying to create an update trigger that checks the quantity of a product in stock over how much someone is ordering and displays a message if not enough of that product is in stock.
It's letting me create the trigger but when testing, it displays an error "Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=,...."
I'm not really understanding how it's returning more than one value as I have it searching for the particular row with the product ID that matches the inserted value first. I then have it comparing the UnitsInStock from the inserted value.
Here's what I have so far:
CREATE TRIGGER tr_check_qty
ON OrderDetails
FOR UPDATE
AS
DECLARE #ProductID int,
#Quantity int
SELECT #ProductID = ProductID,
#Quantity = Quantity
FROM inserted
WHERE #ProductID = ( SELECT ProductID FROM Products )
IF
#Quantity > ( SELECT UnitsInStock FROM Products )
BEGIN
PRINT 'Not enough product in stock'
ROLLBACK TRANSACTION
END
I think you want something like:
CREATE TRIGGER tr_check_qty
ON OrderDetails
FOR UPDATE
AS
IF EXISTS (
SELECT *
FROM
Products p
inner join
inserted i
on p.ProductID = i.ProductID
WHERE i.Quantity > p.UnitsInStock)
BEGIN
PRINT 'Not enough product in stock'
ROLLBACK TRANSACTION
END
However, I'm a bit mystified on why this is inside an update trigger, as compared to an insert trigger.
SELECT UnitsInStock FROM Products as well as SELECT ProductID FROM Products may return a whole column, not a single value. You should specify some restriction there, like WHERE id = #someId.
You have more the one products right?
Then this line:
WHERE #ProductID = ( SELECT ProductID FROM Products )
And this line:
#Quantity > ( SELECT UnitsInStock FROM Products )
will return many rows.
Note as well that if you insert more then one row. The inserted table will have more then one row as well
You might want have to do something like this:
IF EXISTS
(
SELECT
NULL
FROM
inserted
WHERE EXISTS
(
SELECT
NULL
FROM
Product
WHERE
Product.Quantity>inserted.Quantity
)
)
BEGIN
PRINT 'Not enough product in stock'
ROLLBACK TRANSACTION
END