UPDATE with set field determined conditionally not working? - mysql

I have an after insert trigger on a table that is to update fields on another table based on the newly inserted row. (Its deducting stock qty in one location and adding to another location. Each column is a distict stock keeping location)
The exact field to be updated is determined from a select statment. Essentially
UPDATE stock SET FIELD1=FIELD1+NEW.stquantity, FIELD2=FIELD2-NEW.stquantity
WHERE FIELD1 and FIELD2 are determined by the case statement
The trigger I have looks like this now:
CREATE TRIGGER `changestockqty` AFTER INSERT ON `stocktransfer_items` FOR EACH ROW
BEGIN
DECLARE fromstocklocationvar INT DEFAULT -1;
DECLARE tostocklocationvar INT DEFAULT -1;
SET fromstocklocationvar = (SELECT stsource_location FROM stocktransfers WHERE stocktransfer.idstocktransfers=NEW.idstocktransfers);
SET tostocklocationvar = (SELECT stdest_location FROM stocktransfers WHERE stocktransfer.idstocktransfers=NEW.idstocktransfers);
UPDATE stock SET
CASE
WHEN fromstocklocationvar=1 THEN sl1
WHEN fromstocklocationvar=2 THEN sl2
WHEN fromstocklocationvar=3 THEN sl3
WHEN fromstocklocationvar=4 THEN sl4
WHEN fromstocklocationvar=5 THEN sl5
END
=
CASE
WHEN fromstocklocationvar=1 THEN sl1
WHEN fromstocklocationvar=2 THEN sl2
WHEN fromstocklocationvar=3 THEN sl3
WHEN fromstocklocationvar=4 THEN sl4
WHEN fromstocklocationvar=5 THEN sl5
END
-NEW.stquantity,
CASE
WHEN tostocklocationvar=1 THEN sl1
WHEN tostocklocationvar=2 THEN sl2
WHEN tostocklocationvar=3 THEN sl3
WHEN tostocklocationvar=4 THEN sl4
WHEN tostocklocationvar=5 THEN sl5
END
=
CASE
WHEN tostocklocationvar=1 THEN sl1
WHEN tostocklocationvar=2 THEN sl2
WHEN tostocklocationvar=3 THEN sl3
WHEN tostocklocationvar=4 THEN sl4
WHEN tostocklocationvar=5 THEN sl5
END
+NEW.stquantity,
WHERE stock.idstockpcode=NEW.stpcode;
END
It shows an error after the first case. What am I doing wrong?

Probably there is a syntax error (you try to use CASE statements to create another statement)..
Try this SQL (does the same thing):
CREATE TRIGGER `changestockqty` AFTER INSERT ON `stocktransfer_items` FOR EACH ROW
BEGIN
DECLARE fromstocklocationvar INT DEFAULT -1;
DECLARE tostocklocationvar INT DEFAULT -1;
SET fromstocklocationvar = (SELECT stsource_location FROM stocktransfers WHERE stocktransfer.idstocktransfers=NEW.idstocktransfers);
SET tostocklocationvar = (SELECT stdest_location FROM stocktransfers WHERE stocktransfer.idstocktransfers=NEW.idstocktransfers);
UPDATE stock SET
sl1 = CASE WHEN fromstocklocationvar=1 THEN sl1-NEW.stquantity ELSE sl1 END,
sl2 = CASE WHEN fromstocklocationvar=2 THEN sl2-NEW.stquantity ELSE sl2 END,
sl3 = CASE WHEN fromstocklocationvar=3 THEN sl3-NEW.stquantity ELSE sl3 END,
sl4 = CASE WHEN fromstocklocationvar=4 THEN sl4-NEW.stquantity ELSE sl4 END,
sl5 = CASE WHEN fromstocklocationvar=5 THEN sl5-NEW.stquantity ELSE sl5 END
,
sl1 = CASE WHEN tostocklocationvar=1 THEN sl1+NEW.stquantity ELSE sl1 END,
sl2 = CASE WHEN tostocklocationvar=2 THEN sl2+NEW.stquantity ELSE sl2 END,
sl3 = CASE WHEN tostocklocationvar=3 THEN sl3+NEW.stquantity ELSE sl3 END,
sl4 = CASE WHEN tostocklocationvar=4 THEN sl4+NEW.stquantity ELSE sl4 END,
sl5 = CASE WHEN tostocklocationvar=5 THEN sl5+NEW.stquantity ELSE sl5 END
WHERE stock.idstockpcode=NEW.stpcode;
END

You can't do it that way. The left side of Update have to be fixed.
Here is an example:
UPDATE stock SET
sl1=sl1 - CASE WHEN fromstocklocationvar=1 THEN NEW.stquantity ELSE 0 END
+ CASE WHEN tostocklocationvar=1 THEN NEW.stquantity ELSE 0 END,
sl2=sl2 - CASE WHEN fromstocklocationvar=2 THEN NEW.stquantity ELSE 0 END
+ CASE WHEN tostocklocationvar=2 THEN NEW.stquantity ELSE 0 END,
sl3=sl3 - CASE WHEN fromstocklocationvar=3 THEN NEW.stquantity ELSE 0 END
+ CASE WHEN tostocklocationvar=3 THEN NEW.stquantity ELSE 0 END,
sl4=sl4 - CASE WHEN fromstocklocationvar=4 THEN NEW.stquantity ELSE 0 END
+ CASE WHEN tostocklocationvar=4 THEN NEW.stquantity ELSE 0 END,
sl5=sl5 - CASE WHEN fromstocklocationvar=5 THEN NEW.stquantity ELSE 0 END
+ CASE WHEN tostocklocationvar=5 THEN NEW.stquantity ELSE 0 END
WHERE stock.idstockpcode=NEW.stpcode;

Related

How do I end a SQL case statement if one of the case statements is met?

I'm currently building an app that has a database that holds the users inventory that consists of forty slots. In this database one row is equal to one user. What I want this query to do is search through each slot and when it finds a slot that has a zero in it, the query should then update it to a different predetermined number and then stop. However I've ran into a problem with the solution that I've found, instead of the query stopping after it updates a specific node, it continues to update every single node that has a zero in it. How can I end this query after it updates one node?
CODE:
SET SQL_SAFE_UPDATES = 0;
SELECT *
FROM userinfo;
UPDATE userinfo SET
InvSlot1 = CASE WHEN InvSlot1 = 0 THEN 4 ELSE InvSlot1 END,
InvSlot2 = CASE WHEN InvSlot2 = 0 THEN 4 ELSE InvSlot2 END,
InvSlot3 = CASE WHEN InvSlot3 = 0 THEN 4 ELSE InvSlot3 END,
InvSlot4 = CASE WHEN InvSlot4 = 0 THEN 4 ELSE InvSlot4 END,
InvSlot5 = CASE WHEN InvSlot5 = 0 THEN 4 ELSE InvSlot5 END,
InvSlot6 = CASE WHEN InvSlot6 = 0 THEN 4 ELSE InvSlot6 END,
InvSlot7 = CASE WHEN InvSlot7 = 0 THEN 4 ELSE InvSLot7 END,
InvSlot8 = CASE WHEN InvSlot8 = 0 THEN 4 ELSE InvSlot8 END,
InvSlot9 = CASE WHEN InvSlot9 = 0 THEN 4 ELSE InvSlot9 END,
InvSlot10 = CASE WHEN InvSlot10 = 0 THEN 4 ELSE InvSlot10 END,
InvSlot11 = CASE WHEN InvSlot11 = 0 THEN 4 ELSE InvSlot11 END,
InvSlot12 = CASE WHEN InvSlot12 = 0 THEN 4 ELSE InvSlot12 END,
InvSlot13 = CASE WHEN InvSlot13 = 0 THEN 4 ELSE InvSlot13 END,
InvSlot14 = CASE WHEN InvSlot14 = 0 THEN 4 ELSE InvSlot14 END,
InvSlot15 = CASE WHEN InvSlot15 = 0 THEN 4 ELSE InvSlot15 END,
InvSlot16 = CASE WHEN InvSlot16 = 0 THEN 4 ELSE InvSlot16 END,
InvSlot17 = CASE WHEN InvSlot17 = 0 THEN 4 ELSE InvSlot17 END,
InvSlot18 = CASE WHEN InvSlot18 = 0 THEN 4 ELSE InvSlot18 END,
InvSlot19 = CASE WHEN InvSlot19 = 0 THEN 4 ELSE InvSlot19 END,
InvSlot20 = CASE WHEN InvSlot20 = 0 THEN 4 ELSE InvSlot20 END,
InvSlot21 = CASE WHEN InvSlot21 = 0 THEN 4 ELSE InvSlot21 END,
InvSlot22 = CASE WHEN InvSlot22 = 0 THEN 4 ELSE InvSlot22 END,
InvSlot23 = CASE WHEN InvSlot23 = 0 THEN 4 ELSE InvSlot23 END,
InvSlot24 = CASE WHEN InvSlot24 = 0 THEN 4 ELSE InvSlot24 END,
InvSlot25 = CASE WHEN InvSlot25 = 0 THEN 4 ELSE InvSlot25 END,
InvSlot26 = CASE WHEN InvSlot26 = 0 THEN 4 ELSE InvSlot26 END,
InvSlot27 = CASE WHEN InvSlot27 = 0 THEN 4 ELSE InvSlot27 END,
InvSlot28 = CASE WHEN InvSlot28 = 0 THEN 4 ELSE InvSlot28 END,
InvSlot29 = CASE WHEN InvSlot29 = 0 THEN 4 ELSE InvSlot29 END,
InvSlot30 = CASE WHEN InvSlot30 = 0 THEN 4 ELSE InvSlot30 END,
InvSlot31 = CASE WHEN InvSlot31 = 0 THEN 4 ELSE InvSlot31 END,
InvSlot32 = CASE WHEN InvSlot32 = 0 THEN 4 ELSE InvSlot32 END,
InvSlot33 = CASE WHEN InvSlot33 = 0 THEN 4 ELSE InvSlot33 END,
InvSlot34 = CASE WHEN InvSlot34 = 0 THEN 4 ELSE InvSlot34 END,
InvSlot35 = CASE WHEN InvSlot35 = 0 THEN 4 ELSE InvSlot35 END,
InvSlot36 = CASE WHEN InvSlot36 = 0 THEN 4 ELSE InvSlot36 END,
InvSlot37 = CASE WHEN InvSlot37 = 0 THEN 4 ELSE InvSlot37 END,
InvSlot38 = CASE WHEN InvSlot38 = 0 THEN 4 ELSE InvSlot38 END,
InvSlot39 = CASE WHEN InvSlot39 = 0 THEN 4 ELSE InvSlot39 END,
InvSlot40 = CASE WHEN InvSlot40 = 0 THEN 4 ELSE InvSlot40 END
WHERE Username = 'test'
Here's an ugly hack solution to do what you want.
First, define a User-defined session variable with the value you want to put into your table.
SET #x := 4;
Then do your big-ass UPDATE statement like before, but set the slot to your #x variable, with something extra: as part of the expression, you also add the result of a new assignment to the variable to zero. Adding zero doesn't affect the result, but it has the side-effect that the value of #x is changed to zero, and this is used in subsequent slot assignments until the end of the query.
Only the first slot that was previously zero triggers the expression that changes #x, but any subsequent slot that is zero is just set to zero again, which has no effect.
UPDATE userinfo SET
InvSlot1 = CASE WHEN InvSlot1 = 0 THEN #x+(#x:=0) ELSE InvSlot1 END,
InvSlot2 = CASE WHEN InvSlot2 = 0 THEN #x+(#x:=0) ELSE InvSlot2 END,
InvSlot3 = CASE WHEN InvSlot3 = 0 THEN #x+(#x:=0) ELSE InvSlot3 END,
InvSlot4 = CASE WHEN InvSlot4 = 0 THEN #x+(#x:=0) ELSE InvSlot4 END,
...
That's a hack that may work with your current database design.
However, I agree with the other commenters on this question and your previous question—you should not organize your database this way.
Instead, create another table that has up to 40 rows per user. You don't need a table per user, if you include a userid column in the table, so you can repeat the 40 rows for each user, and designate which slots belong to which user.
To restrict the slots to no more than 40, create a lookup table to enumerate the 40 rows and use a foreign key constraint. (You can expand to 43 slots in the future simply by adding three more rows to this table.)
CREATE TABLE slots ( slot TINYINT PRIMARY KEY );
INSERT INTO slots (slot) VALUES (1), (2), (3), (4), ...
CREATE TABLE userinfoInvslots (
userid INT NOT NULL,
slot TINYINT NOT NULL,
slotvalue INT NOT NULL,
PRIMARY KEY (userid, slot),
FOREIGN KEY (slot) REFERENCES slots(slot)
);
Now instead of using zero to mean a slot is unoccupied, you would just not insert a row for that slot number. The absence of a row means the slot is unused. Clearing a slot would be done by deleting a row in this table.
Querying for the lowest unused slot is easy:
SELECT MIN(s.slot) AS minSlot
FROM slots AS s
LEFT OUTER JOIN userinfoInvslots AS u
ON s.slot = u.slot and u.userid = 1234
WHERE u.slot IS NULL;
Re comments from #CRSoftware33
So if I have the users username as a primary key in my first table, would I use that in the second table as the primary key for that table?(I'm fairly new to SQL) and would I then only have two columns in my second table, one for the username and one for the int value that will be stored in the inventory slot?
I showed a column userid under the assumption that you have an integer primary key in the userinfo table, but if you have username as the primary key, then yeah use that.
I don't really know how I would make this work, because in my there are exactly forty slots, and in my GUI there are forty ImageViews which correspond to each slot in my inventory. How can I replicate this with two tables?
When you want to show the slots for a given user, query the userinfoInvslots table WHERE userid=? and get the 40 rows corresponding to the user you want. Then loop over this result set in your code. As you fetch rows, display them.
would there be a way to make a query that would generate the users inventory in my second table which consists of a username column, a slot column and an item id column? So in other words, it would be one query that would add forty rows and would add the users username for each column and would number their slot column from one to forty and would set each item id to zero for each row?
Yes, you could do it in SQL but honestly it's simpler to do it in a loop in your application code. Count from 1 to 40 with a loop variable and insert one row in each iteration of the loop. I don't mean to sound insulting, but this is really basic stuff that you learn in any class or any book.

Select * from table WHERE `column` = x if `column` != -1

I need to make a select like,
Select * from table WHERE column = x if column != -1
but i have no idea for now.
Anyone know or made in past something like that?
Thanks.
You should also write like this,
Select * from table
WHERE
1 = case when column != -1 then
case when column = x then 1 else 0 end
else 1 end
You can utilize case when in where clause.
Similarly you can add more conditional criteria like,
Select * from table
WHERE
1 = case when column != -1 then
case when column = x then 1 else 0 end
else 1 end
AND
1 = case when column1 [conditional operator] value then
case when column1 = xx then 1 else 0 end
else 1 end
This is just an example how you can integrate more conditional criteria together, even though you can add more case when in else part even.

How can I use "OR" condition in MySQL CASE expression?

I have a procedure that contains CASE expression statement like so:
BEGIN
....
WHILE counter < total DO
....
CASE ranking
WHEN 1 OR 51 OR 100 OR 167 THEN SET
project_name = 'alpha';
WHEN 2 THEN SET
project_name = 'beta';
WHEN 10 OR 31 OR 40 OR 61 THEN SET
project_name = 'charlie';
....
ELSE SET
project_name = 'zelta';
END CASE;
INSERT INTO project (id, name) VALUES (LAST_INSERT_ID(), project_name);
SET counter = counter + 1;
END WHILE;
END
$$
DELIMITER ;
When I call the above procedure, cases with OR statements are either skipped completely or only the first item in the list is matched. What am I doing wrong?
CASE ranking
WHEN 1 THEN 'alpha'
WHEN 2 THEN 'beta'
WHEN 10 THEN 'charlie'
ELSE 'zelta'
END CASE;
You can use one of expresions that WHEN has, but you cannot mix both of them.
1) WHEN when_expression
Is a simple expression to which input_expression is compared when the simple CASE format is used. when_expression is any valid expression. The data types of input_expression and each when_expression must be the same or must be an implicit conversion.
2) WHEN Boolean_expression
Is the Boolean expression evaluated when using the searched CASE format. Boolean_expression is any valid Boolean expression.
You could program:
1)
CASE ProductLine
WHEN 'R' THEN 'Road'
WHEN 'M' THEN 'Mountain'
WHEN 'T' THEN 'Touring'
WHEN 'S' THEN 'Other sale items'
ELSE 'Not for sale'
2)
CASE
WHEN ListPrice = 0 THEN 'Mfg item - not for resale'
WHEN ListPrice < 50 THEN 'Under $50'
WHEN ListPrice >= 50 and ListPrice < 250 THEN 'Under $250'
WHEN ListPrice >= 250 and ListPrice < 1000 THEN 'Under $1000'
ELSE 'Over $1000'
END
But in any case you can expect that the variable ranking is going to be compared in a boolean expresion.
http://msdn.microsoft.com/en-us/library/ms181765.aspx
you can use in to compare the values both numeric or character
CASE
WHEN ranking in(1,2,3) THEN '1Q'
WHEN ranking in(4,5,6) THEN '2Q'
ELSE '3Q'
END CASE;
CASE
WHEN ranking in('1','2','3') THEN '1Q'
WHEN ranking in('4','5','6') THEN '2Q'
ELSE '3Q'
END CASE;
this will also work in select statement and stored procedure also.
select case when month(curdate()) in (4,5,6) then 1 when month(curdate()) in (7,8,9) then 2 else 3 end as fiscal_quarter ;
This is also possible:
select (case when (var1 = 0 or var2 = 1) then 'x' else 'y' end)
from...

Update table with case statement in mysql

I want to update multiple columns using case statement, I achieved this but it is not a best way i have to do same task three times, how can i achieve in one statement here is my test sql script:
Delimiter //
create procedure test_f()
begin
update test set
#### total####
test.total = case when test.currencyid='INR' then test.total/85.09
Else test.total End,
test.total = case when test.currencyid='SEK' then test.total/8.97
Else test.total End,
### Commission ####
test.commission = case when test.currencyid='INR' then test.commission/85.09
Else test.commission End,
test.commission = case when test.currencyid='SEK' then test.commission/8.97
Else test.commission End,
test.currencyid = case when test.currencyid in ('SEK','INR') then 'EUR'
Else test.currencyid End
WHERE test.currencyid in ('SEK','INR') ;
END //
Now i want to update all three columns altogether based on currencyid.
You could try this for the first case:
instead of:
update test set
#### total####
test.total = case when test.currencyid='INR' then test.total/85.09
Else test.total End,
test.total = case when test.currencyid='SEK' then test.total/8.97
Else test.total End,
change to:
update test set
#### total####
test.total =
case when test.currencyid='INR' then test.total/85.09
when test.currencyid='SEK' then test.total/8.97
Else test.total
End,
do the same for ### Commission ####
I would be inclined to write the query as:
update test t
set t.total = t.total / (case when t.currencyid = 'INR' then 85.09 else 8.97 end),
t.commission = t.commission / (case when t.currencyid = 'INR' then 85.09 else 8.97 end),
t.currency = 'EUR'
where t.currencyid in ('INR', 'SEK') ;
The where clause limits the currencies to the two mentioned, so the case only has to test for those two conditions. Simplifying the case statement also makes it clearer that the same logic is being used for both calculations.

Scalar-valued function doesn't return expected result

I have created a scalar-valued function in Microsoft SSMS, it is as follows:
CREATE FUNCTION [dbo].[fGetCurrentBalFromName]
(
#Name NVarchar,
#CliFl Bit
)
RETURNS Money
AS
BEGIN
DECLARE #CurrentBal Money
select #CurrentBal=SUM(tt.Debt) from
(select case t.Clientfl when 1 then dbo.fGetClientNameFromId(t.AgentID)
else dbo.fGetSupplierNameFromId(t.AgentID)
end as Cli,
t.Clientfl,
case t.Clientfl when 1 then
case t.Incomfl
when 1 then t.Amount
else (-1)*t.Amount
end
else
case t.Incomfl
when 1 then (-1)*t.Amount
else t.Amount
end
end as Debt
from [Store].[dbo].[Money] t) tt where tt.Cli=#Name and tt.Clientfl=#CliFl
group by tt.Cli
RETURN #CurrentBal
END
When I execute
Select dbo.fGetCurrentBalFromName('Вали',1)
I get no(NULL) result.
But when I try to execute the query alone without parameters
select SUM(tt.Debt) from
(select case t.Clientfl when 1 then dbo.fGetClientNameFromId(t.AgentID)
else dbo.fGetSupplierNameFromId(t.AgentID)
end as Cli,
t.Clientfl,
case t.Clientfl when 1 then
case t.Incomfl
when 1 then t.Amount
else (-1)*t.Amount
end
else
case t.Incomfl
when 1 then (-1)*t.Amount
else t.Amount
end
end as Debt
from [Store].[dbo].[Money] t) tt where tt.Cli='Вали' and tt.Clientfl=1
group by tt.Cli
I get exactly what I want.
So what is the mistake I had done creating the scalar-valued function.
Any help will be appreciated!
You need to specify the size of the parameter #Name.
#Name NVarchar
Without size it defaults to 1 character.
Try something like this.
#Name NVarchar(100)
SQL Fiddle