mysql shrink where clause - php

I have a table with 10 columns and 24 rows that can store a value or can be = 0. They are called msg1, msg2... up to msg10.
I am writing a query to update this table with 0 instead of a specific value in any of the fields.
So I wrote (59 is a test value and will be dinamically updated by php):
UPDATE schedule SET msg1='0' WHERE msg1='59' OR SET msg2='0' WHERE msg2='59'....
OR SET msg10='0' WHERE msg10='59'
This is obviously not working and anyway would be one of the less efficient query I have ever seen. So the question is: what is a clever way of iterating through all the rows and the columns, look for the specific value (i.e. 59) and if yes change it to 0?
I know that i can do it using a for loop but this will end up doing each time 24 query... not efficient too!

You're going to have to reference each column. I'd just do a full scan of the table, and update every row. An index isn't going to help, no need for a WHERE clause. Assuming those columns are character, use single quotes around the values. If those are numeric, you can omit the single quotes.
UPDATE schedule
SET msg1 = IF(msg1='59','0',msg1)
, msg2 = IF(msg2='59','0',msg2)
, msg3 = IF(msg3='59','0',msg3)
, ...
If you want to add a WHERE clause (which isn't really needed), you'd add it after the SET clause...
WHERE msg1='59'
OR msg2='59'
OR msg3='59'
I'd be tempted to write the statement so I only had to specify the value '59' and '0' just one time, like this:
UPDATE schedule s
CROSS
JOIN (SELECT '59' AS oldval, '0' AS newval) v
SET s.msg1 = IF(s.msg1=v.oldval,v.newval,s.msg1)
, s.msg2 = IF(s.msg2=v.oldval,v.newval,s.msg2)
, s.msg3 = IF(s.msg3=v.oldval,v.newval,s.msg3)

Related

How to check/update columns in a database contains about million rows?

I have MYSQL Database that contains about one Million (1,000,000) rows , I want to check all the rows and update some according to a condition , So for example I run a SQL statement like this:
select messageid from messages where messageid !=""
Then I fetch all the IDs and store them in a variable:
$existMessages;
Then I generate a 4 characters string 0-9a-z :
function generateRandomString($length = 4) {
return substr(str_shuffle(str_repeat($y='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', ceil($length/strlen($y)) )),1,$length);
}
Then I update the existing IDs with the generated strings , After checking that the generated IDs are unique.
This process becomes slower and takes the whole CPU as the rows are increasing.
Is there is a better way to do that ? Like using SQL statements directly in MYSQL? Or what to do ?
You should first put a unique or primary constraint on the column you want to be unique.
After that you can execute the update command
UPDATE TABLE_NAME SET COLMUN_NAME=generateRandomString() WHERE messageid !="";
You could use SQL directly, and create a simple hash based upon some data in the row, for instance:
UPDATE table_name SET messageid = MD5(messageid) WHERE messageid !="";
You might want to do that in batches, so add a LIMIT to the statement, ie. LIMIT 0,1000 to do bunch at a time.
You can use below update clause. This will pick 4 random letters and numbers for both lower and upper case and 0-9.
update table_name cross join (select
#chars:='1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIKLMNOPQRSTUVWXYZ') tab
set messageid = concat(substring(#chars, floor(rand()*61) + 1, 1),
substring(#chars, floor(rand()*61) + 1, 1),
substring(#chars, floor(rand()*61) + 1, 1),
substring(#chars, floor(rand()*61) + 1, 1)
)
where messageid !="";
I'll eliminate the need for the 'check', and make it faster.
There are not many more than a million such 4-character strings. So, there will be an annoying number of duplicates if you 'randomly' generate them.
Instead, I recommend
Generate all of them (or a million of them)
Shuffle them
Apply them to your table.
CONV(x, 10, 36) will generate a base-36 (0-9A-Z) value from x. But, The following may be better...
Build a table with 36 rows of 0..9,a..z.
CROSS JOIN it to itself 4 times to generate all 36^4 combos.
ORDER BY RAND() will shuffle them without leading to dups.
A multi-table UPDATE will let you copy the 4-char string from one table to another.

Update Current Row in MySQL Loop

I have a MySQL table with over 16 million rows and there is no primary key. Whenever I try to add one, my connection crashes. I have tried adding one as an auto increment in PHPMyAdmin and in shell but the connection is always lost after about 10 minutes.
What I would like to do is loop through the table's rows in PHP so I can limit the number of results and with each returned row add an auto-incremented ID number. Since the number of impacted rows would be reduced by reducing the load on the MySQL query, I won't lose my connection.
I want to do something like
SELECT * FROM MYTABLE LIMIT 1000001, 2000000;
Then, in the loop, update the current row
UPDATE (current row) SET ID='$i++'
How do I do this?
Note: the original data was given to me as a txt file. I don't know if there are duplicates but I cannot eliminate any rows. Also, no rows will be added. This table is going to be used only for querying purposes. When I have added indexes, however, there were no problems.
I suspect you are trying to use phpmyadmin to add the index. As handy as it is, it is a PHP script and is limited to the same resources as any PHP script on your server, typically 30-60 seconds run time, and a limited amount of ram.
Suggest you get the mysql query you need to add the index, then use SSH to shell in, and use command line MySQL to add your indexes.
If you don't have duplicate rows then the following way might shed some light:
Suppose you want to update the auto incremented value for first 10000 rows.
UPDATE
MYTABLE
INNER JOIN
(SELECT
*,
#rn := #rn + 1 AS row_number
FROM MYTABLE,(SELECT #rn := 0) var
ORDER BY SOME_OF_YOUR_FIELD
LIMIT 0,10000 ) t
ON t.field1 = MYTABLE.field1 AND t.field2 = MYTABLE.field2 AND .... t.fieldN = MYTABLE.fieldN
SET MYTABLE.ID = t.row_number;
For next 10000 rows just need to change two things:
(SELECT #rn := 10000) var
LIMIT 10000,10000
Repeat..
Note: ORDER BY SOME_OF_YOUR_FIELD is important otherwise you would get results in random order. Better create a function which might take limit,offset as parameter and do this job. Since you need to repeat the process.
Explanation:
The idea is to create a temporary table(t) having N number of rows and assigning a unique row number to each of the row. Later make an inner join between your main table MYTABLE and this temporary table t ON matching all the fields and then update the ID field of the corresponding row(in MYTABLE) with the incremented value(in this case row_number).
Another IDEA:
You may use multithreading in PHP to do this job.
Create N threads.
Assign each thread a non overlapping region (1 to 10000, 10001 to
20000 etc) like the above query.
Caution: The query will get slower in higher offset.

Mysql decrease value from another table

I have 2 tables: user_values and decrease_times.
user_values has 3 columns: username, property, value and decrease_times has 3 columns: property, decrease_value, decrease_time. All of these are dynamic and change a lot.
I have a cron job that gets called every hour, and what I want to do is reduce all the value rows from user_values with the amount stored in decrease_value if property from user_values is equal to property from decrease_times.
Also, decrease_time can be 0 or 1, 0 means it should decrease every 1 hour, and 1 means it should decrease every 24 hours. I already implemented an if-clause that detects if it is 6am, and then it should decrease all the values if they are 1 or 0.
What query should I make to do this? Also, this is done in PHP.
Edit: What I did so far is this: UPDATE user_values SET value = value - (SELECT decrease_value FROM decrease_times WHERE property=/* property from UPDATE clase should be here */) WHERE property=(SELECT property FROM decrease_times WHERE decrease_time=0)
You don't need to use a select statement anywhere...
UPDATE user_values AS values
INNER JOIN decrease_times AS times ON values.property = times.property
SET values.value = values.value-times.decrease_value
WHERE values.property = times.property AND times.decrease_time = 0
You are taking your table you're looking to edit, and joining it for the query with the second table... You set the value field on your user_values table using the simple maths equation you need, and ensure that both the property values are the same, and that you're only updating the rows that are meant to be updated every hour.
Your daily version would be the same, but wouldn't need "AND..." onwards
something like this UPDATE TABLE SET your_value=(your_value-1) WHERE USERID=5
or
UPDATE TABLE1 SET your_value=(your_value +(SELECT your_value2+5 FROM TABLE2 WHERE USERID=5)) WHERE USERID=5
-- adapt it to your needs; this is basic SQL syntax.
Need better example and your table structure -- you can use http://sqlfiddle.com/ and recreate some of your tables/data so we can play with it
http://www.w3schools.com/sql/sql_update.asp

How to make MySQL value Increase by one, using PHP

I have the following database table.
id article_content article_views
1 content.. 48
I am trying to increase the value of the column article_views using mysql query.
I know I can fetch the number and then add +1 and then update the value, but is there any better way to do this?
Thanks :)
Your query becomes -
Update TABLE_NAME SET article_views = article_views+1 WHERE id = 'your_id'

How to get ID of the last updated row in MySQL?

How do I get the ID of the last updated row in MySQL using PHP?
I've found an answer to this problem :)
SET #update_id := 0;
UPDATE some_table SET column_name = 'value', id = (SELECT #update_id := id)
WHERE some_other_column = 'blah' LIMIT 1;
SELECT #update_id;
EDIT by aefxx
This technique can be further expanded to retrieve the ID of every row affected by an update statement:
SET #uids := null;
UPDATE footable
SET foo = 'bar'
WHERE fooid > 5
AND ( SELECT #uids := CONCAT_WS(',', fooid, #uids) );
SELECT #uids;
This will return a string with all the IDs concatenated by a comma.
Hm, I am surprised that among the answers I do not see the easiest solution.
Suppose, item_id is an integer identity column in items table and you update rows with the following statement:
UPDATE items
SET qwe = 'qwe'
WHERE asd = 'asd';
Then, to know the latest affected row right after the statement, you should slightly update the statement into the following:
UPDATE items
SET qwe = 'qwe',
item_id=LAST_INSERT_ID(item_id)
WHERE asd = 'asd';
SELECT LAST_INSERT_ID();
If you need to update only really changed row, you would need to add a conditional update of the item_id through the LAST_INSERT_ID checking if the data is going to change in the row.
This is officially simple but remarkably counter-intuitive. If you're doing:
update users set status = 'processing' where status = 'pending'
limit 1
Change it to this:
update users set status = 'processing' where status = 'pending'
and last_insert_id(user_id)
limit 1
The addition of last_insert_id(user_id) in the where clause is telling MySQL to set its internal variable to the ID of the found row. When you pass a value to last_insert_id(expr) like this, it ends up returning that value, which in the case of IDs like here is always a positive integer and therefore always evaluates to true, never interfering with the where clause. This only works if some row was actually found, so remember to check affected rows. You can then get the ID in multiple ways.
MySQL last_insert_id()
You can generate sequences without calling LAST_INSERT_ID(), but the
utility of using the function this way is that the ID value is
maintained in the server as the last automatically generated value. It
is multi-user safe because multiple clients can issue the UPDATE
statement and get their own sequence value with the SELECT statement
(or mysql_insert_id()), without affecting or being affected by other
clients that generate their own sequence values.
MySQL mysql_insert_id()
Returns the value generated for an AUTO_INCREMENT column by the
previous INSERT or UPDATE statement. Use this function after you have
performed an INSERT statement into a table that contains an
AUTO_INCREMENT field, or have used INSERT or UPDATE to set a column
value with LAST_INSERT_ID(expr).
The reason for the differences between LAST_INSERT_ID() and
mysql_insert_id() is that LAST_INSERT_ID() is made easy to use in
scripts while mysql_insert_id() tries to provide more exact
information about what happens to the AUTO_INCREMENT column.
PHP mysqli_insert_id()
Performing an INSERT or UPDATE statement using the LAST_INSERT_ID()
function will also modify the value returned by the mysqli_insert_id()
function.
Putting it all together:
$affected_rows = DB::getAffectedRows("
update users set status = 'processing'
where status = 'pending' and last_insert_id(user_id)
limit 1"
);
if ($affected_rows) {
$user_id = DB::getInsertId();
}
(FYI that DB class is here.)
This is the same method as Salman A's answer, but here's the code you actually need to do it.
First, edit your table so that it will automatically keep track of whenever a row is modified. Remove the last line if you only want to know when a row was initially inserted.
ALTER TABLE mytable
ADD lastmodified TIMESTAMP
DEFAULT CURRENT_TIMESTAMP
ON UPDATE CURRENT_TIMESTAMP;
Then, to find out the last updated row, you can use this code.
SELECT id FROM mytable ORDER BY lastmodified DESC LIMIT 1;
This code is all lifted from MySQL vs PostgreSQL: Adding a 'Last Modified Time' Column to a Table and MySQL Manual: Sorting Rows. I just assembled it.
Query :
$sqlQuery = "UPDATE
update_table
SET
set_name = 'value'
WHERE
where_name = 'name'
LIMIT 1;";
PHP function:
function updateAndGetId($sqlQuery)
{
mysql_query(str_replace("SET", "SET id = LAST_INSERT_ID(id),", $sqlQuery));
return mysql_insert_id();
}
It's work for me ;)
SET #uids := "";
UPDATE myf___ingtable
SET id = id
WHERE id < 5
AND ( SELECT #uids := CONCAT_WS(',', CAST(id AS CHAR CHARACTER SET utf8), #uids) );
SELECT #uids;
I had to CAST the id (dunno why)... or I cannot get the #uids content (it was a blob)
Btw many thanks for Pomyk answer!
Hey, I just needed such a trick - I solved it in a different way, maybe it'll work for you. Note this is not a scalable solution and will be very bad for large data sets.
Split your query into two parts -
first, select the ids of the rows you want to update and store them in a temporary table.
secondly, do the original update with the condition in the update statement changed to where id in temp_table.
And to ensure concurrency, you need to lock the table before this two steps and then release the lock at the end.
Again, this works for me, for a query which ends with limit 1, so I don't even use a temp table, but instead simply a variable to store the result of the first select.
I prefer this method since I know I will always update only one row, and the code is straightforward.
ID of the last updated row is the same ID that you use in the 'updateQuery' to found & update that row. So, just save(call) that ID on anyway you want.
last_insert_id() depends of the AUTO_INCREMENT, but the last updated ID not.
My solution is , first decide the "id" ( #uids ) with select command and after update this id with #uids .
SET #uids := (SELECT id FROM table WHERE some = 0 LIMIT 1);
UPDATE table SET col = 1 WHERE id = #uids;SELECT #uids;
it worked on my project.
Further more to the Above Accepted Answer
For those who were wondering about := & =
Significant difference between := and =, and that is that := works as a variable-assignment operator everywhere, while = only works that way in SET statements, and is a comparison operator everywhere else.
So SELECT #var = 1 + 1; will leave #var unchanged and return a boolean (1 or 0 depending on the current value of #var), while SELECT #var := 1 + 1; will change #var to 2, and return 2.
[Source]
If you are only doing insertions, and want one from the same session, do as per peirix's answer. If you are doing modifications, you will need to modify your database schema to store which entry was most recently updated.
If you want the id from the last modification, which may have been from a different session (i.e. not the one that was just done by the PHP code running at present, but one done in response to a different request), you can add a TIMESTAMP column to your table called last_modified (see http://dev.mysql.com/doc/refman/5.1/en/datetime.html for information), and then when you update, set last_modified=CURRENT_TIME.
Having set this, you can then use a query like:
SELECT id FROM table ORDER BY last_modified DESC LIMIT 1;
to get the most recently modified row.
No need for so long Mysql code. In PHP, query should look something like this:
$updateQuery = mysql_query("UPDATE table_name SET row='value' WHERE id='$id'") or die ('Error');
$lastUpdatedId = mysql_insert_id();

Categories