I have a LINQ to SQL class "VoucherRecord" based on a simple table. One property "Note" is a string that represents an nvarchar(255) column, which is non-nullable and has a default value of empty string ('').
If I instantiate a VoucherRecord the initial value of the Note property is null. If I add it using a DataContext's InsertOnSubmit method, I get a SQL error message:
Cannot insert the value NULL into column 'Note', table 'foo.bar.tblVoucher'; column does not allow nulls. INSERT fails.
Why isn't the database default kicking in? What sort of query could bypass the default anyway? How do I view the generated sql for this action?
Thanks for your help!
If you omit the column, the value becomes the database default, but anything you insert is used instead of the default, example:
INSERT INTO MyTable (ID, VoucherRecord) Values(34, NULL) -- Null is used
INSERT INTO MyTable (ID) Values(34) -- Default is used
Picture for example you have a column that defaults to anything but NULL, but you specifically want NULL...for that to ever work, whatever value you specify MUST override the default, even in the case of NULL.
You need to set Auto-Sync to OnInsert, Auto Generated Value to true and Nullable to false for your column to work. See here for a full run-down with explanation on the Linq side.
For viewing the generated SQL, I have to recommend LinqPad
Related
I'm building a Rails app with Devise for authentication, and its default DB migration sets the following columns:
## Database authenticatable
t.string :email, null: false, default: ""
t.string :encrypted_password, null: false, default: ""
What is the purpose of setting null: false and default: "" at the same time?
My understanding is that null: false effectively makes a value required: i.e., trying to save a record with a NULL value in that column will fail on the database level, without depending any validations on the model.
But then default: "" basically undoes that by simply converting NULL values to empty strings before saving.
I understand that for an optional column, you want to reject NULL values just to make sure that all data within that column is of the same type. However, in this case, email and password are decidedly not optional attributes on a user authentication model. I'm sure there are validations in the model to make sure you can't create a user with an empty email address, but why would you set default: "" here in the first place? Does it serve some benefit or prevent some edge case that I haven't considered?
Broadly speaking:
To make a column required, you must set null: false on it. This is true whether you are creating a new table or updating an existing one.
And in the event that you're updating an existing table, the DB engine will try to populate that new column with NULL in each row. In such cases, you must override this behavior with default: "", or else it will conflict with null: false and the migraiton will fail.
With respect to Devise:
Devise uses two separate templates for building migrations: migration.rb, for creating new tables, and migration_existing.rb, for updating existing tables (see source on GitHub). Both templates call the same migration_data method to generate the lines in question (i.e., the ones that specify null: false, default: ""), but as mentioned above, default: "" is only really relevant in the latter case (see O. Jones’ answer for more).
So the short answer to your question, specifically in the case of Devise migrations, is “because the generator uses borrowed code which doesn’t always apply, but still doesn’t break anything.”
A consideration for UNIQUE columns:
Note that in most popular SQL engines, uniquely indexed columns can still contain multiple rows of NULL values, as long as they are not required (naturally). But the effect of making a new column both required and unique (i.e., null: false, default: "", and unique: true) is that it cannot be added: the DB engine tries to populate the new column with an empty string in each row, which conflicts with the unique constraint and causes the migration to fail.
(The only scenario in which this mechanism fails is if you have exactly one row in your table — it gets a blank string value for the new column, which naturally passes the uniqueness constraint because it's the only record.)
So another way to look at it might be that these options are a safety mechanism preventing you from running migrations that you shouldn't (i.e., retroactively adding required columns to an already-populated table).
There is a difference in the insertion type. For example, let say you have a new_table table such that:
CREATE TABLE IF NOT EXISTS `new_table` (
`col1` VARCHAR(10) NOT NULL,
`col2` VARCHAR(10) NOT NULL DEFAULT '',
`col3` VARCHAR(10) NULL DEFAULT '');
When you use explicit insert of NULL you'll get the NULL:
INSERT INTO new_table(col1,col2,col3) VALUES('a','b',NULL);
'a','b',NULL
for col2 same trick will result in error:
INSERT INTO new_table(col1,col2,col3) VALUES('a',NULL,'c');
But when you use implicit insert of NULL you'll get the default value:
INSERT INTO new_table(col1,col2) VALUES('a','b');
'a','b',''
meaning that setting a default value is not preventing NULL assertion to this column, but only used when the value is not explicitly given.
Some application software gacks on NULL values but not on zero-length text strings. In Oracle, they're the same thing, but not in MySQL.
Things get interesting upon altering tables to add columns. In that case a default value is mandatory, so the DBMS can populate the new column.
I'm thinking this is here because of MySQL 'strict mode' not allowing you to disallow a null value without providing a default.
From mysql docs: https://dev.mysql.com/doc/refman/8.0/en/data-type-defaults.html
For data entry into a NOT NULL column that has no explicit DEFAULT clause, if an INSERT or REPLACE statement includes no value for the column, or an UPDATE statement sets the column to NULL, MySQL handles the column according to the SQL mode in effect at the time: If strict SQL mode is enabled, an error occurs for transactional tables and the statement is rolled back. For nontransactional tables, an error occurs, but if this happens for the second or subsequent row of a multiple-row statement, the preceding rows are inserted. If strict mode is not enabled, MySQL sets the column to the implicit default value for the column data type.
I have a nullable JSON MySQL 5.7 field which I am finding almost impossible to get working.
Example query:
UPDATE `json_test` SET `data` = JSON_SET(`data`, '$.a', 1)
If the field data is NULL already, it won't update.
If it's { "a" : 2 }, then it'll update correctly to 1. I need it to set if not set already, which is what JSON_SET is supposed to do.
Any ideas what's happening?
1) An alternative is to check for null and return an valid empty JSON set ({}) to JSON_SET in those situations, so it just puts in the new data.
UPDATE json_test SET data = JSON_SET(IFNULL(data,'{}' ), '$.a', 1)
2) Finally, another option would be for the data specification to have a default value of {}, ex.
`dataJson TEXT DEFAULT '{}',`
I prefer the first option I presented as I like leaving fields NULL until I need them to have data but then I expect them to start packing in the JSON data immediately!
Updating the entire table for that is an overkill and changing the table definition as well.
This should have no noticeable performance impact:
UPDATE `json_test` SET `data` = JSON_SET(COALESCE(`data`,'{}'), '$.a', 1)
Explanation:
JSON_SET needs a full processing of the column in any case, so it will be evaluated for validity, parsed, etc.
The COALESCE changes a NULL field to an empty JSON object, so the json will be valid and the SET will be successful.
You probably won't be able to measure a performance difference.
it's not supposed to work with nulls
Otherwise, a path/value pair for a nonexisting path in the document is
ignored and has no effect.
Now mysql doesn't let you use a subquery on the same table that's being updated, but you could probably stil solve this with an UPDATE JOIN using CASE/WHEN but I am too lazy so I leave you with a two query solution.
UPDATE `json_test` SET `data` = JSON_SET(`data`, '$.a', 1) WHERE data IS NOT NULL;
UPDATE `json_test` SET `data` = JSON_OBJECT('$.a', 1) WHERE data IS NULL;
I'm having some odd SQL problems when inserting new rows into a table. I have set some columns to NULL, as I have with another table in my database. Obviously when no data is passed through on insertion it should enter NULL into the record, however currently it is not.
I have checked all settings in comparison with my other table (which is inserting records as NULL correctly) but can't find the issue. The columns appear as below, in both tables.
`statement_1` varchar(255) DEFAULT NULL,
No data is being pasted through (so not a blank space issue). Can anyone suggest why one table is doing as expected but the other is not?
Using below as the insert statement
$statement_a = "INSERT INTO statements (ucsid, statement_1, statement_2, statement_3, statement_4, statement_5, statement_6, statement_7, statement_8, statement_9, statement_10) VALUES (:ucsid, :statement_1, :statement_2, :statement_3, :statement_4, :statement_5, :statement_6, :statement_7, :statement_8, :statement_9, :statement_10)";
$q_a = $this->db_connection->prepare($statement_a);
$q_a->execute(array(':ucsid'=>$ucsid,
':statement_1'=>$statement_1,
':statement_2'=>$statement_2,
':statement_3'=>$statement_3,
':statement_4'=>$statement_4,
':statement_5'=>$statement_5,
':statement_6'=>$statement_6,
':statement_7'=>$statement_7,
':statement_8'=>$statement_8,
':statement_9'=>$statement_9,
':statement_10'=>$statement_10));
I can not add comments as I am new:
Try a simple INSERT statement using NOT phpmyadmin. Try
http://www.heidisql.com/ OR https://www.mysql.com/products/workbench/
INSERT INTO statements (ucsid) VALUES (123)
INSERT INTO statements (ucsid, statement_1) VALUES (123, NULL)
In both cases the statement_1 should be NULL. Which in your case most likely is not. However that would tell the problem lies in the database table and NOT with php or the php execute method you are using.
Also is the statement_1 field defined as NOT NULL and the default set as NULL? which can not happen.
Try recreating a new database and a new table with no records and than try inserting NULL as values as a test.
Also can you post the SQL of your database and table with Character Set and Collation
I've fixed the issue by ensuring that NULL is passed through the functions if nothing has been inserted by using the following code
if($_POST['statement_1'] == '') { $statement_1 = NULL; } else { $statement_1 = $_POST['statement_1']; }
Here the value passed by the varriable $statement_1 will be ""
Try this query SELECT * FROM my_table WHERE statement_1 ="".You will get rows.
Which means you are assigning some values to $statement_1 else it should be null.
Check your code. Hope this helps
I'm building a site in Laravel.
I have foreign key constraints set up among InnoDB tables.
My problem is that if i don't select a value in a, say, select box, the framework tries to insert or update a record in a table with '' (empty string). Which causes a MySQL error as it cannot find the equivalent foreign key value in the subtables.
Is there some elegant way to force the insertion of NULL in the foreign key fields other than checking out every single field? Or to force MySQL to accept '' as a "null" foreign key reference?
In other words: I have a, say, SELECT field with first OPTION blank. I leave the blank OPTION chosen. When I submit, an empty string '' is passed. In MySQL apparently I can do UPDATE table SET foreignKey=NULL but not UPDATE table SET foreignKey=''. It does not "convert" to NULL. I could check the fields one by one but and convert '' to NULL for every foreign key, maybe specifying all of them in an array, I was wondering if there's a more streamlined way to do this.
Maybe have to change my ON UPDATE action (which is not set) in my DB schema?
Edit: the columns DO accept the NULL value, the problem is in how the framework or MySQL handle the "empty value" coming from the HTML. I'm not suggesting MySQL "does it wrong", it is also logical, the problem is that you can't set a "NULL" value in HTML, and I would like to know if there's an elegant way to manage this problem in MySQL or Laravel.
In other words, do I have to specify manually the foreign keys and construct my query accordingly or is there another robust and elegant way?
My code so far for the model MyModel:
$obj = new MyModel;
$obj->fill(Input::all())); // can be all() or a subset of the request fields
$obj->save();
At least since v4 of Laravel (and Eloquent models), you can use mutators (aka setters) to check if a value is empty and transform it to null, and that logic is nicely put in the model :
class Anything extends \Eloquent {
// ...
public function setFooBarAttribute($value) {
$this->attributes['foo_bar'] = empty($value)?null:$value;
}
}
You can check out the doc on mutators.
I've been oriented by this github issue (not exactly related but still).
Instead of using
$obj = new MyModel;
$obj->fill(Input::all())); // can be all() or a subset of the request fields
$obj->save();
Use
$obj = new MyModel;
$obj->fieldName1 = Input::get('formField1');
$obj->fieldName2 = Input::has('formField2') && Input::get('formField2') == 'someValue' ? Input::get('formField2') : null;
// ...
$obj->save();
And make sure your database field accepts null values. Also, you can set a default value as null from the database/phpmyadmin.
You must remove the "not null" attribute from the field that maps your foreign key.
In the model add below function.
public function setFooBarAttribute($value)
{
$this->attributes['foo_bar'] = $value?:null;
}
I have a DBML with a class that contains a timestamp column in sql.
The property in the DBML is declared as: (only relevant fields specified here)
Auto generated value:true
Name: timestamp
Nullable: false
Server data type: rowversion NOT NULL
Source: Timestamp
Time Stamp:true
Type: binary
Update check:never
In my logic, I insert into this table. However, I'm surprised to see that the generated sql looks something like this:
exec sp_executesql N'INSERT INTO [dbo].Foo( /* elided */)
VALUES (#p0, #p1, #p2, #p3, #p4, #p5, #p6, #p7, #p8, #p9, #p10, #p11, #p12, #p13, #p14, #p15, #p16, #p17, #p18, #p19, #p20, #p21, #p22, #p23, #p24, #p25)
SELECT [t0].[Timestamp]
FROM [dbo].[Foo] AS [t0]
WHERE [t0].[Id] = #p26', /* elided */
I'd like to get rid of that SELECT statement - I don't use the result.
Is that possible, or is it mandated that for object tracking the datacontext must know what the timestamp was of that new record?
I recognise that I could switch to using an SP for the insert method, but would prefer to avoid that.
The additional select is required to update the timestamp that the local instance of the object uses when updating itself in the concurrency check for any subsequent update/delete clauses. Since the RowVersion (TimeStamp) value is updated everytime the row is changed/inserted, the client needs the new version, otherwise a subsequent update would fail because the timestamp value would no longer be the same.