Currently I'm manually creating a string where I concatenate all the values in each row in my table. I'm hashing this string for each row to get a hash value for the current values (/status) of the row, which I'm later is using to determine if the row has changed.
Instead of doing this manually, is there an build-in way i mySQL to get a unique hash value for each row?
you could do something like
SELECT MD5(concat(field1, field2, field3, ...)) AS rowhash
but you can't get away from listing which fields you want, as concat(*) is not an option (syntax error).
It's better to use concat_ws(). e.g. two adjacent column: 12,3 => 1,23 .
Sorry, this still has some problems. Think about the null value, empty string, string can contain ',', etc...
A program is required to generate the hash statement, which should replace null to specific value (for null-able columns), and also use the seldom used char/byte as separator.
There are problems with CONCAT, e.g. CONCAT('ab', 'c') vs CONCAT('a', 'bc'). Two different rows, but result is the same. You could use CONCAT_WS(';', 'ab', 'c') to get ab;c but in case of CONCAT_WS(';', ';', '') vs CONCAT_WS(';', '', ';') you still get the same result.
Also CONCAT(NULL, 'c') returns NULL.
I think the best way is to use QUOTE:
SELECT MD5(CONCAT(QUOTE(c1), QUOTE(c2), QUOTE(c3))) AS row_hash FROM t1;
Result of: select (concat(quote('a'), quote('bc'), quote('NULL'), quote(NULL), quote('\''), quote('')));
is: 'a''bc''NULL'NULL'\''''
Also, don't use GROUP_CONCAT() to get hash of table, it has limit: https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_group_concat_max_len
Instead, CHECKSUM TABLE might be better, but you can't skip columns with CHECKSUM TABLE https://dev.mysql.com/doc/refman/5.7/en/checksum-table.html
Well I made a little script that could do excactly what you want, and maybe what others want... so here it goes...for PHP that is...
first you have to make a list of columns of the table, then you make a "case when" statement for each column based on their type and put that in the concat_ws statement and then you hash it with sha1...i've used this method on very large tables (600000+ records) and the speed is quite good when selecting all records. also I think that it is faster to concat the required data in a concat_ws and explode it in php or whatever you are using, but that is just a hunch...
<?
$query= mysql_query("SHOW COLUMNS FROM $table", $linklive);
while ($col = mysql_fetch_assoc($query)) {
$columns[] = mysql_real_escape_string($col['Field']);
if ($col['Key'] == 'PRI') {
$key = mysql_real_escape_string($col['Field']);
}
$columnsinfo[$col['Field']] = $col;
}
$dates = array("date","datetime","time");
$int = array("int","decimal");
$implcols = array();
foreach($columns as $col){
if(in_array($columnsinfo[$col]['Type'], $dates)){
$implcols[] = "(CASE WHEN (UNIX_TIMESTAMP(`$col`)=0 || `$col` IS NULL) THEN '[$col EMPTY]' ELSE `$col` END)";
}else{
list($type, $rest) = explode("(",$columnsinfo[$col]['Type']);
if(in_array($columnsinfo[$col]['Type'], $dates)){
$implcols[] = "(CASE WHEN ( `$col`=0 || `$col` IS NULL ) THEN '[$col EMPTY]' ELSE `$col` END)";
}else{
$implcols[] = "(CASE WHEN ( `$col`='' || `$col` IS NULL ) THEN '[$col EMPTY]' ELSE `$col` END)";
}
}
}
$keyslive = array();
//echo "SELECT $key SHA1(CONCAT_WS('',".implode(",", $columns).")) as compare FROM $table"; exit;
$q = "SELECT $key as `key`, SHA1(CONCAT_WS('',".implode(", ",$implcols).")) as compare FROM $table";
?>
Related
I have the following code which force me to order values from lowest to highest.
$values = '4|2|7|1|20';
$test = $db->QueryFetchArrayAll("SELECT id FROM user WHERE (CONCAT(',', id, ',') REGEXP ',($values),')");
foreach ($test as $test_as) {
echo $test_as['id'].',';
}
// Output:
1,2,4,7,20
// Should be same as string values:
4,2,7,1,20
How I can stop ordering it by default and make by values order?
Add a
ORDER BY FIELD(id,4,2,7,1,20);
To your SELECT.
You still should use prepared stetement when you are4 testing.
I was told that I could check whether a SELECT statement finds a column with the syntax
$rows = query( "SELECT * FROM tbl WHERE id = idx");
if ( $rows == false )
and it seems to work.
Anyway, if I check if ( $rows == 0 ) it doesn't return the same value.
Shouldn't 0 and false be the same (apart from the type, of course)?
What's the actual value returned by the query when it finds no row? I ask because it doesn't seems to be false, since the statement var_dump( $rows === false ) prints false..
***EDIT: I'm sorry guys, query() was a function from a library someone else wrote and I had no idea (i'm starting now with sql...). It simply excutes an SQL statement, returning an array of all rows in result set or false on (non-fatal) error (like row not found).
I have still a little question, though.
The function returns false when it finds no row, so shouldn't I be able to catch that with if ( $rows === false )?
Why var_dump(false) doens't print me out anything, while var_dump(true) prints me out 1?
I'm not pretty sure if you use simple mysql_* functions, MySQLi or PDO but in any case $rows is not returning the number of resulting rows. It is a boolean value / object returned / created depending of success of your query.
$sql = $mysqli->query("SELECT * FROM tbl WHERE id='1'");
if(!$sql->error)
$number_of_rows = $sql->num_rows; // for sure it will output 1
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");
I have three tables in a mysql database . Deseasetype(DTID,TypeName) , Symptom(SID, SymptomName, DTID) , Result(RID, SID1, SID2, SID3, result).1st two table, i think is clear enough.
In result table: there will be combination's of symtoms and any values of SymID1/ SymID2/ SymID3 can be null. here i send a picture of the table result.
I want to input some symptom and output will be the result from the 'Result' table.
For that i wrote this query:
$query = "select Result from result where (result .SID1= '$symptom1') AND (result.SID2= '$symptom2' ) AND (result.SID3 = '$symptom3')";
This work only when three symptom's have value. but if any of the symptom's are null, then no result found. May be the query should be more perfect.
**please avoid any syntax error in my writing.
That's because you are comparing NULL to an empty string, and they aren't equal. You could try this instead:
SELECT Result
FROM symptom
WHERE IFNULL(symptom.SID1, '') = '$symptom1'
AND IFNULL(symptom.SID2, '') = '$symptom2'
AND IFNULL(symptom.SID3, '') = '$symptom3'
Notes:
You need to correctly escape the values of $symptom1, $symptom2 and $symptom3.
This won't efficiently use indexes.
As mark pointed out, the query is eventually falling down to compare with null if you are not escaping the null.
Or you can slightly change your logic to show a empty symptom with value '0' and then using the coalesce function you can easily build your query.
Does this work?
$query = "select Result from result
where (result.SID1 = '$symptom1' OR result.SID1 IS NULL) AND
(result.SID2 = '$symptom2' OR result.SID2 IS NULL) AND
(result.SID3 = '$symptom3' OR result.SID3 IS NULL)";
I am creating a function to get all keywords from a database
The database has two tables
keywords [id | word | account] ( aliased as k )
keywordsTemplateLink [templateId | keywordId] ( aliased as ktl )
the functions signature is
getKeywords($id = null){}
so the way it works is,
if id != null a where clause is added which must limit the result set to keywords where ktl.templateId = $id
What would be the most effective way to achieve this query?
Im thinking SELECT id, keyword FROM keywords k, templatekeywordlink tkl WHERE tkl.templateId= $id AND tkl.keywordId = k.id AND k.account=$account
Is there a better way?
function getKeywords($id = null){
//query generated by function
$query .= ($id != null) ? ' where ktl.templateId = ' . $id : '';
}
It is generally bad practice to create sql by variable concatenation in this way..
If you don't want some script kiddie to pwn you via sql injection, use prepared queries.
$stm = $dbo->prepare("SELECT id, keyword FROM keywords k, templatekeywordlink tkl WHERE tkl.templateId= ? AND k.account=?);
$stm->execute(array($id,$account));