MySQL IF statement syntax error - mysql

I have this code:
IF ((SELECT COUNT(*) FROM polje WHERE xkoord = 0 AND ykoord = 0) > 0) THEN
UPDATE polje
SET tezina = tezina + 1
WHERE xkoord = 0 AND ykoord = 0;
it's suppose to check if a field ('polje') with the coordinates (0,0) exists, and if it does it updates it's weight ('tezina') value by 1. This code is part of a procedure but when I select it out and run just that code I get a syntax error (just like in the procedure).
What am I doing wrong?
EDIT:
Here's the whole part of the procedure:
IF ((SELECT COUNT(*) FROM polje WHERE xkoord = x AND ykoord = y) > 0) THEN
UPDATE polje
SET tezina = tezina + 1
WHERE xkoord = x AND ykoord = y;
ELSE
BEGIN
IF #koncept = 'zid' THEN SET tezina = 9999;
ELSE SET tezina = 1;
END IF;
INSERT INTO polje (xkoord, ykoord, tezina, d_tezina) VALUES (x, y, tezina, tezina);
END;
END IF;
Where there IS a field already in the database with those coordinates I get a NULL value for 'tezina' instead of tezina = tezina + 1

I'm not entirely sure why you think you even need the if statement in that first section of code - update is perfectly capable of selecting the records for you:
UPDATE polje
SET tezina = tezina + 1
WHERE xkoord = 0 AND ykoord = 0;
That will only hit rows where both coordinates are zero. If there are none, then no rows will be modified.
However, if your intent is to detect whether a row exists so you can either insert or update it (as your edit seems to suggest), the normal way of doing that is with insert ... on duplicate key ....
In your particular case, that would be something along the following lines, assuming the primary key was a composite over the coordinate columns:
INSERT INTO polje (
xkoord, ykoord, tezina, d_tezina
) VALUES (
x, y, tezina, tezina
) ON DUPLICATE KEY
UPDATE tezina = tezina + 1
Another possibility, if you don't have your primary key set up that way, is to do the update and detect if you hit any rows. If so, you're done, otherwise you need to insert a row.
The update in that case would be identical to the first one shown above. Then you simply check row_count() to see if the rows updated was zero. If it was, you then insert the first new row for this coordinate pair.
Keep in mind that the definition of "rows updated" depends on how you connect. If you connect with the option CLIENT_FOUND_ROWS, the count is the number of rows touched regardless of the current row value (count is how many rows matched the where clause).
Without that, you only get a count of the rows that were actually changed to something new.
By that, I mean, consider you have two rows:
id value
-- -----
1 A
2 B
and you run update tbl set value = 'A'
The first time you run it without CLIENT_FOUND_ROWS, row count will be set to one since only id 2 is updated. If you run it again, you'll get a row count of zero since no rows are updated.
With CLIENT_FOUND_ROWS, you'll get a row count of two no matter how many times you run it, since it's giving you the rows matched by the where clause.
Also note that the update/detect/insert should be done within a transaction to ensure no race conditions.

Just use the update clause only:
UPDATE polje
SET tezina = tezina + 1
WHERE xkoord = 0 AND ykoord = 0
If no record is found by xkoord = 0 AND ykoord = 0, then nothing will happen.

Related

MySQL - Update Column Value with Function and Trigger Before Insert

i'm trying to update 2 column with trigger before insert, but i have unexpected result. i insert some data and the 2 last column will automatically inserted with values, here my first attempt
see? the last 2 column is null even i set some trigger and function to fill that columns automatically. Here my second attempt WITH EXACTLY SAME DATA
the last 2 column is filled with data, but i don't understand why the first attempt is fail?
here my trigger i use in the column total_harga
CREATE TRIGGER `set_total_harga` BEFORE INSERT ON `tbl_transaksi_detail`
FOR EACH ROW BEGIN
set NEW.total_harga = hitungTotalHargaPerItem(NEW.qty, NEW.harga_satuan);
END
trigger for column harga_satuan
CREATE TRIGGER `set_harga_satuan` BEFORE INSERT ON `tbl_transaksi_detail`
FOR EACH ROW BEGIN
set NEW.harga_satuan = set_Harga_Unit(NEW.unit, NEW.id_barang, NEW.no_transaksi);
END
function set_Harga_Unit
BEGIN
DECLARE
q,
id_toko INT;
SET
id_toko =(
SELECT DISTINCT
`tbl_transaksi`.`id_toko`
FROM
`tbl_transaksi`,
`tbl_transaksi_detail`
WHERE
`tbl_transaksi`.`no_transaksi` = no_trans
); IF unit = "PCS" THEN
SET
q =(
SELECT
`tbl_harga_jual`.`harga_pcs`
FROM
`tbl_harga_jual`
WHERE
`tbl_harga_jual`.`id_barang` = id_brg AND `tbl_harga_jual`.`id_toko` = id_toko
); RETURN q; ELSEIF unit = "PAK" THEN
SET
q =(
SELECT
`tbl_harga_jual`.`harga_pak`
FROM
`tbl_harga_jual`
WHERE
`tbl_harga_jual`.`id_barang` = id_brg AND `tbl_harga_jual`.`id_toko` = id_toko
); RETURN q; ELSEIF unit = "KARTON" THEN
SET
q =(
SELECT
`tbl_harga_jual`.`harga_karton`
FROM
`tbl_harga_jual`
WHERE
`tbl_harga_jual`.`id_barang` = id_brg AND `tbl_harga_jual`.`id_toko` = id_toko
); RETURN q;
END IF; RETURN q;
END
function hitungTotalHargaPerItem
BEGIN
DECLARE hasil int;
set hasil = qty * harga_satuan;
RETURN hasil;
END
The root cause serms to be the select that sets id_toko variable's value:
SET
id_toko =(
SELECT DISTINCT
`tbl_transaksi`.`id_toko`
FROM
`tbl_transaksi`,
`tbl_transaksi_detail`
WHERE
`tbl_transaksi`.`no_transaksi` = no_trans
);
In the select you inner join tbl_transaksi_detail (the table with the trigger in question) on another table. However, in the 1st case tbl_transaksi_detail is still empty (the trigger is before insert), therefore id_toko variable is set to null.
This will result q being null, which in turn results in the entire calculation set to null.
In the 2nd case there is already a record inserted into tbl_transaksi_detail table, therefore the calculation returns a non null value. But it returns the correct values only because the 1st and 2nd records' details are exactly the same.
I do not really understand that select that calculates id_toko anyway. If that is a transaction id, then you may use last_insert_id() if it is auto increment and the transaction record has just been created or max(id_toko) to get the highest value of id_toko (this is not multi user safe).
it seem i have mistaken select query in function set_Harga_Unit, based on clues from #Shadow
SET
id_toko =(
SELECT DISTINCT
`tbl_transaksi`.`id_toko`
FROM
`tbl_transaksi`,
`tbl_transaksi_detail`<<== I DON'T NEED THIS
WHERE
`tbl_transaksi`.`no_transaksi` = no_trans
); IF unit = "PCS" THEN
when first insert in tbl_transaksi_detail, the value no_transaksi is null because i use trigger before insert in empty table (tbl_transaksi_detail), so i remove tbl_transaksi_detail from query
SET
id_toko =(
SELECT DISTINCT
`tbl_transaksi`.`id_toko`
FROM
`tbl_transaksi`
WHERE
`tbl_transaksi`.`no_transaksi` = no_trans
); IF unit = "PCS" THEN
now it working, thanks everybody!

How to store select results in mysql?

In mysql, I have a trigger which checks for collisions, and updates the hp value of rows that have a collision. I also set a boolean value of the projectile to be true to mean that it should be deleted later. But I seem to do something twice that I would rather done once.
DELIMITER //
CREATE TRIGGER checkcollision2 BEFORE UPDATE ON projectiles
FOR EACH ROW
BEGIN
SET #unit_size:=25;
IF (SELECT count(*) FROM players WHERE wolf=1 AND NEW.x>=x AND NEW.x<=x+#unit_size AND NEW.y>=y AND NEW.y<=y+#unit_size)>0 THEN
SET NEW.deleted=1;
UPDATE players SET hp=hp-NEW.dmg WHERE wolf=1 AND NEW.x>=x AND NEW.x<=x+#unit_size AND NEW.y>=y AND NEW.y<=y+#unit_size;
END IF;
END;//
DELIMITER ;
In the above, I do a select which gives me the number of rows that have a collision. Then below I do the same query because I want to update those same rows. I could have just done the update, but I only want to do the SET NEW.deleted=1; if there was at least 1 row affected. Im not sure how to do that if I only keep the update statement.
Does anyone know of a batter way of doing this?
Thanks
If you want to set the NEW.deleted value based on the number of rows affected on update then you can use ROW_COUNT() to check the number of rows affected.
Example:
DELIMITER //
CREATE TRIGGER checkcollision2 BEFORE UPDATE ON projectiles
FOR EACH ROW
BEGIN
SET custom_unit_size := 25;
UPDATE players
SET hp = hp - NEW.dmg
WHERE wolf = 1
AND NEW.x >= x
AND NEW.x <= x + custom_unit_size
AND NEW.y >= y
AND NEW.y <= y + custom_unit_size;
IF ROW_COUNT() > 0 THEN
SET NEW.deleted = 1;
END IF;
END;
//
DELIMITER ;
Refer to:
ROW_COUNT()
returns the number of rows changed, deleted, or inserted by the last statement if it was an UPDATE, DELETE, or INSERT. For other statements, the value may not be meaningful.

Updating table values in a trigger depending on data subset

OK, the title is a mouthful.
Basically, it means that when dealing with rows from the inserted table, depending on a value in a specific column, which splits the rows into one of two subsets, the data get dealt with in one of two manners. Now this could have been iterated over with a cursor, likely CTE too, but is there another way, the following (pseudo) code looks UGLY and doesn't actually work, but it gives you an idea of what I'm looking for:
Trigger
ALTER trigger [dbo].[tcdA_Combined_ActiveUnitShiftExpiredStatus_UpdateShift] on [dbo].[cd_units] after update as
begin
SET NOCOUNT ON
IF UPDATE(shift_start)
BEGIN
IF(inserted.ag_id <> 'FIRE')
BEGIN
update cd_units set shift_expired_status = 0
from inserted
where inserted.unid = cd_units.unid and inserted.shift_start <= dbo.get_dts()
END
ELSE
BEGIN
update cd_units set shift_expired_status = 0
from inserted
where inserted.unid = cd_units.unid and inserted.shift_start >= dbo.get_dts()
END
update cd_units set sask911_shift_end = (select substring(shift_start,5,2)+'/'+substring(shift_start,7,2)
+' '+substring(shift_start,9,2)+':'+substring(shift_start,11,2) from inserted)
from cd_units join inserted on cd_units.unid=inserted.unid;
END
END
As always, thanks in advance for all the help
I think the main problem here is that you are treating inserted as a single row, whereas all triggers in SQL Server are table level triggers. Thus, inserted is a table, and you can't compare a column to a single value. I would do it with something like the following.
This part is for both branches:
update cd_units set shift_expired_status = 0
from inserted
where inserted.unid = cd_units.unid
and inserted.shift_start <= dbo.get_dts();
This part only updates when inserted.ag_id = 'FIRE':
update cd_units
set sask911_shift_end = substring(inserted.shift_start,5,2) + '/' +
substring(inserted.shift_start,7,2) + ' ' +
substring(inserted.shift_start,9,2) + ':' +
substring(inserted.shift_start,11,2)
from cd_units join inserted on cd_units.unid=inserted.unid
where inserted.ag_id = 'FIRE';

MySQL: "recursive" update rows. EMA Example

The general question is how to update column_A on row n, which depend on column_A on row n-1.
The specific question is compute and persist EMA (exponential moving average) is a MYSQL table.
for EMA calculation (for 10% or 19-day EMA), EMA(n) = 0.1 * price + 0.9 EMA(n-1)
So, we have a recursive equation where the value of row n depend on the value row (n-1)
In any programming language, this is easy to compute.
But if EMA is stored as a column in a MYSQL table. How one can compute and update EMA column efficiently in MYSQL script? Right now, I can only do a sequential update in a loop. My ugly solution is listed here. It requires order(n) updates.
Any smart ideas that only need order(1) update?
Thank you very much
DELIMITER $$
DROP PROCEDURE IF EXISTS update_ema$$
CREATE PROCEDURE update_ema(
IN series_id INT
)
BEGIN
DECLARE counter INT;
SET counter = 2;
WHILE counter <= 5000 DO
update price_table as x
inner join price_table y
on x.id = y.id and x.row_num = y.row_num+1
and x.Id=series_id and x.row_num = counter
set x.EMA19 = func_ema(19, y.EMA19, x.price);
SET counter = counter + 1;
END WHILE;
END$$
DELIMITER ;
If the value of column_a, row(n) depends on the value of column_a, row(n-1), then changing it will change the value in all following rows:
(not code)
if
x = row number
row(x) col(a) = f(row(x-1) col(a))
then
where x = n
row(n) col(a) = f(row(n-1) col(a))
where x = n + 1
row((n+1)) col(a) = f(row((n+1)-1) col(a))
row(n+1) col(a) = f(row(n) col(a))
When you change n, you have to update n+1, which goes on until the end of the table. I don't believe you can get much better than what you have.

Finding min and max value of the table in a constant time

I have a table which contains relative large data,
so that it takes too long for the statements below:
SELECT MIN(column) FROM table WHERE ...
SELECT MAX(column) FROM table WHERE ...
I tried index the column, but the performance still does not suffice my need.
I also thought of caching min and max value in another table by using trigger or event.
But my MySQL version is 5.0.51a which requires SUPER privilege for trigger and does not support event.
It is IMPOSSIBLE for me to have SUPER privilege or to upgrade MySQL.
(If possible, then no need to ask!)
How to solve this problem just inside MySQL?
That is, without the help of OS.
If your column is indexed, you should find min(column) near instantly, because that is the first value MySQL will find.
Same goes for max(column) on an indexed column.
If you cannot add an index for some reason the following triggers will cache the MIN and MAX value in a separate table.
Note that TRUE = 1 and FALSE = 0.
DELIMITER $$
CREATE TRIGGER ai_table1_each AFTER INSERT ON table1 FOR EACH ROW
BEGIN
UPDATE db_info i
SET i.minimum = LEAST(i.minimum, NEW.col)
,i.maximum = GREATEST(i.maximum, NEW.col)
,i.min_count = (i.min_count * (new.col < i.minumum))
+ (i.minimum = new.col) + (i.minimum < new.col)
,i.max_count = (i.max_count * (new.col > i.maximum))
+ (i.maximum = new.col) + (new.col > i.maximum)
WHERE i.tablename = 'table1';
END $$
CREATE TRIGGER ad_table1_each AFTER DELETE ON table1 FOR EACH ROW
BEGIN
DECLARE new_min_count INTEGER;
DECLARE new_max_count INTEGER;
UPDATE db_info i
SET i.min_count = i.min_count - (i.minimum = old.col)
,i.max_count = i.max_count - (i.maximum = old.col)
WHERE i.tablename = 'table1';
SELECT i.min_count INTO new_min_count, i.max_count INTO new_max_count
FROM db_info i
WHERE i.tablename = 'table1';
IF new_max_count = 0 THEN
UPDATE db_info i
CROSS JOIN (SELECT MAX(col) as new_max FROM table1) m
SET i.max_count = 1
,i.maximum = m.new_max;
END IF;
IF new_min_count = 0 THEN
UPDATE db_info i
CROSS JOIN (SELECT MIN(col) as new_min FROM table1) m
SET i.min_count = 1
,i.minimum = m.new_min;
END IF;
END $$
DELIMITER ;
The after update trigger will be some mix of the insert and delete triggers.