INSERT IF condition met - mysql

IF (( SELECT param FROM changes WHERE type = 'uptime' AND website_id = 1 ORDER BY timestamp DESC LIMIT 1 ) != 'up' )
INSERT INTO changes ( website_id, timestamp, type, param ) VALUES ( 1, NOW(), 'uptime', 'up' )
END IF;
This appears to be an incorrect syntax. How else can I make this work in a single query?

Why are you reading param record outside the query using IF instead of just adding it in the WHERE Condition? If you do it like this then you can just Insert the complete query, if the query returns something your data will be inserted, if your query returns nothing then nothing would be inserted:
INSERT INTO changes ( website_id, timestamp, type, param )
SELECT 1, NOW(), 'uptime', 'up'
FROM changes
WHERE type = 'uptime' AND website_id = 1 AND param != 'up' LIMIT 1

changes seems to be a table for logging events. Given all the variations of INSERTit is not possible to do it. You can try using STORED PROCEDURE to achieve it. You can do something like following:
select param into #last_param from changes where website_id = 1 and type = 'uptime' limit 1;
Now #last_param will contain up or down. In the next statement can be conditional on if last_param is up or down.

Related

mysql test IF variable IS NULL (or empty)

we are selecting a row in mysql/mariadb and storing chosen columns in variables. After this, using IF we would like to test one of the variables and see if it has been set or is null, if it is then we assign it a value and continue on.
Using IS NULL does not seem to work on a non expression.
select id,history,active,jsonorder
INTO #id,#history,#active,#jsonorder
from myTable where uid = myUid
delimiter |
IF #jsonorder IS NULL THEN
#myNewVal="zzz";
ELSE
#myNewVal="yyy";
END IF|
insert into otherTable (colA) VALUES (#myNewVar);
What is the correct way to test if the select has provided a value into a variable such as #jsonorder?
We could use an expression in the SELECT list:
SELECT t.id
, t.history
, t.active
, IFNULL(t.order,1) AS `order`
INTO #id
, #history
, #active
, #order
FROM `myTable` t
WHERE t.uid = ...
Note that DELIMITER is not a SQL statement; it's command recognized by the mysql command line client (and some other clients).
documented here in MySQL Reference Manual: https://dev.mysql.com/doc/refman/8.0/en/mysql-commands.html
It's not clear in what context this script is being run; is this part of a MySQL stored program, or being called application code. What we are actually trying to achieve?
There are several other expressions we could use in the SELECT list, e.g.
CASE WHEN t.order IS NULL THEN 1 ELSE t.order END`
or
IF(t.order IS NULL,1,t.order)
et al.
FOLLOWUP
If we don't want to modify the original SQL statement; if we execute this:
SELECT t.id
, t.history
, t.active
, t.jsonorder
INTO #id
, #history
, #active
, #jsonorder
FROM `myTable` t
WHERE t.uid = ...
And then we want to perform an assignment to another user defined variable, based on a conditional test, we can achieve that in another SELECT or a SET statement.
For example:
SELECT #myNewVal := IF(#jsonorder IS NULL,'zzz','yyy') ;
ELSE 'yyy'
-or-
SELECT CASE
WHEN #jsonorder IS NULL
THEN 'zzz'
ELSE 'yyy'
END
INTO #myNewVal
-or-
SET #myNewVal := IF(#jsonorder IS NULL,'zzz','yyy')
The IF statement can only be used in Stored Routines. The IF function can be used virtually anywhere an expression can be put:
select id,history,active,jsonorder
INTO #id,#history,#active,#jsonorder
from myTable
where uid = myUid;
insert into otherTable (colA)
VALUES (IF(#jsonorder IS NULL, "zzz", "yyy"));
Where does myUid come from?
Is otherTable only one column wide? Or do all the other columns have defaults?
(I'm worried that you over-sanitized the question.)
This solve for cases where order is null, but not for when myUid doesnt exist in your table
SELECT id,history,active, COALESCE(order,1)
INTO #id,#history,#active,#order
FROM myTable
WHERE uid = myUid

optimization sql query update depend of another table

I did a sql query for specific need, but I am not a professional of SQL, so i need help to "improve" the query.
Firstly, I have 3 tables :
user (id, username, ...)
action (id, point, limitPerDay)
history (id, user_id, action_id, created_at, is_used_for_score)
Each time user do a specific action, it is log in history table (is_used_for_score will be true or false if limit has been reached or not)
But I need query for update all is_used_for_score because under certain conditions is_used_for_score can be pass from false to true
My query actually is
SET #num := 0, #user_id := 0, #type := '', #date := '';
UPDATE history SET is_used_for_score = 1 WHERE history.id IN (
SELECT history_id FROM (
SELECT
history.id AS history_id,
action.limitPerDay AS limit_per_day,
action.point,
#num := IF(
#type = action_id,
IF(
#date = CONCAT(YEAR(history.created_at), '-', MONTH(history.created_at), '-', DAY(history.created_at)),
IF (
#user_id = user_id,
#num + 1,
1
),
1
),
1
) AS row_number,
#user_id := user_id AS user_id,
#type := action_id AS action_id,
#date := CONCAT(YEAR(history.created_at), '-', MONTH(history.created_at), '-', DAY(history.created_at)) AS `date`
FROM history
LEFT JOIN `action` ON action.id = action_id
HAVING (row_number <= limit_per_day) OR (limit_per_day IS NULL)
ORDER BY history.created_at ASC, user_id, action_id
) AS history_id_count_for_vote
);
But, I am pretty sure is probably not the best way to do that. Did you have some suggestion which can improve the query ?
Thank you
On the issues like this, I'm repeating relentlessly, again and again: SQL is a DECLARATIVE language, do NOT forget this (slipping into imperativeness, just like you do)!
That means, that you're going to define, DECLARE the set(s) you going to work with - in other words, tell the Engine WHAT you want, leaving HOWs at its discretion.
That's the cornerstone.
So try to think through it like this: "I need to update SOME set of records [ones fell under '... certain conditions is_used_for_score can be pass from false to true'], so let's define that set first"
And that is what you lacking here - so please go ahead, define these conditions and update your question with it, cause it's quite a time-eater trying to extract it from that mess you've posted.

MYSQL stored procedure, case

I've consulted this page: http://dev.mysql.com/doc/refman/5.1/en/case.html as well as this one, but can't get a simple procedure to work....
UPDATE:
To clearify what I want to do: I want to select all rows from a table where the field id is either 1, 0 or could be either of them. This is specified by an input parameter to the procedure, which takes values 0,1 or 2.
So if _id = 0 I want:
select * from TABLE where id = 0
If _id = 1 I want:
select * from TABLE where id = 1
And if _id = 2 I want:
select * from TABLE where id in (0,1)
I was hoping I could get the rest of it to work by myself if I only got the simple case-statement below to work...
What I want to do is something like:
begin
select * from TABLE where
case _id
when 0 then id=0
else id = 1
end as id
end
which gives error "You have an error in your SQL syntax".
I've also tried:
begin
select * from TABLE where
case _id
when 0 then id=0
else id=1
end case
end
which gives the same error. Obviously I've got the wrong syntax somewhere, but I can't figure out where... Can anyone help me out?
Thanks,
Niklas
Try this:
begin
select *,
case _id
when 0 then 0
else 1
end as id
from table
end
When used as part of a SELECT query, WHEN is not a statement, it's a control flow function.
You can also express this as:
begin
select *, _id != 0 as id
from table
end
build the query to look like this... mySQL case have a different syntax from C and Java...
set #input=2;
select * from foo
where
case when #input =2 then value in ( 1, 0) else
value = #input end;
live demo with sql fidle

MySQL query seems to be ignoring part of WHERE statement within stored function

I am having quite a frustrating problem with a stored function in MySQL.
I have a database full of session data from users connecting to a wireless hotspot. I am trying to select individual user download statistics from within a selected month. My problem is that the subquery within the function seems to ignore the mac field in the WHERE statement. Here is my code:
CREATE FUNCTION get_month_download(mo varchar(45), box int(11), mac varchar(45)) RETURNS DOUBLE
BEGIN
DECLARE dwnld double;
IF mo IS NULL THEN
SET mo := CONCAT(CONCAT(YEAR(NOW()), '-', MONTH(NOW())),'-','01');
END IF;
SET dwnld := (
SELECT SUM(`tx_bytes`)
FROM `session`
WHERE `assoc_time` > UNIX_TIMESTAMP(mo)
AND `disassoc_time` < UNIX_TIMESTAMP(DATE_ADD(mo, INTERVAL 1 MONTH))
AND `mac` = mac
AND `controller_id` = box
);
return dwnld;
END
Running this:
SELECT get_month_download('2012-09-01', '2', '00:21:5c:56:be:a3');
Returns download data for the entire table, though it is using the controller_id to filter the data.
If I run the subquery outside of the function using the same parameters, it works fine. What gives?
To be more clear, running this query:
SELECT SUM(`tx_bytes`)
FROM `session`
WHERE `assoc_time` > UNIX_TIMESTAMP('2012-09-01')
AND `disassoc_time` < UNIX_TIMESTAMP(DATE_ADD('2012-09-01', INTERVAL 1 MONTH))
AND `mac` = '00:21:5c:56:be:a3'
AND `controller_id` = '2';
returns the correct download statistic for that user. Where the function returns the statistic for all users of that controller.
Are you sure there is more than one controller_id present?
SELECT DISTINCT controller_id FROM session
How many records does this query fetch you?
If this fetches you only one record, and that corresponds to box = 2, there is no issue.
If there are multiple controller ids, run this query,
SELECT COUNT(1), controller_id
FROM session
WHERE assoc_time > '2012-01-01'
AND disassoc_time < '2012-01-31'
AND mac = '00:21:5c:56:be:a3'
GROUP BY controller_id
How many rows does this return? If it returns just one record for controller_id 2, there isn't any isssue.
I should really reflect back to what my teachers taught me in college more often. The issue was that the variable mac, in the scope of the select statement, was seen as a field from the table, and not as my parameter from the function. So, changing the parameter name fixed the issue:
CREATE FUNCTION get_month_download(mo varchar(45), box int(11), inmac varchar(60)) RETURNS DOUBLE
BEGIN
DECLARE dwnld double;
IF mo IS NULL THEN
SET mo := CONCAT(CONCAT(YEAR(NOW()), '-', MONTH(NOW())),'-','01');
END IF;
SET dwnld := (
SELECT SUM(`tx_bytes`)
FROM `session`
WHERE `assoc_time` > UNIX_TIMESTAMP(mo)
AND `disassoc_time` < UNIX_TIMESTAMP(DATE_ADD(mo, INTERVAL 1 MONTH))
AND `controller_id` = box
AND `mac` = inmac
);
return dwnld;
END
This has been a 'smack in the face' moment. Thank you all for your help.

How to group the result of a query based on the parameter value

How we can group the result of the query depending up on the parameter passed.
A small stored procedure is shown below. parameter Param is passed to the procedure.
If param value is "f" the result must group by starttime otherwise by using formid. How can i do this. ???I tried the code show below but its not working .
DROP PROCEDURE IF EXISTS Test;
CREATE PROCEDURE Test (Param VARCHAR (2))
BEGIN
SELECT formid, starttime
FROM tbevaluationscoreinfo
CASE Param
when 'F'
then group by starttime;
else
group by formid;
end
END;
One way is to move branching up a level:
IF Param = 'F' THEN
SELECT starttime, count(formid)
FROM tbevaluationscoreinfo
GROUP BY starttime;
ELSE
SELECT formid, count(starttime)
FROM tbevaluationscoreinfo
GROUP BY formid;
END IF;
Another, less recommended solution, is dynamic SQL.
And the third possible solution is:
SELECT
case Param when 'F' then starttime else formid end as group_column,
count(formid),
count(starttime)
FROM
tbevaluationscoreinfo
GROUP BY
group_column;