SELECT & UPDATE according to status in one sql - mysql

I am wondering how to do this:
At the moment I have this, as you can see I am selecting all the status 0 & 2, then later in the code updating status 0 to 97 and status 2 to 99
SELECT id_queue, status FROM table WHERE status IN (0,2) ORDER BY status, id_queue ASC FOR UPDATE;
UPDATE table SET status = 97 WHERE id_queue= " + id_sms + ";
UPDATE table SET status = 99 WHERE id_queue= " + id_sms + ";
I want to SELECT & UPDATE, and also get the row ids of id_queue for later use
Can anyone help?
Much appreciated.

UPDATE table
SET status = CASE status WHEN 0 THEN 97
WHEN 2 THEN 98
END
WHERE status IN (0, 2)
If you want to save the id_queue values, then you'll either need to create a temp table, or store them in your program outside the database. I'd do the second for most reasonably sized set - a lot less housekeeping if you can keep them in scope in your program.

Related

MySQL Trigger Function (between 3 different tables)

I have 3 tables (written as important parts, not all data):
tableDeviceStatus:
device_id status
--------- ------
device01 15
device02 20
tableDeviceTrigger:
device_id device_operator device_param triggered_device command
--------- --------------- ------------ ---------------- -------
device01 > 22 device80 1
device01 < 18 device87 0
device02 = 1 device89 1
tableDeviceCommand:
device_id command
--------- -------
device80
device87
device89
Flow:
When tableDeviceStatus updated I will create trigger (AFTER)
If a device updated, check tableDeviceTrigger and compare by operators and update tableDeviceCommand
For example:
device01 updated as 25.
there is 2 comparison. Check both and since 25>22, update tableDeviceCommand.device80 as 1.
I have tried trigger but couldn't solve search, compare and execute. (because a device can take more than one comparison. I nearly created for one row but could't formulize for like for each rows and execute)
Welcome to Stack Overflow! If I understand correctly, for each new row entered in tableDeviceStatus, you want to test each row with a matching #device_id in tableDeviceTrigger.
For each matching row in tableDeviceTrigger, you test if it meets the requirements (#status #device_operator #device_param is TRUE). For each row that meets these requirements, you add a row in tableDeviceCommand with the #triggered_device and #command.
Does something like this work?
INSERT INTO tableDeviceCommand (device_id, command)
-- maybe REPLACE INTO or UPDATE depending on your requirements
(
SELECT tdt.triggered_device, tdt.command
FROM tableDeviceStatus tds
INNER JOIN tableDeviceTrigger tdt on tds.device_id = tdt.device_id
WHERE
(tdt.device_operator = ">" and tds.status > tdt.device_param) OR
(tdt.device_operator = "<" and tds.status < tdt.device_param) OR
(tdt.device_operator = "=" and tds.status = tdt.device_param)
-- add additional ORs to test the comparison specified by device_operator
)

unpaid monthly salaries (mysql +vb.net)

I have a MySQL table some thing like that
NAME salary amount month 1 month 2 month 3 month 4
john 300 300 300 0 0
maria 400 400 0 0 0
tom 380 380 380 380 0
I wanna see results in table or list view or whatever like that
name unpaid month salary amount
john month 3 300
john month 4 300
maria month 2 400
maria month 3 400
maria month 4 400
tom month 4 380
I tried code like:
sql1="select name,month1 from table where month1=0 "
sql2="select name,month2 from table where month2=0"
sql3="select name,month3 from table where month3=0"
sql4="select name,month4 from table where month4=0"
Dim Sql = String.Concat(sql1, ";", sql2 ,";",sql2,";",sql4 )
but didn't work , any help pls ?
The syntax looks a little off in your code that you have now. I do not know if this is the exact code that you have in your program, but when the SQL statements are not properly formatted nothing will happen. I have made some changes to show what may be the issue.
sql1="SELECT name, month1, amount FROM table WHERE month1=0"
sql2="SELECT name, month2, amount FROM table WHERE month2=0"
sql3="SELECT name, month3, amount FROM table WHERE month3=0"
sql4="SELECT name, month4, amount FROM table WHERE month4=0"
Dim Sql = String.Concat(sql1, ";", sql2 ,";",sql2,";",sql4 )
The issue that I see with your current formatting is that you may want to have two separate tables for the name and the pay. With the separate tables you could then use foreign keys and join the two tables to have each name on every month with the amount they were paid that month. You would also be able to group the users based off of their name using GROUP BY
With the restructured table your call would be as simple as the statement below. Since I do not know your table names I have made fake ones for them.
Dim Sql = "SELECT NameTable.name, MonthTable.month, MonthTable.amount
FROM NameTable INNER JOIN MonthTable
ON {prmarykey for name} = {foreign key for month}
GROUP BY NameTable.name"
This should give you the result that you are looking for. Let me know if you have any questions or need clarification.
Try the UNION mysql aggregate :
sql1="select name,month1 as unpaid_month from table where month1=0 "
sql2="select name,month2 as unpaid_month from table where month2=0"
sql3="select name,month3 as unpaid_month from table where month3=0"
sql4="select name,month4 as unpaid_month from table where month4=0"
Dim Sql = String.Concat(sql1, " UNION ", sql2 ,"UNION ",sql2," UNION ",sql4
How you could use spaces before column names.
I would suggest you to use UNION.
But, here's the least version of SQL;
sql = "SELECT NAME, month1 as unpaid_month, salary_amount FROM tablename WHERE month1 = 0"
sql = sql & " UNION "
sql = sql & "SELECT NAME, month2 as unpaid_month, salary_amount FROM tablename WHERE month2 = 0"
sql = sql & " UNION "
sql = sql & "SELECT NAME, month3 as unpaid_month, salary_amount FROM tablename WHERE month3 = 0"
sql = sql & " UNION "
sql = sql & "SELECT NAME, month4 as unpaid_month, salary_amount FROM tablename WHERE month4 = 0"
But, the query is not good enough. What will happen if someone is paid half of its salary. And why you should get more than one record for a single person? Shouldn't there be any SUM for salary_amount and string concatenation for unpaid_month?
As it wasn't the part of your question, I can't post the advanced SQL here. Please ask for it in comment if you want that.
Please read these functions SUM() and GROUP_CONCAT() with temporary table AS TABLE. I think that you should use them for good programming.

Add Column in Table with conditional in block WHERE

I was doing a query with MySQL to save all objects returned, but I'd like identify these objects based in statements of the block WHERE, that is, if determined object to satisfy the specific characteristic I'd like create one column and in this column I assignment the value 0 or 1 in the row corresponding the object if it satisfy or not satisfy these characteristic.
This is my script:
SELECT
s.id, al.ID, al.j, al.k, al.r, gal.i
FROM
datas as al
WHERE
AND s.id = al.ID
AND al.j between 1 and 1
AND al.k BETWEEN 15 and 16
AND al.r BETWEEN 67 and 72
The script above is working perfectly and I can to save all objects which it return.
So, I'd like to know if is there a way add in the query above, on block WHERE, the following statement,
( Flags & (dbo.environment('cool') +
dbo.environment('ok') -
dbo.environment('source')) ) = 25
and ((al_pp x al_pp1)-0.5/3=11
and determined the objects that satisfy or not these condition with 0 or 1 in a new column created in Table saved.
I read some tutorials about this and saw some attempts with IF, CASE, ADD COLUMN or WHEN, but none of these solved.
Thanks in advance
MySQL has if function, see here
So you can simply use it in your query:
SELECT IF(( Flags & (dbo.fPhotoFlags('SATURATED') +
dbo.fPhotoFlags('BRIGHT') +
dbo.fPhotoFlags('EDGE')) ) = 0
and petroRad_r < 18
and ((colc_u - colc_g) - (psfMag_u - psfMag_g)) < -0.4
, 1 --// VALUE IF TRUE
, 0 --// VALUE IF FALSE
) as conditional_column, ... rest of your query

Update query for 110 million records won't finish

We are using MySQL.
We have a very long table (110m * 7) and we regularly add in new records to this table (when doing that, we upload csv files so never a really painful process even large # of records).
Now we need to add a new column to this table to distinguish the records in some way. To be specific, the most recent added about 10 million records will be flagged as type 2 and all the old records will be flagged as type 1. And in the future, we will be putting in new records for both types.
At first, we tried the approach 1 as below, but it ran for more than 24 hours without complaining anything and the host server has always been responsive.
-- ====================================================
-- start of approach 1
-- ====================================================
-- Add column.
ALTER TABLE
bond_price
ADD
bp_name_version SMALLINT
;
-- Set value 1 for BOND_CHARACTER types.
UPDATE
bond_price
SET
bp_name_version = 1
WHERE
bp_serial_id < 107480325
;
-- Set value 2 for BOND_CHARACTER_EIKON types.
UPDATE
bond_price
SET
bp_name_version = 2
WHERE
bp_serial_id >= 107480325
;
-- Set a NOT NULL constrain on the new column
ALTER TABLE
bond_price
ALTER
bp_name_version SET NOT NULL
;
-- ====================================================
-- END of approach 1
-- ====================================================
We lost our faith after 24 hours, thinking setting a conditional clause might has made it difficult for such a long table. So we tried approach 2 which is doing approach 1 step by step and without conditional clause.
So we firstly execute the following query and it finished within seconds.
-- Add column.
ALTER TABLE
bond_price
ADD
bp_name_version SMALLINT
;
We then execute the following query to put value 1 to all records, hoping later on we can change the value for only 10 million records to 2.
-- Set value 1 for all records.
UPDATE
bond_price
SET
bp_name_version = 1;
But this query has run for over 24 hours till now, again without complaining for anything.
We have been monitoring the server by:
select * from pg_stat_activity;
And the 'Set value 1' query is still active and the server is still very responsive.
OUR QUESTIONS:
Is this speed what one should expect, considering the record number is over 100 million?
Is there a possibility that this query will not work but just get stuck forever? Is there anyway to tell?
Is there anyway to improve the speed? Or do it a different way?
Many thanks in advance!
I don't know the basis of the data, but 110+ million records in one push is probably a bit nuts.
Why not trying to find some other criteria, do a loop, and do them in smaller chunks... maybe based on some "Add" Date, or some other field in the structure. Or, just use the bp_serial_id, such as (again, handled by a loop)
pseudo-code to update 1 million max at a time
maxSerialForType1 = 107480325
for cycle = 0 to 110
startSerialID = cycle * 1000000
endSerialID = (cycle +1 ) * 1000000
if startSerialID < maxSerialForType1
UPDATE bond_price
SET bp_name_version = 1
WHERE bp_serial_id < maxSerialForType1
AND bp_serial_id >= startSerialID
AND bp_serial_id < endSerialID
end for update type 1
if startSerialID > maxSerialForType1
UPDATE bond_price
SET bp_name_version = 2
WHERE bp_serial_id > maxSerialForType1
AND bp_serial_id >= startSerialID
AND bp_serial_id < endSerialID
end for update type 2
end of loop
So this, if made like a stored procedure will update the 110 million for you.
I would then make a suggestion, that when importing new records into a temp table with the bp_name_version value assigned there, THEN pull them into the final table so you don't have to go through trying to update 110+ million each time.

Mysql query to skip rows and check for status changes

I'm building a mysql query but I'm stuck... (I'm logging each minute)
I have 3 tables. Logs, log_field, log_value.
logs -> id, create_time
log_value -> id, log_id,log_field_id,value
log_field -> id, name (one on the entries is status and username)
The values for status can be online,offline and idle...
What I would like to see is from my query is:
When in my logs someone changes from status, I want a row with create_time, username, status.
So for a given user, I want my query to skip rows until a new status appears...
And I need to be able to put a time interval in which status changes are ignored.
Can someone please help ?
Although you have nothing to differentiate an actual "User" (such as by user ID) listed in your post, and what happens if you have two "John Smith" names.
First, an introduction to MySQL #variables. You can think of them as an inline program running while the query is processing rows. You create variables, then change them as each row gets processed, IN THE SAME order as the := assignment in the field selection occurs which is critical. I'll cover that shortly.
Fist an initial premise. You have a field value table of all possible fields that can/do get logged. Of which, two of them exist... one is for the user's name, another for the status you are looking a log changed. I don't know what those internal "ID" numbers are, but they would have to be fixed values per your existing table. In my scenario, I am assuming that field ID = 1 is for the User's Name, and field ID 2 = status column... Otherwise, you would need two more joins to get the field table just to confirm which field was the one you wanted. Obviously my "ID" field values will not match your production tables, so please change those accordingly.
Here's the query...
select FinalAlias.*
from (
select
PQ.*,
if( #lastUser = PQ.LogUser, 1, 0 ) as SameUser,
#lastTime := if( #lastUser = PQ.LogUser, #lastTime, #ignoreTime ) as lastChange,
if( PQ.create_time > #lastTime + interval 20 minute, 1, 0 ) as BeyondInterval,
#lastTime := PQ.create_time as chgTime,
#lastUser := PQ.LogUser as chgUser
from
( select
ByStatus.id,
l.create_time,
ByStatus.Value LogStatus,
ByUser.Value LogUser
from
log_value as ByStatus
join logs l
on ByStatus.log_id = l.id
join log_value as ByUser
on ByStatus.log_id = ByUser.log_id
AND ByUser.log_field_id = 1
where
ByStatus.log_field_id = 2
order by
ByUser.Value,
l.create_time ) PQ,
( select #lastUser := '',
#lastTime := now(),
#ignoreTime := now() ) sqlvars
) FinalAlias
where
SameUser = 1
and BeyondInterval = 1
Now, what's going on. The inner-most query (result alias PQ representing "PreQuery") is just asking for all log values where the field_id = 2 (status column) exists. From that log entry, go to the log table for it's creation time... while we're at it, join AGAIN to the log value table on the same log ID, but this time also look for field_id = 1 so we can get the user name.
Once that is done, get the log ID, Creation time, Status Value and Who it was for all pre-sorted on a per-user basis and sequentially time oriented. This is the critical step. The data must be pre-organized by user/time to compare the "last" time for a given user to the "next" time their log status changed.
Now, the MySQL #variables. Join the prequery to another select of #variables which is given an "sqlvars" query alias. This will pre-initialize the variables fo #lastUser, #lastTime and #ignoreTime. Now, look at what I'm doing in the field list via section
if( #lastUser = PQ.LogUser, 1, 0 ) as SameUser,
#lastTime := if( #lastUser = PQ.LogUser, #lastTime, #ignoreTime ) as lastChange,
if( PQ.create_time > #lastTime + interval 20 minute, 1, 0 ) as BeyondInterval,
#lastTime := PQ.create_time as chgTime,
#lastUser := PQ.LogUser as chgUser
This is like doing the following pseudo code in a loop for every record (which is already sequentially ordered by same person and their respective log time
FOR EACH ROW IN RESULT SET
Set a flag "SameUser" = 1 if the value of the #lastUser is the same
as the current person record we are looking at
if the last user is the same as the previous record
use the #lastTime field as the "lastChange" column
else
use the #ignore field as the last change column
Now, build another flag based on the current record create time
and whatever the #lastTime value is based on a 20 minute interval.
set it to 1 if AT LEAST the 20 minute interval has been meet.
Now the key to the cycling the next record.
force the #lastTime = current record create_time
force the #lastUser = current user
END FOR LOOP
So, if you have the following as a result of the prequery... (leaving date portion off)
create status user sameuser lastchange 20minFlag carry to next row compare
07:34 online Bill 0 09:05 0 07:34 Bill
07:52 idle Bill 1 07:34 0 07:52 Bill
08:16 online Bill 1 07:52 1 08:16 Bill
07:44 online Mark 0 09:05 0 07:44 Mark
07:37 idle Monica 0 09:05 0 07:37 Monica
08:03 online Monica 1 07:37 1 08:03 Monica
Notice first record for Bill. The flag same user = 0 since there was nobody before him. The last change was 9:05 (via the NOW() when creating the sqlvars variables), but then look at the "carry to next row compare". This is setting the #lastTime and #lastUser after the current row was done being compared as needed.
Next row for Bill. It sees he is same as last user previous row, so the SameUser flag is set to 1. We now know that we have a good "Last Time" to compare against the current record "Create Time". So, from 7:34 to 7:52 is 18 minutes and LESS than our 20 minute interval so the 20 minute flag is set to 0. Now, we retain the current 7:52 and Bill for third row.
Third row for Bill. Still Same User (flag=1), last change of 7:52 compared to now 8:16 and we have 24 minutes... So the 20 minute flag = 1. Retain 8:16 and Bill for next row.
First row for Mark. Same User = 0 since last user was Bill. Uses same 9:05 ignore time and don't care about 20 min flag, but now save 7:44 and Mark for next row compare.
On to Monica. Different than Mark, so SameUser = 0, etc to finish similar to Bill.
So, now we have all the pieces and rows considered. Now, take all these and wrap them up as the "FinalAlias" of the query and all we do is apply a WHERE clause for "SameUser = 1" AND "20 Minute Flag" has been reached.
You can strip down the final column list as needed, and remove the where clause to look at results, but be sure to add an outer ORDER BY clause for name/create_time to see similar pattern as I have here.