MySQL updates all rows when the column name is only specified in the where clause - php

It's been a while I didn't work on mysql and I was suprised to see that the following statement is valid:
UPDATE table_A
SET col_a = value
WHERE id;
It looks like MySQL updates all rows in the table. I have a good MSSQL background and I am trying to understand why this valid in MySql?
Also, I ran a query with a similar where clause on a varchar column:
SELECT distinct description
FROM table_A
where NOT description;
I tought it will return only null or empty values. Instead, it returns lots of rows with non-null values.
Any ideas why?
Thanks,

WHERE x will match any rows for which x evaluates to a truthy value. Since BOOLEAN is actually a number type TINYINT(1), this works by converting to a number and then comparing to zero - any nonzero number is truthy. (NULL is falsy.)
If you write WHERE id = 123, then only for the row where id is 123, the expression id = 123 will evaluate to TRUE (which is the same as 1) and it will match, otherwise it will evaluate to FALSE (0).
But if you write WHERE id, the requirement is that id evaluates to a truthy value. If id is a number, only IDs 0 and NULL will be falsy.
However, in case of description, you have a string. And the string is first converted to a number. The reason you got many results there is that any string that starts with a number (that is nonzero) is matching, such as 01234hello (which converts to 1234, which is nonzero). Check what CONVERT(description, SIGNED) gives - if it is nonzero, then it matches.
This is why, when building AND or OR queries in code, you can avoid handling the case of zero conditions specially by starting with TRUE or 1 (in the AND case) or FALSE or 0 (in the OR case), since WHERE TRUE/WHERE 1 is valid (matches everything), as is WHERE FALSE/WHERE 0 (matches nothing). So you build a query by starting with WHERE 1 and adding to it: WHERE 1, WHERE 1 AND id = 123, WHERE 1 AND id = 123 AND type = 'xy', etc.

Related

MySql/mariadb - SELECT 0 = 'N;' returns true [duplicate]

This question already has answers here:
mysql: why comparing a 'string' to 0 gives true?
(3 answers)
Closed 3 days ago.
I was going through some MySql (mariadb) report looking for why I was getting too many rows in a report and found that the column being queried which stores serialized php and matches another column value is falsely matching.
where m.section = SUBSTRING_INDEX(SUBSTRING_INDEX(s.other,CHAR(59),2),CHAR(58),-1)
This is fine. m.section contains a number. s.other might have a value a:1:{s:3:"foo";i:4;}, so it is matching the 4 in that data.
Then I found that some n.other records are storing null. Php's serialize outputs a capital N followed by a semicolon to represent a null.
echo serialize(null); // outputs N;
echo is_null(unserialize('N;')); // outputs 1
So php thinks N; is null.
In my query, when m.section equals zero, it is matching the record. Zero equals php null? Lets test that.
SELECT 0 = 'N;'
outputs
1 !
What else equals null that shouldn't?
select 1 = 'N;' --> 0
select 0 = null --> null
select 0 is null --> 0
select 'N;' is null --> 0
Am I going mad? How come zero equals php's serial null?
Its not PHP, its MariaDB.
Through the type conversion rules, 'N;' is converted (badly) to DECIMAL.
During the conversion it gets a 0 value. And with the equality test to 0 becomes true.
Looking at the warnings of the select 0 = 'N;' will result in:
Warning 1292 Truncated incorrect DECIMAL value: 'N;'
ref: https://dbfiddle.uk/4rZ0N8CF

Return BLOB column Which is Empty Or Not Empty as 1 or 0

I have a table with BLOB column that some row has BLOB, some empty.
1 Apple BLOB-8KiB
2 Banana
3 Pear BLOB-6KiB
4 Orange BLOB-7KiB
Is there any way I can use PHP MYSQL to get the array like this:
$fruit = array(
array("1",Apple,1),
array("2",Banana,0),
array("3",Pear,1),
array("4",Orange,1)
);
I just want to change the BLOB data with 1, Empty with 0 in my PHP array. Pls help.
Your select statement can use IF and ISNULL (note these are not widely implemented in the same format on different database backends, this is for MySQL).
So you would use:
SELECT ID, Name, IF(ISNULL(BlobField), 0, 1) FROM TableName
IF allows you to choose one of two values according to a logical operation.
ISNULL returns true or false according to whether or not the value is NULL

SQL SELECT is selecting rows that do not match the query

I am using php mysql_query() to select rows from my SQL Database but for some reason it is selecting rows that do not match the query. For example:
mysql_query("SELECT * FROM Table WHERE ID='153'")
This will return the row who has an ID of 153
but so will:
mysql_query("SELECT * FROM Table WHERE ID='153c'")
Am I doing something wrong?
As noted, the issue is comparing a string value to an integer value. Your expression is:
WHERE ID = '153' and
WHERE ID = '153a'
You can imagine the MySQL engine describing what it does as: "id is an integer column. So, I need to compare it to an integer value. Oh, the right side is a string, so I will convert the right hand side to an integer."
The way that MySQL converts values to a number from a string could be called "silent conversion". It converts the longest leading number that it finds, and then stops. If there is no leading number (say 'a123'), then the value is 0. There is no error produced.
If you really want to confuse yourself, consider the following:
select (case when 123 = '123e' then 1 else 0 end),
(case when 123 = '123e3' then 1 else 0 end),
(case when 123 = '123a' then 1 else 0 end),
(case when 123 = '123a3' then 1 else 0 end)
This returns: true, false, true, and true. Why is the second one false, but the others true? Well, '123e3' is interpreted as scientific notation, so the value becomes 123,000. For all the others, the conversion stops at the first alphabetic character.
As mentioned in the other answers, the obvious fix is to drop the single quotes on the constant.
Why are you passing the ID value as a String? This would most likely be your issue. Try passing the following query instead:
mysql_query("SELECT * FROM Table WHERE ID= 153")
This way, the ID value being searched for is an int (or any other primitive the ID column is set to). This should restrict the IDs properly. In addition, if you are creating IDs with numbers, it's best practice to use distinct ints only and not strings.

Mysql Match Against & Like search

SELECT * FROM `db`
WHERE MATCH (city) AGAINST ('south ban' IN BOOLEAN MODE)
SELECT * FROM 'db'
WHERE city LIKE '%south ban%'
I have 2 queries, one is use Match against, the other is LIKE,
When I try to search 'south bank'
Match wont return if user type south ban but Like will return the result
How can I improve Match against search?
I did testing,
(country LIKE '%south%' || state LIKE '%south%' || city LIKE '%south%') &&
(country LIKE '%bank%' || state LIKE '%bank%' || city LIKE '%bank%')
MATCH (country, stateprov, city) AGAINST ('south bank*' IN BOOLEAN MODE)
for 3 millions rows
Match - 69,310 rows - 2.5 sec
LIKE - 67,092 rows - 1.87 sec
How come Like is faster than match?
If you use a IN BOOLEAN MODE query without +, match is actually rating each row, which takes time.
Check out this info from http://dev.mysql.com/doc/refman/5.5/en/fulltext-boolean.html:
(no operator)
By default (when neither + nor - is specified) the word is optional, but the rows that contain it are rated higher. This mimics the behavior of MATCH() ... AGAINST() without the IN BOOLEAN MODE modifier.

column <> 1 returns False on NULL values

I have two tables. offers and offer_status. I want to return rows from offers where the offer_status row is either missing or the disabled column in that table is 0.
Here is how I tried to do it:
SELECT offers.id,network,campid,name,url FROM offers
LEFT JOIN offer_status ON offers.id = offer_status.sql_id
AND offer_status.user_id='3'
WHERE country='US'
AND offer_status.disabled != '1'
ORDER BY epc DESC LIMIT 7
However, this does not work if the row in offer_status does not exist (and the JOIN fails). To me, it seems like it should still work though since offer_status.disabled is null.
This DOES work:
SELECT offers.id,network,campid,name,url FROM offers
LEFT JOIN offer_status ON offers.id = offer_status.sql_id
AND offer_status.user_id='3'
WHERE country='US'
AND (offer_status.disabled IS NULL OR offer_status.disabled=0)
ORDER BY epc DESC LIMIT 7
But to me this seems worse preformance-wise, and I am still disappointed my original thought didn't work.
Is the OR statement above the only way to do this? Is there a better method?
SQL logic is a tristate logic with states TRUE, FALSE and UNKNOWN. The WHERE clause selects rows where the condition evaluates to TRUE.
If you compare a NULL value with any non-null value, you get the third logic state, UNKNOWN (not FALSE as claimed in your title). This is not the same as TRUE, so the rows where a condition evaluates to UNKNOWN are not selected.
You can use the explicit IS NULL test to check for nullness.
AND (offer_status.disabled != '1' OR offer_status.disabled IS NULL)
Note the extra parentheses for safety, clarity and accuracy.
If this is too much of a performance hit, enforce a NOT NULL constraint on the offer_status.disabled column, and then you do not have to fiddle around with the IS NULL test or OR'd conditions. Generally, every column that possibly can be NOT NULL should be NOT NULL; it makes your database much easier to work with.
Note that if offer_status.disabled is NULL, then:
WHERE offer_status.disabled = '1' -- evaluates to UNKNOWN and the row is not selected
WHERE offer_status.disabled != '1' -- evaluates to UNKNOWN and the row is not selected
This is the difference between the incorrect 'comparison with NULL returns FALSE' as claimed in the title and the actual behaviour of 'comparison with NULL returns UNKNOWN'.
Comparing NULL (using =, <>, <, > etc) with another value yields NULL. And NULL is neither true, nor false. Secondly, in order to satisfy the WHERE clause, the condition must evaluate to true.
So, your second query is in fact the correct way to go.
For the sake of completeness, it is possible (but not recommended) to rewrite your query using functions such as IF and IFNULL, for example:
WHERE IFNULL(offer_status.disabled, 0) = 0
Quoting from MySQL document:
If one or both arguments are NULL, the result of the comparison is
NULL, except for the NULL-safe <=> equality comparison operator. For
NULL <=> NULL, the result is true.
So I think yes, (disabled IS NULL OR disabled=0) is necessary.

Categories