I have this query that increments a value index in existing rows ordered by the value in the column key.
UPDATE `documents`, (
SELECT #row_number:=ifnull(#row_number, 0)+1 as `new_index`, `id`
FROM `documents`
WHERE `path` = "/path/to/doc"
ORDER BY `key`
) AS `table_position`,
(
SELECT #row_number:=0
) AS `rowNumberInit`
SET `index` = `table_position`.`new_index`
WHERE `table_position`.`id` = `documents`.`id`
and I use this PHP code to execute it:
/** #var PDO $pdo */
$ret = $pdo->query($sql);
// Now every value in column `index` is set to 1
$res = $ret->execute();
// Now every value in column `index` is counted up
This doesn't look quite like the right way to do it.
I currently use PDO directly, because Zend_Db_Adapter_Pdo_Mysql seems to wreak havoc with this query.
In addition to that I'd like to have the "/path/to/doc" string in the WHERE clause a a bind param. Replacing it by a ? and passing the value to execute() didn't work.
How would I do this correctly with Zend or PDO?
Related
I have drop down where the drop down is list of namaJabatan
my table - infojawatan
ID - PK of the table
namaJabatan - where the condition appear ($search - its up to where the user select from Dropdown)
tarikhKemaskini - where i want to get the latest date of row
my query
$sql = "SELECT * FROM infojawatan WHERE namaJabatan = '$search' && tarikh Kemaskini IN (SELECT MAX(tarikhKemaskini) FROM infojawatan GROUP BY ID)";
$sql_rs = mysql_query($sql);
while($row_Sql = mysql_fetch_array($sql_rs)) {
$tarikhKemaskini = $row_Sql['tarikhKemaskini'];
}
echo "Current Date :" .$tarikhKemaskini;
You have a few syntax errors in your SQL.
SQL spells out AND, not &&.
tarikhKemaskini is one word.
SELECT *
FROM infojawatan
WHERE namaJabatan = :namaJabatan AND
tarikhKemaskini IN (
SELECT MAX(tarikhKemaskini)
FROM infojawatan
GROUP BY ID
)
Note carefully that I used :namaJabatan there instead of hard coding $search. Hard coding variables into SQL leaves you open to a SQL Injection Attack where a malicious attacker can craft a search query that lets them get more information than they're allowed to, or even run arbitrary SQL queries.
Instead, use parameters, the :namaJabatan there, and pass your variables in when you execute the query.
Unfortunately the mysql_query interface doesn't support this. Fortunately it was deprecated and there are now better interfaces. Here's a breakdown. I'd recommend using PDO as it is a generic interface applicable to any SQL database. Then you can use the more secure and efficient prepared statements with bind parameters.
$stmt = $dbh->prepare("
SELECT *
FROM infojawatan
WHERE namaJabatan = :namaJabatan AND
tarikhKemaskini IN (
SELECT MAX(tarikhKemaskini)
FROM infojawatan
GROUP BY ID
)
")
$stmt->execute(array( ':namaJabatan' => $search));
while( $row = $stmt->fetch() ) {
echo $row['tarikhKemaskini'];
}
I have this scenario that I can't figure out:
Inside table wp_comment I need to list all user_id (not duplicate) with comment_type=complete.
I tried this:
$results = $GLOBALS['wpdb']->get_results( "SELECT * FROM wp_comments WHERE comment_type='sensei_course_status' AND comment_approved='complete'", ARRAY_A );
$corsisti = $results[user_id];
// I need to print only ids to put this array in
get_users( include=> '$corsisti' )
The database screenshot:
You can use the wpdb::get_col() method to retrieve an array with values from a single column:
$corsisti = $GLOBALS['wpdb']->get_col( "SELECT `user_id` FROM wp_comments WHERE comment_type='sensei_course_status' AND comment_approved='complete'");
Then simply use the result in get_users (you do not need the quotes):
$users = get_users( include=> $corsisti );
I want my the id field in my table to be a bit more " random" then consecutive numbers.
Is there a way to insert something into the id field, like a +9, which will tell the db to take the current auto_increment value and add 9 to it?
Though this is generally used to solve replication issues, you can set an increment value for auto_increment:
auto_increment_increment
Since that is both a session and a global setting, you could simply set the session variable just prior to the insert.
Besides that, you can manually do it by getting the current value with MAX() then add any number you want and insert that value. MySQL will let you know if you try to insert a duplicate value.
You have a design flaw. Leave the auto increment alone and shuffle your query result (when you fetch your data)
As far as i know, it's not possible to 'shuffle' your current IDs. If you wanted though, you could pursue non-linear IDs in the future.
The following is written in PDO, there are mysqli equivalents.
This is just an arbitrary INSERT statement
$name = "Jack";
$conn = new PDO("mysql:host=$dbhost;dbname=$dbname",$dbuser,$dbpass);
$sql = "INSERT INTO tableName (name) VALUES(:name)";
$q = $conn->prepare($sql);
$q->execute(':name' => $name);
Next, we use lastInsertId() to return the ID of the last inserted row, then we concatenate the result to rand()
$lastID = $conn->lastInsertId();
$randomizer = $lastID.rand();
Finally, we use our 'shuffled' ID and UPDATE the previously inserted record.
$sql = "UPDATE tableName SET ID = :randomizer WHERE ID=:lastID ";
$q = $conn->prepare($sql);
$q->execute(array(':lastID' => $lastID , ':randomizer' => $randomizer));
An idea.. (Not tested)
CREATE TRIGGER 'updateMyAutoIncrement'
BEFORE INSERT
ON 'DatabaseName'.'TableName'
FOR EACH ROW
BEGIN
DECLARE aTmpValueHolder INT DEFAULT 0;
SELECT AUTO_INCREMENT INTO aTmpValueHolder
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'DatabaseName'
AND TABLE_NAME = 'TableName';
SET NEW.idColumnName =aTmpValueHolder + 9;
END;
Edit : If the above trigger doesn't work try to update AUTO_INCREMENT value directly into the system's schema. But as noted by Eric, your design seems to be flawed. I don't see the point of having an auto-increment here.
Edit 2 : For a more 'random' and less linear number.
SET NEW.idColumnName =aTmpValueHolder + RAND(10);
Edit 3 : As pointed out by Jack Williams, Rand() produces a float value between 0 and 1.
So instead, to produce an integer, we need to use a floor function to transform the 'random' float into an integer.
SET NEW.idColumnName =aTmpValueHolder + FLOOR(a + RAND() * (b - a));
where a and b are the range of the random number.
I'm using $this->db->update(); to create an update query that adds the value stored in a variable, $amount, to the value in a column, count. My function call currently looks like this:
$data = array('count' => 'count + '.$amount);
$this->db->where('id', $item_id);
$this->db->update('items', $data);
However, this generates the following broken SQL:
UPDATE `items` SET `count` = 'count + 2' WHERE `id` = '2'
Is there a way to generate the SET clause without the quotes around count + 2?
Thanks, Maxime Morin, for putting me on the right track. According to the CodeIgniter Documentation, you can create a "set" clause without quotes by setting the optional $escape parameter to FALSE. Thus, the solution to my problem was:
$this->db->set("count", "count + $amount", FALSE);
$this->db->where("id", $item_id);
$this->db->update("items", $data);
Here's the work-around I used until I found my accepted solution
$query = $this->db->query("UPDATE `items` SET `count` = `count` + $amount WHERE `id` = $item_id");
Is it safe to use the following code to prevent race conditions? (key and status fields and mysql_affected_rows are used to implement locking)
$mres = mysql_query("SELECT `values`, `key`, `status`
FROM `test`
WHERE `id` = 1");
$row = mysql_fetch_array($mres);
if($row['status'] != UPDATING) {
$mres = mysql_query("UPDATE `test` SET
`status` = UPDATING,
`key` = `key` + 1
WHERE `id` = 1 AND `key` = ".$row['key']);
if($mres && mysql_affected_rows()) {
//update here safely and then...
mysql_query("UPDATE `test` SET
`status` = NOT_UPDATING,
`key` = `key` + 1
WHERE `id` = 1");
}
}
My test shows that either it is not safe or I should search for a well-hidden mistake in my code.
Table is MyISAM
You should "acquire the lock" first before you retrieve values. Otherwise they may change before you get the lock.
$mres = mysql_query("UPDATE `test` SET
`status` = 'UPDATING'
WHERE `id` = 1 AND `status` = 'NOT_UPDATING'");
if ($mres && mysql_affected_rows()) {
// got the lock
// now select and update
}
id better be a unique field in the db or things may behave very weird
I couldn't see a reason to increment key
notice I quoted the strings 'UPDATING' and 'NOT_UPDATING' in sql
in your code, you should have also checked that $row['status'] had a meaningful value(what if it was false/null?) before comparing to the php constant UPDATING
hopefully you understand enough php to know that php strings should be quoted.
You can check for GET_LOCK and RELEASE_LOCK functions in MySql to simulate row locks.
http://dev.mysql.com/doc/refman/5.1/en/miscellaneous-functions.html#function_get-lock
With this approach you don't need to update rows. Also with mysql_affected_rows() if something goes wrong you may finish with always locked row (for example if you script crash before releasing row by updating it status to NOT_UPDATING). Locks granted with GET_LOCK are released automatically when connection is terminated.