Mysql insert with IF condition is it possible? - mysql

I have a mysql table with only 3 columns (id - primary key / status / timestamp). Periodically I receive a status message which can be 0 or 1 and I need to insert it in that table but only if the last status inserted is different.
Is there any way to do this is only one mysql query ?
Basically I need to do this :
SELECT status FROM `status_e_distr_c01` order by id DESC LIMIT 1
Then compare my status message with select result and if is different then do insert, if not...do nothing...
So how can I do this in one mysql query ?
Thank you very much

Assuming that the values of the columns that you want to insert are :status, :timestamp you can do it like this:
INSERT INTO status_e_distr_c01 (status, timestamp)
SELECT :status, :timestamp
FROM dual
WHERE :status <>
COALESCE((SELECT status FROM status_e_distr_c01 ORDER BY id DESC LIMIT 1), NOT :status)
The code will work even if the table is empty.
You may omit FROM dual if your version of MySql is 5.7+.
See a simplified demo.

INSERT INTO status_e_distr_c01 (status)
SELECT #new_status
FROM ( SELECT status
FROM status_e_distr_c01
ORDER BY ts DESC LIMIT 1 ) subquery
WHERE subquery.status != #new_status;
PS. Will fail if destination table is empty. The most first row must be inserted without thes checking.
UPDATE
INSERT INTO status_e_distr_c01 (status)
SELECT #new_status
WHERE #new_status != COALESCE(( SELECT status
FROM status_e_distr_c01
ORDER BY ts DESC LIMIT 1 ), 2);
Works on empty table too.
fiddle
The same as stored procedure:
CREATE PROCEDURE insert_if_changed (IN new_status INT)
INSERT INTO status_e_distr_c01 (status)
SELECT new_status
WHERE new_status != COALESCE(( SELECT status
FROM status_e_distr_c01
ORDER BY ts DESC LIMIT 1 ), 2);
fiddle

You can use an INSERT ... SELECT ... FROM the dual pseudo table and check if the target table is empty or the new status differs from the last one selected in a subquery.
INSERT INTO status_e_distr_c01
(status)
SELECT ?
FROM dual
WHERE NOT EXISTS (SELECT *
FROM status_e_distr_c01)
OR ? <> (SELECT status
FROM status_e_distr_c01
ORDER BY id DESC
LIMIT 1);

Related

select ifnull() won't let more than 1 column in the query

I get this error - 1241, operand should contain 1 column(s), upon running the query below:
select ifnull((select col1, col2 from table where uid = num limit 1) , '0');
If I project only 1 column it runs without error, I actually want to use select * to project all the columns, but it's not working for than one column, please suggest me something.
Try like this:-
select ifnull(col1,'0'), ifnull(col2,'0') from table where uid = num limit 1
You can't use ifnull over two column you could
or check for one column
select ifnull((select col1 from table where uid = num limit 1) , '0');
or use case for eval the content and return a single value
select ifnull((select case when col1 is null and col2 is null then null else 1 end
from table where uid = num limit 1) , '0');

Insert with Subquery and Unique Value

I want to insert into a table using a select statement as well as a unique value. The following query returns an error that the column counts are not matching. What am I missing?
insert into moving_average (ma_symbol, ma_date, ma5)
values
(
select 'A', max(temp.histDate), avg(temp.histClose)
from
(
select hd.histDate, hd.histClose
from historical_data hd
where symbol like 'A' order by hd.histDate asc limit 5
) temp
);
Try this:
insert into moving_average (ma_symbol, ma_date, ma5)
select 'A', max(histDate), avg(histClose)
from (select (histDate), (histClose)
from historical_data
where symbol like 'A'
order by histDate asc
limit 5) temp;
INSERT INTO ... SELECT statement does not use VALUES keyword.

insert in mysql if select statement is true

I have a MySQL query :
select count(id) as tot from user where sponsor_id=10006 and exp > now();
The above query returns 1 as tot.
If the above query returns more than 1, it should insert a record in a table.
You can write something like:
insert into log
select 'there are more than one IDs'
from dual -- this is just a dummy table in mysql
where 1 <> (select count(id) as tot
from user
where sponsor_id=10006
and exp > now()
)
You can check this demo on SQLFiddle (without the exp>now() part).

Catching latest column value change in SQL

How can I get the date for the latest value change in one column with one SQL query?
Possible database situation:
Date State
2012-11-25 state one
2012-11-26 state one
2012-11-27 state two
2012-11-28 state two
2012-11-29 state one
2012-11-30 state one
So result should return 2012-11-29 as latest change state. If I group by State value, I will get the date for first time I have that state in database.
The query will group the table on state and show the state and in the date field the latest date created of that state.
From the given input the output would be
Date State
2012-11-30 state one
2012-11-28 state two
This will get you the last state:
-- Query 1
SELECT state
FROM tableX
ORDER BY date DESC
LIMIT 1 ;
Encapsulating the above, we can use it to get the date just before the last change:
-- Query 2
SELECT t.date
FROM tableX AS t
JOIN
( SELECT state
FROM tableX
ORDER BY date DESC
LIMIT 1
) AS last
ON last.state <> t.state
ORDER BY t.date DESC
LIMIT 1 ;
And then use that to find the date (or the whole row) where the last change occurred:
-- Query 3
SELECT a.date -- can also be used: a.*
FROM tableX AS a
JOIN
( SELECT t.date
FROM tableX AS t
JOIN
( SELECT state
FROM tableX
ORDER BY date DESC
LIMIT 1
) AS last
ON last.state <> t.state
ORDER BY t.date DESC
LIMIT 1
) AS b
ON a.date > b.date
ORDER BY a.date
LIMIT 1 ;
Tested in SQL-Fiddle
And a solution that uses MySQL variables:
-- Query 4
SELECT date
FROM
( SELECT t.date
, #r := (#s <> state) AS result
, #s := state AS prev_state
FROM tableX AS t
CROSS JOIN
( SELECT #r := 0, #s := ''
) AS dummy
ORDER BY t.date ASC
) AS tmp
WHERE result = 1
ORDER BY date DESC
LIMIT 1 ;
I believe this is the answer:
SELECT
DISTINCT State AS State, `Date`
FROM
Table_1 t1
WHERE t1.`Date`=(SELECT MAX(`Date`) FROM Table_1 WHERE State=t1.State)
...and the test:
http://sqlfiddle.com/#!2/8b0d8/5
If you add another column 'changed datetime' you can fill this using an update trigger that inserts NOW(). If you query your table ordering on the changed column, it will endup first.
CREATE TRIGGER `trigger` BEFORE UPDATE ON `table`
FOR EACH ROW
BEGIN
SET ROW.changed = NOW();
END$$
Try this ::
Select
MAX(`Date`), state from mytable
group by state
If you had been using postgres, you could compare different rows in the same table using "LEAD .. OVER" I have not managed to find the same functionallity in mysql.
A bit hairy, but I think this will do:
select min(t1.date) from table_1 t1 where
(select count(distinct state) from table_1 where table_1.date>=t1.date)=1
Basically, this asks for the first time no changes in state is found for any later values. Be warned, it may be this query scales terribly for large data sets....
I think your best choice here are analytical functions. Try this - it should be OK performance-wise:
SELECT *
FROM test
WHERE my_date = (SELECT MAX (my_date)
FROM (SELECT MY_DATE
FROM ( SELECT MY_DATE,
STATE,
LAG (state) OVER (ORDER BY MY_DATE)
lag_val
FROM test
ORDER BY MY_DATE) a
WHERE state != lag_val))
In the inner select, the LAG function gets the previous value in the STATE column and in the outer select I mark the date of a change - those with lag value different than the current state value. And outside, I'm getting the latest date from those dates of a change... I hope that this is what you needed.
SELECT MAX(DATE) FROM YOUR_TABLE
Above answer doesn't seem to satisfy what OP needs.
UPDATED ANSWER WITH AFTER INSERT/UPDATE TRIGGER
DELCARE #latestState varchar;
DELCARE #latestDate date;
CREATE TRIGGER latestInsertTrigger AFTER INSERT ON myTable
FOR EACH ROW
BEGIN
IF OLD.DATE <> NEW.date THEN
SET #latestState = NEW.state
SET #latestDate = NEW.date
END IF
END
;
CREATE TRIGGER latestUpdateTrigger AFTER UPDATE ON myTable
FOR EACH ROW
BEGIN
IF OLD.DATE = NEW.date AND OLD.STATE <> NEW.STATE THEN
SET #latestState = NEW.state
SET #latestDate = NEW.date
END IF
END
;
You may use the following query to get the latest record added/updated:
SELECT DATE, STATE FROM myTable
WHERE STATE = #latestState
OR DATE = #latestDate
ORDER BY DATE DESC
;
Results:
DATE STATE
November, 30 2012 00:00:00+0000 state one
November, 28 2012 00:00:00+0000 state two
November, 27 2012 00:00:00+0000 state two
The above query results needs to be limitted to 2, 3 or n based on what you need.
Frankly it seems like you want to get max from both columns based on the data sample you have given. Assuming that your state only increases with the date. Only I wish if the state was an integer :D
Then union of two max sub queries on both columns would have solved it easily. Still a string manipulation regex can find what's the max in state column. Finally this approach needs limit x. However it still has lope hole. Anyway it took me sometime to figure your need out :$

Update multiple rows from same table in mysql

Update a single column over multiple rows depending on the data from the same table.
update table1 set status=newtime
from (
select
case
when TIME_FORMAT( TIMEDIFF( ADDTIME( time_val, '120:00:00' ), NOW() ), '%Hh %im %ss')<0 then '4'
else '0'
end as newtime,
id as new_id
FROM table1
where id2='2'
and status='0'
)
where id=new_id
This is my query. Thanks in advance.
Edit:
This is an alternate query to achieve this. But it also gives me an error
update table1 set status=
(select
case when timeleft<0 then '4' else '0' end as something,
new_id
from
(
select
TIME_FORMAT( TIMEDIFF( ADDTIME( time_val, '120:00:00' ), NOW() ), '%Hh %im %ss') as newtime,
id as new_id
FROM
table1
where id2='2' and
status='0'
)
}
where id=new_id
"#1248 - Every derived table must have its own alias".
I cannot use alias as I am fetching two columns from the query. Any help would be great.
UPDATE statements have no FROM clause in MySQL syntax. However, you can JOIN table against the subquery.
UPDATE
table1 t1
JOIN (
select
case
when TIME_FORMAT( TIMEDIFF( ADDTIME( time_val, '120:00:00' ), NOW() ), '%Hh %im %ss')<0 then '4'
else '0'
end as newtime,
id as new_id
FROM table1
WHERE id2='2' AND status='0'
) tsub ON t1.id = tsub.new_id
SET status = tsub.newtime
It looks to me like you don't need to do any subquerying or joining at all. This should do what you want:
UPDATE table1
SET status = CASE WHEN TIME_FORMAT(TIMEDIFF(ADDTIME(time_val, '120:00:00'), NOW()), '%Hh %im %ss') < 0 THEN '4' ELSE '0' END
WHERE id2 = '2' AND status = '0'
In the query you wrote, your subquery will get back the new time_val and the id number of the row to update, for any rows that match the criteria id2 = '2' AND status = '0'. You will then update all those rows (that matched the above criteria) and set the status to the new time_val.
Instead of selecting them first, cut out the middle man and just update all rows that match that criteria with the new value. Your query will be faster and more straightforward.
Besides the simplified version (provided by #Travesty3), it seems you are using a whole bunch of date and time functions to test for a simple thing:
UPDATE table1
SET status = '4'
WHERE id2 = '2'
AND status = '0'
AND time_val < NOW() - INTERVAL 120 HOUR
We can update table with multiple row by same table or two different table in this manner, just posting a snippet of mysql code from my procedure
Update documentcolumns as tb1, documentcolumns as tb2 set tb1.documentColumnPos = tb2.documentColumnPos Where tb1.userID = user_id and tb2.userID is NULL and tb1.columnNameDefID= tb2.columnNameDefID and tb1.tabtype = tab_type and tb2.tabtype = tab_type;,