I've noticed that, in Laravel, (when using $model->fill(Input::all()), not that it matters how the data comes in), empty fields (empty in a form) come through as an empty string (''). That makes sense, as that's how it's delivered from browser to HTTP server to PHP.
The problem is that if the column is numeric and has a DEFAULT value, or is NULLable, the generated query from Eloquent has '' for the column's value and so MySQL interprets that and enters the value as 0 rather than the default column value or NULL. Is it something I'm doing wrong here, or will I need to put extra work in (e.g. a mutator) to detect this empty string and convert to null to achieve what I actually want?
Of course I understand that from a technical point of view, Laravel, without knowing how your columns work, can't just assume that empty string means pass NULL to the INSERT query, because sometimes you actually want to set a field (specifically a character-based one) to an empty string rather than NULL.
That said, I'd rather not have to define mutators for all my models just because I'm using $model->fill(), but is there anything I don't know about that I can do?
For the MySQL people reading this - is it correct behaviour to set a numeric field to 0 if passed ''? Seems like it should be seen as NULL as it's not explicitly 0, but I guess it's maybe weak typing equating '' to 0 rather than the more distant NULL.
There is a very simple way to do this, and that is by using an array_filter.
$input = array_filter(Input::all(), 'strlen');
$model->fill($input);
The array_filter will return all of the keys that have something assigned to them.
There are some caveats with this solution:
strlen has been used, and not empty. This is because empty will cause other items (such as the number 0) to also be unset.
this means that edits that are made with an update, such as a text box being completely emptied, will not be fulfilled by your application, so use wisely!
EDIT: As for the MySQL question, yes, this is normal.
mysql> SELECT CAST("tim?" AS SIGNED);
+------------------------+
| CAST("tim?" AS SIGNED) |
+------------------------+
| 0 |
+------------------------+
1 row in set, 1 warning (0.00 sec)
mysql> SHOW WARNINGS;
+---------+------+-------------------------------------------+
| Level | Code | Message |
+---------+------+-------------------------------------------+
| Warning | 1292 | Truncated incorrect INTEGER value: 'tim?' |
+---------+------+-------------------------------------------+
For the default values to be inserted on some fields then do not set values for those fields explicitly.
When you receive form data, process each of the fields and decide for which of them you want to insert the default values. Then remove them from insert into ... statement.
Example:
Form fields: field1_1, field_2.
If valid input is given for both of them, then you can write sql statement like:
insert into my_table values( field_1_value, field_2_value );
If one of the fields, say field_2 does not have valid input and you still want the record go into the database table. Then write the sql statement like:
insert into my_table( field_1 ) values( field_1_value );
When you do this, SQL engine will use the default valued defined on the omitted fields in the insert statement.
Note: This only works when you have set default someValue on the columns at the time of creating or altering them in a database table.
Related
If i modify the max number of allowed digits in MYSQL 5.7 from double(8,2) to double(12,2), is the change immediate or will it need to process all rows??
You can test this to see if it can be changed as an instant change:
mysql> create table mytable (id serial primary key, d double(8,2));
mysql> alter table mytable modify column d double(25,2), algorithm=inplace, lock=none;
Query OK, 0 rows affected, 1 warning (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 1
The options algorithm=inplace, lock=none mean you want the alter to run as an instant change, without performing a table copy. By default, MySQL runs the DDL change in that mode if the change can be done in that mode.
If you request it explicitly, but the change cannot be done in that mode, then you'll get an error.
For example:
mysql> alter table mytable modify column d float(8,2), algorithm=inplace, lock=none;
ERROR 1846 (0A000): ALGORITHM=INPLACE is not supported.
Reason: Cannot change column type INPLACE. Try ALGORITHM=COPY.
In this example I'm changing the 8-byte DOUBLE to a 4-byte FLOAT. Any change to the size of a data type cannot be done without copying the table. So the request to do it as an instant change fails and the error shown is returned.
So if you're in doubt about whether a given change can be done instantly, you can use this method to test it. You don't have to do the test against your production table! I did this test on my local instance, without even adding any data to the table. I just created an empty table as I showed above, and ran the DDL.
You should read https://dev.mysql.com/doc/refman/8.0/en/innodb-online-ddl-operations.html for details on which types of DDL changes can be done inplace.
As written in the MySQL Reference Manual all ALTER Table statements needs processing time. On top of this MySQL will convert the data:
For data type changes using CHANGE or MODIFY, MySQL tries to convert existing column values to the new type as well as possible.
Therefore all Columns will be visited.
Beside the fact that specifying number of digits for floating point data types is deprecated, a change from double(8,2) to double(12,2) doesn't change the column type, it is still a double precision 8-byte number, so not even a single row will change it's value.
Example:
CREATE TABLE t1 (a double(6,2));
INSERT INTO t1 VALUES (1234.56);
# change the precision
ALTER TABLE t1 change a a double(4,2);
INSERT INTO t1 VALUES (1234.56);
ERROR 1264 (22003): Out of range value for column 'a' at row 1
SELECT a FROM t1;
+---------+
| a |
+---------+
| 1234.56 |
+---------+
Even if 1234.56 doesn't fit in double(4,2) it is still unchanged.
I am working in a PHP + MySQL application. The application is working fine for me. But when I hosted it in another server, I got a MySQL error:
Error Code: 1364. Field 'field' doesn't have a default value
I know this is a problem with the MySQL version and we should setup default values for all columns. But currently I have more than 100 tables. So I need to set default value to NULL for all columns in all tables that has no default value yet.
I can't make use of the strict mode option, because the server is a shared one. Is it possible to setup in a single step rather than setting for each and every table ? If not possible tell me the easiest way to setup it.
Any help would be greatly appreciated
For anyone else with this problem, it will take a bit of coding to perform automatically, but the following would be how you would do so:
First run the following query:
SELECT table_schema,table_name,column_name,data_type FROM information_schema.columns WHERE IS_NULLABLE='NO' AND column_default is null AND column_key=''
Next, for each row returned from the above query perform the following:
If data_type contains 'int' set default to 0
else if data_type='datetime' set default to '0000-00-00 00:00:00'
else if data_type='date' set default to '0000-00-00'
else if data_type='time' set default to '00:00:00'
else set default to ''
create and run the following query with all [[...]] variables replaced with their proper values:
ALTER TABLE `[[table_schema]]`.`[[table_name]]` ALTER COLUMN `[[column_name]]` SET DEFAULT '[[default]]'
This should replace the default values for all databases, all tables, all columns that are set to be NOT NULL and are not primary keys and have no default value set.
Another solution that i found is like:-
Get all column name put it in array...
Now push values in column array for inserting -- with ZERO value for all those arrays we do not have values.
FOR EXAMPLE:
in a table we have COLUMN
NAME LASTNAME COMPNAME PHONO EMAIL ADDRESS ALTERPERSON ALTERPHONE ALTEREMAIL
Now after migration we see the eeror
Error Code: 1364. Field 'field' doesn't have a default value
if we run a INSERT QUERY LIKE
mysqli_query($con,'insert into table
(NAME,LASTNAME,COMPNAME,PHONO,EMAIL,ADDRESS) values
(NAME,LASTNAME,COMPNAME,PHONO,EMAIL,ADDRESS)')
now it will give error...
So just turn the table
get all the column value from DB.TABLE
put it in an array or do it like one by one using while loop or for loop....
check insert values for each column
put condition if insert value is equal to ZERO or NULL then insert ZERO it will solve all issues.
WHY ZERO --
because it will work for VARCHAR,TEXT,INT,BIGINT and in many Data Types except time or date function and DATE/TIME data type got ZERO values by default...
=============================== Another option...
run a PHP code
get all TABLE NAME
then for each TABLE NAME
get all COLUMN NAME
and run this command as in function under loop
ALTER TABLE DB.TABLEnAME CHANGE columnNAME_A columnNAME_A
VARCHAR(100) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL
DEFAULT NULL;
=======================
And its DONE
SELECT * FROM plugin_referral_code WHERE code=0;
Returns this record
What!? Why?
MySQL does not return an incorrect result for that query. Please, note that most important web sites use MySQL or its forks, so it is illogical to assume that a query like that doesn't work correctly.
You are comparing potatoes with tomatoes. 'butts' is a string, 0 is a number. MySQL (or any program ever existed) cannot compare strings to numbers. So, it internally converts 'butts' to a number. Any string, if converted to a number, is 0.
Also note that your client is ignoring a MySQL warning that explains the problem:
MariaDB [test]> SHOW WARNINGS;
+---------+------+-------------------------------------------+
| Level | Code | Message |
+---------+------+-------------------------------------------+
| Warning | 1292 | Truncated incorrect DOUBLE value: 'butts' |
+---------+------+-------------------------------------------+
1 row in set (0.00 sec)
I suggest to immediately uninstall any client that hides MySQL warnings... but this is a personal opinion :)
Are you definitely executing the entire query in your query editor? I know some editors let you execute (highlighted) parts of queries. It's possible that if you're actually only executing SELECT * FROM plugin_referral_code WHERE code it would return any rows with non-NULL code values.
This is because the where clause needs to evaluate to true, and WHERE code would evaluate to true for any non-NULL code value.
That's all I can guess on this. If this is not the case, please provide more detail, e.g. what type column code is, and execute your query with DESC before it to get any query plan details.
I have two tables, pattern and pattern_optimized. Some of the fields in the pattern table are defined as BIGINT(20) DEFAULT '-1', which is way too big for the data they hold; Additionally, -1 is used as "not set" (zero is a valid value) - no other negative values are used otherwise.
The pattern_optimized table uses a new data format for these rows, defining them as INT(10) UNSIGNED DEFAULT NULL. I would now like to copy the data from pattern to the pattern_optimized table.
This would be easily done using INSERT INTO pattern_optimized SELECT * FROM pattern, but obviously all negative values are out of range now, resulting in warnings like this:
100 row(s) affected, 64 warning(s):
1264 Out of range value for column 'version' at row 1
1264 Out of range value for column 'triggered' at row 1
...
Records: 100 Duplicates: 0 Warnings: 357
My first idea was to create a BEFORE INSERT trigger as follows:
CREATE TRIGGER `negativeValueFix` BEFORE INSERT ON `pattern_optimized`
FOR EACH ROW
BEGIN
IF new.version < 0 THEN
SET new.version = NULL;
END IF;
-- ...
END
but unfortunately this doesn't help either: The same warning pops up and all values that used to be -1 in the original table become 0 in the new table (instead of NULL, which is - apart from being implemented in the trigger - also the default for the row).
It seems that MySQL converts the value even before the trigger.
I know that I can solve this problem using a temporary table but I'd rather not do that. The pattern table is unpleasantly large and I don't want to do a stored procedure just for that.
Is there any other way or am I missing some simple point?
EDIT: There are quite alot columns in the original table that suffer from the SIGNED problem, so I was hoping to somewhat automate that.
Could you use a case statement? Something like:
INSERT INTO pattern_optimized
SELECT
CASE version
WHEN -1 THEN null
ELSE version
END CASE AS version
FROM pattern
Just add small condition to the SELECT statement, e.g. -
INSERT INTO pattern_optimized(version)
SELECT IF(version < 0, NULL, version) version FROM pattern
...add other fields.
To be honest, I'm feeling pretty stupid right now. But this simply isn't working.
Scenario
I have a stored procedure that includes an output parameter. I'm trying to SELECT a value INTO that parameter. This seems simple, but it continues giving me faulty results. I've checked many online sources, and I'm certain that I'm trying to do it properly.
Code
DELIMITER //
CREATE PROCEDURE `spGetId`(
IN ParamA VARCHAR(32),
OUT OutputId INT
)
BEGIN
SELECT `id` INTO OutputId
FROM `Table`
WHERE `column_a` = ParamA;
END//
CALL spGetId('foobar', #Bloop)//
SELECT #Bloop//
Results
I have two rows in this table, their IDs being '1' and '2'. The result I get back is '31', whether the SELECT statement matches anything or not.
I have tried many variations, including removing the WHERE clause entirely and having the SELECT return a COUNT(1) into the parameter (which gives me a result of '32', despite there being only 2 rows), and I have tried "declaring" the #Bloop variable before using it in the sproc call by using SET #Bloop = 0.
If you have any insight on why this is happening, and what I can do to make it return the proper value, I would be much obliged. Also, if you can show me how to achieve the same desired result using a Stored Function instead, with a return value, I'd appreciate that even more! My desired approach is using a stored function, but I had similar problems with that, then gave up and tried using a stored proc, only to find I was getting similar results.
Anything you can offer would be helpful!
Edit:
CREATE TABLE `Table` (
`id` int(11) NOT NULL auto_increment,
`column_a` varchar(32) character set utf8 NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
mysql> SELECT * FROM Table;
+------+----------+
| id | column_a |
+------+----------+
| 1 | asdf |
| 2 | foobar |
+------+----------+
When I call spGetId() with any argument, it returns the value '31' (even if the argument is 'foobar', which should return an integer value of '2' (or ascii 0x32)). If I modify spGetId() to return the total rowcount of Table, instead of returning '2', it returns '32'.
Your stored proc is working. I think it is returning the ascii value of the character '1' instead of the integer value 1.
I need to learn to vary my testing environments.
I'm still not sure exactly what the problem was, but it looks like phpMyAdmin was performing some kind of type conversion of its own, and I had been running all my tests through that particular client.
Throwing together a quick PHP script of my own and manually calling the sproc (and in further testing, calling a stored function as well) provided the desired results.
So, lesson learned: don't ever trust the client. Got to remember to switch it up a bit.