DEV Community

Cover image for Day 3: Creating and Dropping Databases – CREATE DATABASE, DROP DATABASE
CodeWithDhanian
CodeWithDhanian

Posted on

Day 3: Creating and Dropping Databases – CREATE DATABASE, DROP DATABASE

Creating a database is one of the most fundamental operations you will perform as a SQL developer or database engineer. It marks the moment you move from simply connecting to a database management system to actually owning and organizing your own structured data environment. In this tutorial, we will explore exactly how databases are created and removed, why these operations behave the way they do under the hood, and how to handle them responsibly in both development and production environments.

We will work with the two most popular open-source relational database management systems introduced earlier: PostgreSQL and MySQL. Although both support the core SQL commands CREATE DATABASE and DROP DATABASE, their implementations differ in important architectural ways that affect performance, security, and scalability.

What a Database Actually Represents

Before writing any code, it is essential to understand what a database truly is from an architectural perspective. In a relational database management system, a database is a named container that holds multiple schemas, tables, indexes, views, functions, and other database objects. It acts as a logical boundary for data isolation, access control, and resource allocation.

Physically, a database is more than just a name. In PostgreSQL, each database corresponds to a separate set of files on disk, managed by the postmaster process. These files store the actual data pages, transaction logs (WAL), and catalog metadata. In MySQL (using the default InnoDB storage engine), each database typically maps to a subdirectory under the MySQL data directory, with table definitions and data stored in .frm, .ibd, or similar files depending on the engine.

This separation is deliberate. It allows a single database server instance to host dozens or even hundreds of independent databases, each with its own security context and performance characteristics. This architecture is what enables multi-tenant applications, separate development/staging/production environments, and efficient resource isolation.

The CREATE DATABASE Statement

The primary command to create a new database is CREATE DATABASE. The basic syntax is intentionally simple, yet the optional clauses reveal deep control over how the database will behave.

-- PostgreSQL version
CREATE DATABASE my_app_production
    WITH OWNER = postgres
         ENCODING = 'UTF8'
         LC_COLLATE = 'en_US.UTF-8'
         LC_CTYPE = 'en_US.UTF-8'
         TEMPLATE = template0;
Enter fullscreen mode Exit fullscreen mode
-- MySQL version
CREATE DATABASE my_app_production
    CHARACTER SET utf8mb4
    COLLATE utf8mb4_unicode_ci;
Enter fullscreen mode Exit fullscreen mode

Let us break down every important part of these statements.

  • WITH (PostgreSQL only): Introduces a list of optional parameters that configure the new database at creation time.
  • OWNER: Specifies which role (user) will own the database. The owner has full rights to create objects, grant privileges, and drop the database later. In production systems, it is a best practice to create a dedicated application role with the least privileges necessary rather than using the default superuser.
  • ENCODING / CHARACTER SET: Defines how text data will be stored. UTF8 (PostgreSQL) or utf8mb4 (MySQL) is almost always the correct choice today because it properly supports international characters, emojis, and modern languages without data corruption.
  • LC_COLLATE and LC_CTYPE: Control how string sorting and character classification work. These settings affect query performance on text columns and the results of ORDER BY operations. Choosing the wrong collation can lead to unexpected sorting behavior or degraded index performance.
  • TEMPLATE (PostgreSQL): Every new database is cloned from a template. The default template1 includes standard objects and extensions. Using template0 gives you a completely clean slate, which is preferable when you want strict control over what exists in the new database.

In MySQL, the equivalent parameters are simpler because MySQL handles character sets at both the server and database level. The COLLATE clause determines the default sorting and comparison rules for all tables created inside that database unless overridden at the table or column level.

Why These Options Matter in Real Engineering

Choosing the right encoding and collation at creation time is not a cosmetic decision. Once a database is created, changing these properties is extremely difficult and often requires a full data export/import cycle. In large-scale systems, incorrect collation choices have caused production incidents where searches and sorts returned inconsistent results across environments.

Performance implications are also significant. A database with the wrong collation can prevent the query planner from using indexes efficiently during text comparisons, leading to full table scans even when indexes exist.

Creating Databases in Practice: Development Workflows

Professional developers rarely create databases manually in production. Instead, they follow repeatable workflows:

  1. Use infrastructure-as-code tools or migration scripts.
  2. Create separate databases for development, testing, and production.
  3. Automate creation as part of CI/CD pipelines so every developer works with identical starting conditions.

Here is a realistic example of a script you might keep in your project repository:

-- create_dev_db.sql
DO $$
BEGIN
    IF NOT EXISTS (SELECT FROM pg_database WHERE datname = 'my_app_dev') THEN
        PERFORM dblink_exec('host=localhost user=postgres',
            'CREATE DATABASE my_app_dev WITH OWNER = app_user ENCODING = ''UTF8''');
    END IF;
END
$$;
Enter fullscreen mode Exit fullscreen mode

This pattern uses a conditional check to make the script idempotent, meaning it can be run multiple times without error.

The DROP DATABASE Statement

Removing a database is the counterpart to creation and must be treated with respect. The syntax is:

-- PostgreSQL
DROP DATABASE my_app_production;

-- Safer version
DROP DATABASE IF EXISTS my_app_production;

-- MySQL
DROP DATABASE my_app_production;
DROP DATABASE IF EXISTS my_app_production;
Enter fullscreen mode Exit fullscreen mode

The IF EXISTS clause prevents an error if the database does not exist. This is especially valuable in automated scripts.

Important behavior differences:

  • In PostgreSQL, you cannot drop a database while active connections exist. The server will reject the command unless you use the WITH FORCE option (available in newer versions) or first terminate connections manually.
  • In MySQL, dropping a database is more permissive but still requires appropriate privileges.

DROP DATABASE is a permanent, non-recoverable operation in most cases. The files on disk are removed, and all data disappears. This is why production systems almost never allow direct DROP DATABASE commands except during controlled maintenance windows.

Internal Workflows and System Behavior

When you execute CREATE DATABASE:

  1. The database server validates your permissions.
  2. It allocates a new database OID (object identifier).
  3. It copies the template files (PostgreSQL) or creates a new subdirectory (MySQL).
  4. It updates the system catalogs (pg_database in PostgreSQL, mysql system database in MySQL).
  5. It initializes the default schema (public in PostgreSQL, the database itself in MySQL).

When you execute DROP DATABASE:

  1. The server checks for active connections and may block or terminate them.
  2. It removes all associated files from the filesystem.
  3. It cleans up metadata entries across system catalogs.
  4. Any open transactions involving that database are rolled back or invalidated.

These operations affect the entire server instance. On very large systems, creating or dropping databases can momentarily increase I/O load because of file system operations.

Best Practices and Scalability Considerations

  • Never create databases with generic names like "test" or "db1" in production. Use descriptive, versioned names that reflect purpose and environment.
  • Prefer schemas over multiple databases when you need logical separation within the same data set. This is more efficient because it avoids the overhead of managing separate file sets.
  • In multi-tenant SaaS applications, some teams create one database per customer for strong isolation, while others use a single database with tenant_id columns. Each approach has different scalability and backup implications.
  • Always back up critical databases before any DROP operation.
  • Grant the minimal privileges necessary. Application users should rarely have CREATE DATABASE or DROP DATABASE rights.

Real-World Engineering Example

Imagine you are building an e-commerce platform. You might maintain:

  • ecommerce_production – live customer data
  • ecommerce_staging – pre-production testing
  • ecommerce_dev – local development

You would create these using scripts that also initialize required extensions, default roles, and security policies. When a new feature requires schema changes that cannot be rolled back easily, you create a fresh database from a recent backup of production, test the migration thoroughly, then promote the change.

These patterns ensure consistency, reduce human error, and make your infrastructure reproducible.

For even more in-depth examples and exercises, consider purchasing this SQL Playbook: https://codewithdhanian.gumroad.com/l/hjmix

Day 3: Creating and Dropping Databases

Top comments (0)