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
Related
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.
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)
We have records with a count field on an unique id.
The columns are:
mainId = unique
mainIdCount = 1320 (this 'views' field gets a + 1 when the page is visited)
How can you insert all these mainIdCount's as seperate records in another table IN ANOTHER DBASE in one query?
Yes, I do mean 1320 times an insert with the same mainId! :-)
We actually have records that go over 10,000 times an id. It just has to be like this.
This is a weird one, but we do need the copies of all these (just) counts like this.
The most straightforward way to this is with a JOIN operation between your table, and another row source that provides a set of integers. We'd match each row from our original table to as many rows from the set of integer as needed to satisfy the desired result.
As a brief example of the pattern:
INSERT INTO newtable (mainId,n)
SELECT t.mainId
, r.n
FROM mytable t
JOIN ( SELECT 1 AS n
UNION ALL SELECT 2
UNION ALL SELECT 3
UNION ALL SELECT 4
UNION ALL SELECT 5
) r
WHERE r.n <= t.mainIdCount
If mytable contains row mainId=5 mainIdCount=4, we'd get back rows (5,1),(5,2),(5,3),(5,4)
Obviously, the rowsource r needs to be of sufficient size. The inline view I've demonstrated here would return a maximum of five rows. For larger sets, it would be beneficial to use a table rather than an inline view.
This leads to the followup question, "How do I generate a set of integers in MySQL",
e.g. Generating a range of numbers in MySQL
And getting that done is a bit tedious. We're looking forward to an eventual feature in MySQL that will make it much easier to return a bounded set of integer values; until then, having a pre-populated table is the most efficient approach.
I have a table with an value that I would like to be updated periodically with a cron job. However, I need to update the value by replacing it with a value from a different table. The issue is that I would like the replacement value to be chosen randomly.
For example, Table 1 has
ID Email
=================
1 bobatumail
Table 2 has:
ID Email
================
1 bobatumail
2 joeatumail
3 peteatumail
4 biffatumail
5 wilneratumail
6 wilsonatumail
I would like the query to replace bobatumail in Table 1 with any of the other values in Table 2 as long as it is random. It could even be the same value as in Table 1.
Any idea how to do this?
In MySQL you could use the REPLACE statement:
REPLACE INTO table1 (ID, Email)
SELECT 1, table2.Email FROM table2 ORDER BY RAND() LIMIT 1;
The "1" in the second line represents the id of the entry while the second part returns a random value out of table2. Yes, there are solutions using the UPDATE statement (JOIN and ANSI) but its always tricky and you usually have to turn off safe update mode.
http://dev.mysql.com/doc/refman/5.5/en/mysql-command-options.html#option_mysql_safe-updates
Please note that REPLACE first deletes the old entry and then reinserts the new one.
http://dev.mysql.com/doc/refman/5.5/en/replace.html
I have a number which is in this form : 2012-01 (2012 as current year) and 01 is just a the maximum value of a field in my database incremented by 1, and each year that number is reset to 0.
but if there are two users that try to do the same operation at the same time the value is the same for both and thus i get the same number inserted twice in my database .
I thought of creating a sequence but that requires a job that resets the sequence each year and i would prefer if there is a way to make a lock before i get the next number and
release it after an insert is done ?
Thanks.
CREATE UNIQUE INDEX index_name ON table_name (column_name);
or
ALTER TABLE table_name ADD CONSTRAINT constraint_name UNIQUE (column_name);
You don't specify where you store the field that is used as the counter. But maybe it is possible to use a SELECT FOR UPDATE statement.
Before you increment the value of your counter field by 1 you can lock that record by using a SELECT FOR UPDATE. Then update the counter.
Something like this, assuming the table has only 1 record:
SELECT *
FROM CounterTable
FOR UPDATE;
UPDATE CounterTable
SET Counter = Counter + 1;
COMMIT;
If one session (user) has done the SELECT FOR UPDATE and not yet committed or rolled back, the other session (user) doing a SELECT FOR UPDATE will block waiting to be able to get a lock. This prevents two users from getting the same number.