MySQL, better to insert NULL or empty string? - mysql

I have a form on a website which has a lot of different fields. Some of the fields are optional while some are mandatory. In my DB I have a table which holds all these values, is it better practice to insert a NULL value or an empty string into the DB columns where the user didn't put any data?

By using NULL you can distinguish between "put no data" and "put empty data".
Some more differences:
A LENGTH of NULL is NULL, a LENGTH of an empty string is 0.
NULLs are sorted before the empty strings.
COUNT(message) will count empty strings but not NULLs
You can search for an empty string using a bound variable but not for a NULL. This query:
SELECT *
FROM mytable
WHERE mytext = ?
will never match a NULL in mytext, whatever value you pass from the client. To match NULLs, you'll have to use other query:
SELECT *
FROM mytable
WHERE mytext IS NULL

One thing to consider, if you ever plan on switching databases, is that Oracle does not support empty strings. They are converted to NULL automatically and you can't query for them using clauses like WHERE somefield = '' .

One thing to keep in mind is that NULL might make your codepaths much more difficult. In Python for example most database adapters / ORMs map NULL to None.
So things like:
print "Hello, %(title)s %(firstname) %(lastname)!" % databaserow
might result in "Hello, None Joe Doe!" To avoid it you need something like this code:
if databaserow.title:
print "Hello, %(title)s %(firstname) %(lastname)!" % databaserow
else:
print "Hello, %(firstname) %(lastname)!" % databaserow
Which can make things much more complex.

Better to Insert NULL for consistency in your database in MySQL. Foreign keys can be stored as NULL but NOT as empty strings.
You will have issues with an empty string in the constraints.
You may have to insert a fake record with a unique empty string to satisfy a Foreign Key constraint. Bad practice I guess.
See also: Can a foreign key be NULL and/or duplicate?

I don't know what best practice would be here, but I would generally err in favor of the null unless you want null to mean something different from empty-string, and the user's input matches your empty-string definition.
Note that I'm saying YOU need to define how you want them to be different. Sometimes it makes sense to have them different, sometimes it doesn't. If not, just pick one and stick with it. Like I said, I tend to favor the NULL most of the time.
Oh, and bear in mind that if the column is null, the record is less likely to appear in practically any query that selects (has a where clause, in SQL terms) based off of that column, unless the selection is for a null column of course.

If you are using multiple columns in a unique index and at least one of these columns are mandatory (i.e. a required form field), if you set the other columns in the index to NULL you may end up with duplicated rows. That's because NULL values are ignored in unique columns. In this case, use empty strings in the other columns of the unique index to avoid duplicated rows.
COLUMNS IN A UNIQUE INDEX:
(event_type_id, event_title, date, location, url)
EXAMPLE 1:
(1, 'BBQ', '2018-07-27', null, null)
(1, 'BBQ', '2018-07-27', null, null) // allowed and duplicated.
EXAMPLE 2:
(1, 'BBQ', '2018-07-27', '', '')
(1, 'BBQ', '2018-07-27', '', '') // NOT allowed as it's duplicated.
Here are some codes:
CREATE TABLE `test` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`event_id` int(11) DEFAULT NULL,
`event_title` varchar(50) DEFAULT NULL,
`date` date DEFAULT NULL,
`location` varchar(50) DEFAULT NULL,
`url` varchar(200) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `event_id` (`event_id`,`event_title`,`date`,`location`,`url`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
Now insert this to see it will allow the duplicated rows:
INSERT INTO `test` (`id`, `event_id`, `event_title`, `date`, `location`,
`url`) VALUES (NULL, '1', 'BBQ', '2018-07-27', NULL, NULL);
INSERT INTO `test` (`id`, `event_id`, `event_title`, `date`, `location`,
`url`) VALUES (NULL, '1', 'BBQ', '2018-07-27', NULL, NULL);
Now insert this and check that it's not allowed:
INSERT INTO `test` (`id`, `event_id`, `event_title`, `date`, `location`,
`url`) VALUES (NULL, '1', 'BBQ', '2018-07-28', '', '');
INSERT INTO `test` (`id`, `event_id`, `event_title`, `date`, `location`,
`url`) VALUES (NULL, '1', 'BBQ', '2018-07-28', '', '');
So, there is no right or wrong here. It's up to you decide what works best with your business rules.

Related

MYSQL INSERT not adding values to table

I am using the INSERT function to try and add new values to a table. While there is no error when I run the query, it is not showing the new attributes added to the table. I have no idea why. I also have safe updates turned off. The values entered also match the value type for each column of the table.
CODE ENTERED:
INSERT INTO productlines
VALUES
('Jet Packs', 'Futuristic flying machines that only exist in prototype.', NULL, NULL),
('Jet Skis', 'Much more realistic things that very much exist already.', NULL, NULL),
('Wheelbarrows', 'I cannot believe we actually stock these.', NULL, NULL);
SELECT *
FROM productlines;
CREATE TABLE `productlines` (
`productLine` varchar(50) NOT NULL,
`textDescription` varchar(4000) DEFAULT NULL,
`htmlDescription` mediumtext,
`image` mediumblob,
PRIMARY KEY (`productLine`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
INSERT INTO
[name of your **TABLE**](col1, col2, col3)#NAME OF YOUR COLUMNS
VALUES
('value for col1','value for col2','value for col3'),
You didn't add the column names for which you are performing the insert

MYSQL insert null for not null default column

I have curious about why some not null column already set default value, but during insert sql script, it will throw error.
Here is the sample table
drop table if exists `delivery`;
create table `delivery`(
`price` BIGINT not null default 0,
`created_time` TIMESTAMP(6) not null default CURRENT_TIMESTAMP (6)
) ENGINE=INNODB DEFAULT CHARSET=UTF8MB4
;
Let's say, execute the three statement below, only the second statement will not throw error
insert into `delivery` (`price`,`created_time`) values (null, null);
insert into `delivery` (`price`,`created_time`) values (1, null);
insert into `delivery` (`price`,`created_time`) values (null, now());
So does it have anyway to insert null for bigint datatype column and make it execute success? And any ideas for the logic behind.
You can't insert null values since you have NOT NULL constraints on the columns.
The first and third statement throw an error since you are trying to insert a null value into the column price and/or created_time, and that clearly doesn't satisfy the constraint(s).
If you really want to allow null values, then remove the NOT NULL constraint on the column(s).
Alternatively, you could sucessfully run your SQL statements as shown below:
insert into `delivery` () values ();
insert into `delivery` (`price`) values (1);
insert into `delivery` (`created_time`) values (now());
select * from `delivery`;
Result:
price created_time
----- --------------------------
0 2020-04-16 09:48:23.505147
1 2020-04-16 09:48:25.549202
0 2020-04-16 09:48:26.0
EDIT:
The second query actually succeeds in MySQL 5.7; it silently ignores the explicit null value and uses the default value.
It seems that the behavior was fixed in MySQL 8.x since it fails now (as it should).

How to keep the same "INSERT INTO" statements in my code when adding a new column in my database with the "nullable" property and a default value?

Let's consider a "User" MySQL table with 2 columns:
Auto increment id
Full name
CREATE TABLE `user` ( `id` INT NOT NULL AUTO_INCREMENT, `fullname` TEXT NOT NULL, PRIMARY KEY (`id`));
INSERT INTO `user` VALUES (NULL, 'John Smith');
INSERT INTO `user` VALUES (NULL, 'James Miller');
Everywhere I add a user in my application, I'm having this code:
INSERT INTO `user` VALUES (NULL, <name to insert>);
Now if I add a 'is_admin' boolean with 0 as default value, like so:
ALTER TABLE `user` ADD COLUMN `is_admin` TINYINT NOT NULL DEFAULT 0;
If I run my insert query with VALUES (NULL, <name to insert>), I expect MySQL to understand that I want new users to have is_admin set to 0, but it doesn't work:
Error Code: 1136. Column count doesn't match value count at row 1
I thought it was because of the 'NOT NULL' flag so I changed the 'ADD COLUMN' statement from 'NOT NULL' to 'NULL':
ALTER TABLE `user` ADD COLUMN `is_admin` TINYINT NULL DEFAULT 0;
Now it's pretty obvious how I want it to behave. I said the default value should be 0 and I didn't even set it to mandatory. But I still get the same error.
I just have to update all my INSERT INTO user occurences to:
INSERT INTO `user` VALUES (NULL, <name to insert>, 0);
OR
INSERT INTO `user` VALUES (NULL, <name to insert>, default);
In this example it looks like it's not a big deal, but on a big application with tens of INSERT statements, it's a big deal because at each version, you must modify all insert statements for all altered tables, and if you miss a single one, then it doesn't work anymore.
I worked many years with Oracle and maybe I'm wrong but I thought I remembered Oracle was using default values when not provided.
Is there any way to do the same with MySQL?
Always list the columns when doing an insert. So:
INSERT INTO user (id, fullname)
VALUES (NULL, 'John Smith');
That, in turn, is overkill. Just do:
INSERT INTO user (fullname)
VALUES ('John Smith');
This will set is_admin (and any other columns) to their default values.

mysql find records that contain words from a different table?

SCENARIO:
I have one large table (let's call it "WordTable") with a list of words (let's call the field "theWord") that could have 10,000+ records.
I also have a large table (let's call it "MySentences") with a VARCHAR field (let's call the field "theSentence") that contains many varied sentences - it could have millions of records.
QUESTION:
What SQL could I write for the MySQL database to give me a list of which records in MySentences.theSentence contain any of the words from WordTable.theWord ?
Since there are many records in both tables, using numerous Like statements is not feasible. Would FullText Search allow some capability here?
Hopefully this helps... by the way, a "sentence" does not always need to have spaces... it could just be a collection of letters
Here are some MySQL scripts to illustrate the scenario:
CREATE TABLE `MySentences` (
`id` int(11) NOT NULL,
`theSentence` varchar(1000) NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=latin1;
INSERT INTO `MySentences` (`id`, `theSentence`) VALUES
(1, 'hereisatestsentence'),
(2, 'asdfasdfadsf'),
(3, 'today is a blue sky'),
(4, 'jk2k2lkjskylkjdsf'),
(5, 'ddddddd'),
(6, 'nothing'),
(7, 'sometest');
CREATE TABLE `WordTable` (
`id` int(11) NOT NULL,
`theWord` varchar(50) NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1;
INSERT INTO `WordTable` (`id`, `theWord`) VALUES
(1, 'test'),
(2, 'house'),
(3, 'blue'),
(4, 'sky');
ALTER TABLE `MySentences`
ADD PRIMARY KEY (`id`);
ALTER TABLE `WordTable`
ADD PRIMARY KEY (`id`);
ALTER TABLE `MySentences`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=8;
ALTER TABLE `WordTable`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=5;
I made a query using the LIKE operator in the JOIN clause which will find any sentence that contains word. The LIKE operator uses wildcards % which will match anything.
SELECT
A.theSentence, B.theWord
FROM
MySentences A
INNER JOIN WordTable B ON A.theSentence LIKE CONCAT('%',B.theWord,'%');
If you are interested in just the sentence that was matched, you could use the DISTINCT operator to see distinct results:
SELECT
DISTINCT A.theSentence
FROM
MySentences A
INNER JOIN WordTable B ON A.theSentence LIKE CONCAT('%',B.theWord,'%');
You split your string into rows using something like this
SQL split values to multiple rows
You need a separator char probably space _, but also be carefull, may have to remove special chars like , . : ;
Then you join that result to your WordTable and find which words are there.

mysql insert find next in sequence

Sorry, this one is hard to explain in the title.
I have a simple table like this:
CREATE TABLE `categories` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(125) NOT NULL,
`desc` text NOT NULL,
`ordering` int(10) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
)
The ordering column is normally set in the client-- that is, the client can drag and reorder these categories, so it is not auto_incremented.
My question is, when I want to insert a row outside the client using a direct SQL insert is there a way to quickly get the max of the ordering column in the same statement?
Something like:
INSERT INTO `categories` (title, desc, ordering)
VALUES ('test title', 'description', (SELECT max(ordering) FROM `categories`)+1);
I've tried a dozen variations on this theme with no success.
The trick to get that to work is to avoid using the VALUES clause, and instead use a SELECT as the rowsource for the INSERT,
something like this:
INSERT INTO `categories` (title, desc, ordering)
SELECT 'test title', 'description', MAX(ordering)+1 FROM `categories`
NOTE: This may work with MyISAM tables, which disallows concurrent inserts. But for other engines that allow concurrent INSERTS, this approach will likely be "broken by design". I don't think there is any guarantee that two INSERT statements running concurrently won't generate the same value for the ordering column.
This design is also "broken by design" when the categories table is empty, because MAX(ordering) would return a NULL. But an IFNULL function can fix that.
SELECT 'test title', 'description', IFNULL(MAX(ordering),0)+1 FROM `categories`
try this:
insert into `categories` (`title`, `desc`, `ordering`)
select 'test title','description', max(ordering) + 1 FROM `categories`