AD JB Toolbox 01 EN

MariaDB Tutorial / Define Input Rules with NULL, DEFAULT, NOT NULL, and CHECK

NULL represents missing or unknown information and differs from zero or an empty string. DEFAULT supplies a value when an input is omitted, NOT NULL rejects NULL, and CHECK tests a value against a condition; combine them because each solves a different validation problem.

Separate NULL, an empty string, zero, and omission

Input state Meaning Example
NULL Missing, unknown, or not yet determined No note has been collected
Empty string '' A known string of length zero A note was submitted without text
0 The numeric value zero No units in stock
Column omitted Not listed in the INSERT column list The stock default supplies zero

A business may choose to treat an uncollected note and an empty note alike, but that is a data policy. Automatically replacing every NULL with an empty string removes the ability to distinguish those states. This exercise uses ordinary string handling and does not enable compatibility modes such as EMPTY_STRING_IS_NULL. Distinguish these rules from uniqueness in Identify Rows with PRIMARY KEY, AUTO_INCREMENT, and UNIQUE and from querying stored NULL values in Compare NULL Correctly with IS NULL and COALESCE.

Most ordinary comparisons with NULL produce an unknown result, represented by NULL, rather than true or false. Use IS NULL to test for it, not = NULL. The NULL-value reference explains the operators and exceptions.

Use each constraint for its own responsibility

Definition What it does What it does not guarantee
DEFAULT 0 Provides zero when the column is omitted or DEFAULT is requested Does not generally replace every explicitly supplied NULL
NOT NULL Rejects a missing-value marker Does not prohibit an empty string, zero, or every negative value
CHECK (stock >= 0) Rejects a false nonnegative-stock condition Does not itself reject NULL on a nullable column
NOT NULL with CHECK Requires a value and checks its condition Does not replace application feedback or review of existing data

Do not use a default to invent unknown information. If stock has not been counted, zero may incorrectly mean sold out. This example deliberately defines a new item’s initial stock as zero; another business may need NULL or an independent verification status.

Application checks can provide useful messages, while database constraints protect the storage boundary when another application, client, or SQL file writes data. Both layers have a purpose; validating only the visible form does not constrain every writer.

Run a complete example of defaults and nullable values

Connect as course_user to the practice server prepared in the installation guides, with the shopcourse database available. The command-line client and SQL-file guide explains the connection and SOURCE command. Run these checks inside that connection, not in the operating-system shell:

SELECT VERSION() AS server_version, DATABASE() AS selected_database;
USE shopcourse;
SELECT DATABASE() AS selected_database;

The selected database should be shopcourse. Save a complete normal example as a UTF-8 .sql file and run it with SOURCE and its actual local path. Keep the intentionally failing statements separate: an error can stop a source file. Use a learning instance, not a connection containing production data.

The normal file inserts only valid rows. It saves the session mode and CHECK setting, then explicitly enables strict mode and CHECK evaluation. Check the actual server version when working with old installations rather than assuming that accepting CHECK syntax always proves enforcement.

USE shopcourse;
SET @saved_sql_mode = @@SESSION.sql_mode;
SET @saved_check_constraints = @@SESSION.check_constraint_checks;
SET SESSION sql_mode = 'STRICT_TRANS_TABLES';
SET SESSION check_constraint_checks = ON;
CREATE TABLE validation_lab (
  item_id INT PRIMARY KEY,
  item_name VARCHAR(100) NOT NULL,
  note VARCHAR(100) NULL DEFAULT NULL,
  stock INT NOT NULL DEFAULT 0,
  CONSTRAINT ck_validation_stock CHECK (stock >= 0)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO validation_lab (item_id, item_name, note) VALUES
  (1, 'Notebook', NULL),
  (2, 'Mug', '');
SELECT item_id, note IS NULL AS note_is_null,
       note = '' AS note_is_empty, stock
FROM validation_lab ORDER BY item_id;
CREATE TABLE nullable_check_lab (
  item_id INT PRIMARY KEY,
  stock INT NULL,
  CONSTRAINT ck_nullable_stock CHECK (stock >= 0)
) ENGINE=InnoDB;
INSERT INTO nullable_check_lab VALUES (1, NULL);
SELECT item_id, stock IS NULL AS stock_is_null FROM nullable_check_lab;
Row note_is_null note_is_empty stock
1 1 NULL 0
2 0 1 0

The first note is NULL, so its comparison with an empty string is also NULL. The second note is an empty string, making IS NULL false and the empty-string comparison true. Both inserts omit stock, so both receive the default zero.

The separate nullable_check_lab accepts a NULL stock value. Its CHECK expression does not evaluate to false for NULL. If stock must always be known and nonnegative, specify NOT NULL as well as CHECK; a nonnegative comparison alone is insufficient.

Test NOT NULL and CHECK failures separately

After the valid example, execute these statements one at a time. The first supplies NULL for a required name; the second supplies a name but violates the nonnegative-stock constraint:

INSERT INTO validation_lab (item_id, item_name) VALUES (3, NULL);
INSERT INTO validation_lab (item_id, item_name, stock) VALUES (3, 'Desk lamp', -1);

Expect a cannot-be-null error for the first and a constraint failure identifying the stock rule for the second. After each, SELECT COUNT(*) FROM validation_lab; should remain two. The failed item identifier 3 was not saved, so it can be reused in the second failure test. Exact error wording can vary by version and output language; identify the column or constraint that failed.

A default, the input form, and the type’s rules determine whether an explicit NULL is allowed, rejected, or handled specially. AUTO_INCREMENT and TIMESTAMP have special NULL behavior in some cases, so do not generalize this ordinary note-and-stock example to every type.

Inspect blank values, named rules, and enforcement settings

A NOT NULL string may still contain an empty string, as the string-type discussion explains. A rule such as CHECK (CHAR_LENGTH(TRIM(item_name)) > 0) can additionally reject empty names and names consisting of ordinary spaces. Do not assume default TRIM behavior removes every Unicode whitespace character or invisible glyph.

SHOW CREATE TABLE validation_lab;
SELECT @@SESSION.sql_mode, @@SESSION.check_constraint_checks;

The name ck_validation_stock makes a failed rule easier to locate. Use meaningful names that identify the table, column, or condition, and check naming scope when supporting different server versions. The constraint reference documents CHECK syntax and enforcement controls.

Disabling check_constraint_checks can permit data that violates the declared checks. Fix the input or the intended rule instead of disabling validation merely to make a write succeed. Switching checks on also does not mean that every previously stored invalid row has been reviewed or repaired.

Restore settings and repeat only the disposable exercise

SET SESSION check_constraint_checks = @saved_check_constraints;
SET SESSION sql_mode = @saved_sql_mode;

Restore these session settings in the same connection that saved the variables. If you disconnected, those variables no longer contain the original values; inspect the new connection instead of applying an empty saved variable.

Before repeating the exercise, use SELECT DATABASE(); and SHOW CREATE TABLE table_name;, substituting each actual table name, to confirm that the targets are the disposable tables created here. Back up any data you need to keep. Only after verifying those targets, remove them with:

DROP TABLE shopcourse.validation_lab, shopcourse.nullable_check_lab;

This removes both the named tables and their rows. A data-definition statement such as DROP TABLE is not undone by an ordinary ROLLBACK. You can then rerun the complete normal example. The existing products table from the first product-table exercise is deliberately outside this reset.

More in This Category
MariaDB Tutorial / Connect with the Command-Line Client and Run SQL Files

MariaDB Tutorial / Connect with the Command-Line Client and Run SQL Files

Connect to MariaDB with explicit TCP and UTF-8 settings, check account and query results, and execute a complete SQL file with SOURCE. Learn how to diagnose path, encoding, and partial-execution errors.

MariaDB Tutorial / Use Subqueries, EXISTS, and NOT EXISTS

MariaDB Tutorial / Use Subqueries, EXISTS, and NOT EXISTS

Choose scalar and existence subqueries according to the result needed. Compare paid-order customers and absent-order customers, inspect correlation keys and avoid NULL-related exclusion assumptions.

MariaDB Tutorial / Choose Conditional Values and Aggregates with CASE

MariaDB Tutorial / Choose Conditional Values and Aggregates with CASE

Use CASE for stock labels and conditional reports. Compare branch order, ELSE and NULL behavior, and understand why COUNT with ELSE 0 counts every non-NULL result.

MariaDB Tutorial / Break Queries into Named Steps with WITH and CTEs

MariaDB Tutorial / Break Queries into Named Steps with WITH and CTEs

Build a readable multi-step query with WITH, verify paid orders and per-order totals independently, and understand CTE scope, naming and execution-plan limitations.

MariaDB Tutorial / Use Stored Procedures, Functions, and Triggers

MariaDB Tutorial / Use Stored Procedures, Functions, and Triggers

Compare procedures, functions, and triggers with complete stock, amount, and audit examples. Verify commit, rollback, invalid inputs, and the difference between caller and definer privileges.

MariaDB Tutorial / Troubleshoot Local and Remote MariaDB Connections

MariaDB Tutorial / Troubleshoot Local and Remote MariaDB Connections

Diagnose MariaDB connections by layer, verify local TCP identity and actual table access, and review remote listener, host, firewall and certificate requirements without weakening checks by default.

MariaDB Tutorial / Build a Local MariaDB Lab with Docker Compose

MariaDB Tutorial / Build a Local MariaDB Lab with Docker Compose

Configure a local MariaDB container with Compose, file-based secrets, a healthcheck, and a named volume. Verify connections and distinguish container replacement from deleting persistent data.

MariaDB Tutorial / Compare ROW_NUMBER, RANK, and DENSE_RANK

MariaDB Tutorial / Compare ROW_NUMBER, RANK, and DENSE_RANK

Compare ROW_NUMBER, RANK and DENSE_RANK on customer totals, then run regional rankings and an outer top-N filter. Check how a row limit differs from retaining tied ranks.

MariaDB Tutorial / Change Table Structure Safely with ALTER TABLE

MariaDB Tutorial / Change Table Structure Safely with ALTER TABLE

Practice an add-fill-constrain migration with a product SKU column, verify counts and rejected duplicates, and review metadata locks, online-change limits and recovery boundaries.

MariaDB Tutorial / Save Reusable Query Definitions with VIEW

MariaDB Tutorial / Save Reusable Query Definitions with VIEW

Create an INVOKER view for paid-order totals, inspect its definition and observe transaction changes. Review security, updateability, dependencies and the difference between a view and stored result data.