Home Database Databases, Schemas, Tables & Data Types: A Beginner's Guide
Beginner 4 min · July 17, 2026

Databases, Schemas, Tables & Data Types: A Beginner's Guide

Learn Snowflake's hierarchical structure: databases, schemas, tables, and data types.

N
Naren Founder & Principal Engineer

20+ years shipping high-throughput database systems. Written from production experience, not tutorials.

Follow
Production
production tested
July 17, 2026
last updated
2,439
articles · all by Naren
Before you start⏱ 15-20 min read
  • Basic understanding of SQL (SELECT, INSERT, CREATE).
  • A Snowflake account (free trial available).
  • Familiarity with relational database concepts (tables, columns, data types).
 ● Production Incident 🔎 Debug Guide ⚙ Triage Commands
Quick Answer
  • Snowflake organizes data in a hierarchy: Account > Database > Schema > Table.
  • Databases contain schemas; schemas contain tables, views, functions, etc.
  • Tables store data in rows and columns with defined data types.
  • Snowflake supports standard SQL data types plus semi-structured types like VARIANT.
  • Use CREATE DATABASE, CREATE SCHEMA, CREATE TABLE commands to set up your environment.
✦ Definition~90s read
What is Databases, Schemas, Tables, and Data Types?

Snowflake databases, schemas, tables, and data types form the foundational structure for organizing and storing data in the Snowflake cloud data platform.

Think of Snowflake like a large office building.
Plain-English First

Think of Snowflake like a large office building. The building is your Snowflake account. Each floor is a database. On each floor, there are rooms (schemas). Inside each room, there are filing cabinets (tables). Each cabinet has drawers (columns) that hold specific types of documents (data types). You need to know where to put your files and what kind of files they are.

When you start working with Snowflake, the first thing you need to understand is how data is organized. Unlike traditional databases where you might just have a flat list of tables, Snowflake uses a hierarchical structure: Account → Database → Schema → Table. This structure helps you manage large amounts of data, enforce security, and organize your work logically.

Imagine you're building a data warehouse for an e-commerce company. You'll have different teams (marketing, sales, inventory) that need their own space. Snowflake's hierarchy allows you to create separate databases for each department, schemas for different projects, and tables for specific datasets. This not only keeps things tidy but also makes it easy to grant permissions at the right level.

In this tutorial, you'll learn how to create databases, schemas, and tables in Snowflake. We'll cover the essential data types you'll use daily, from simple strings and numbers to complex semi-structured data like JSON. You'll also see production-ready examples and common pitfalls to avoid. By the end, you'll be able to set up your Snowflake environment confidently and avoid the mistakes that can lead to costly production incidents.

Understanding Snowflake's Object Hierarchy

Snowflake organizes data in a multi-level hierarchy. At the top is your Snowflake account. Under the account, you can create multiple databases. Each database contains schemas, and each schema contains objects like tables, views, functions, and stored procedures. This structure is similar to a file system: account is the root, databases are folders, schemas are subfolders, and tables are files.

Why is this hierarchy important? It allows you to
  • Organize data logically (e.g., by department, project, or environment).
  • Control access at different levels (grant usage on database, schema, or table).
  • Avoid naming conflicts (same table name can exist in different schemas).

When you first log into Snowflake, you are placed in a default database and schema (usually PUBLIC). It's a common mistake to start creating tables there without realizing that PUBLIC is shared with all users who have access to the database. Always create your own schema for production work.

SELECT CURRENT_DATABASE(), CURRENT_SCHEMA();

USE DATABASE mydb; USE SCHEMA mydb.myschema;

USE SCHEMA mydb.myschema;

Now, let's create our first database and schema.

create_db_schema.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
-- Create a database for e-commerce analytics
CREATE DATABASE IF NOT EXISTS ecommerce_analytics;

-- Create a schema for sales data
CREATE SCHEMA IF NOT EXISTS ecommerce_analytics.sales;

-- Set context to the new schema
USE SCHEMA ecommerce_analytics.sales;

-- Verify current context
SELECT CURRENT_DATABASE(), CURRENT_SCHEMA();
Output
+---------------------+------------------------+
| CURRENT_DATABASE() | CURRENT_SCHEMA() |
|---------------------+------------------------|
| ECOMMERCE_ANALYTICS | SALES |
+---------------------+------------------------+
💡Naming Conventions
📊 Production Insight
In production, you might have separate databases for development, staging, and production. Use the same schema structure across environments to simplify deployment scripts.
🎯 Key Takeaway
Always create a dedicated schema for your work to avoid cluttering the PUBLIC schema and to manage permissions effectively.

Creating Tables in Snowflake

Tables are the core storage objects in Snowflake. They consist of rows and columns, where each column has a name and a data type. Snowflake supports both permanent and transient tables. Permanent tables have a fail-safe period for data recovery, while transient tables do not (but are cheaper). For most production use cases, you'll use permanent tables.

To create a table, you use the CREATE TABLE statement. You must specify the table name, column names, and their data types. You can also define constraints like NOT NULL, UNIQUE, PRIMARY KEY, and FOREIGN KEY. However, Snowflake does not enforce constraints except NOT NULL and UNIQUE (if you create a unique constraint, it creates a unique index). Primary keys and foreign keys are metadata only; they are not enforced.

CREATE TABLE orders ( order_id INTEGER AUTOINCREMENT, customer_id INTEGER NOT NULL, order_date DATE NOT NULL, total_amount DECIMAL(10,2), status VARCHAR(20) DEFAULT 'PENDING', PRIMARY KEY (order_id) );

Notice the AUTOINCREMENT property for order_id – it automatically generates a unique number for each row. The DEFAULT clause sets a default value for status. The PRIMARY KEY is informational; Snowflake will not prevent duplicate order_id values unless you also create a unique constraint.

Let's create a more comprehensive table for our e-commerce schema.

create_tables.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
-- Create customers table
CREATE TABLE customers (
    customer_id INTEGER AUTOINCREMENT,
    first_name VARCHAR(50) NOT NULL,
    last_name VARCHAR(50) NOT NULL,
    email VARCHAR(100) UNIQUE NOT NULL,
    signup_date DATE,
    PRIMARY KEY (customer_id)
);

-- Create orders table
CREATE TABLE orders (
    order_id INTEGER AUTOINCREMENT,
    customer_id INTEGER NOT NULL,
    order_date DATE NOT NULL,
    total_amount DECIMAL(10,2),
    status VARCHAR(20) DEFAULT 'PENDING',
    PRIMARY KEY (order_id),
    FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
);

-- Create order_items table
CREATE TABLE order_items (
    order_item_id INTEGER AUTOINCREMENT,
    order_id INTEGER NOT NULL,
    product_id INTEGER NOT NULL,
    quantity INTEGER NOT NULL,
    unit_price DECIMAL(10,2) NOT NULL,
    PRIMARY KEY (order_item_id),
    FOREIGN KEY (order_id) REFERENCES orders(order_id)
);

-- Show tables in current schema
SHOW TABLES;
Output
+---------------------------------+------+-------+---------+
| created_on | name | kind | database_name | schema_name |
|---------------------------------+------+-------+---------|
| 2025-03-15 10:00:00.000 -0700 | CUSTOMERS | TABLE | ECOMMERCE_ANALYTICS | SALES |
| 2025-03-15 10:00:01.000 -0700 | ORDERS | TABLE | ECOMMERCE_ANALYTICS | SALES |
| 2025-03-15 10:00:02.000 -0700 | ORDER_ITEMS | TABLE | ECOMMERCE_ANALYTICS | SALES |
+---------------------------------+------+-------+---------+
🔥Constraints in Snowflake
📊 Production Insight
In production, consider using transient tables for staging data that can be easily recreated. This saves on storage costs.
🎯 Key Takeaway
Use CREATE TABLE with appropriate data types and constraints. Remember that only NOT NULL and UNIQUE are enforced; other constraints are for documentation.

Snowflake Data Types: The Essentials

Snowflake supports a wide range of data types, categorized into: - Numeric: INTEGER, DECIMAL, FLOAT, etc. - String: VARCHAR, CHAR, TEXT, etc. - Date/Time: DATE, TIME, TIMESTAMP, etc. - Semi-structured: VARIANT, OBJECT, ARRAY (for JSON, Avro, Parquet, etc.) - Others: BOOLEAN, GEOGRAPHY, etc.

For beginners, the most common types are
  • INTEGER: Whole numbers (e.g., 1, 100, -5).
  • DECIMAL(p,s): Fixed-point numbers (e.g., DECIMAL(10,2) for currency).
  • VARCHAR(n): Variable-length strings (e.g., VARCHAR(100)).
  • DATE: Date without time (e.g., '2025-03-15').
  • TIMESTAMP: Date and time (e.g., '2025-03-15 14:30:00').
  • BOOLEAN: TRUE or FALSE.
  • VARIANT: Any semi-structured data (JSON, etc.).

Choosing the right data type is crucial for performance and storage. For example, use DATE instead of TIMESTAMP if you don't need time, as it uses less space. Use VARCHAR instead of TEXT for shorter strings.

Let's see how to use these types in a table.

data_types_example.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
-- Create a table with various data types
CREATE TABLE product_reviews (
    review_id INTEGER AUTOINCREMENT,
    product_id INTEGER NOT NULL,
    reviewer_name VARCHAR(100),
    rating INTEGER CHECK (rating BETWEEN 1 AND 5),
    review_text VARCHAR(5000),
    review_date DATE DEFAULT CURRENT_DATE,
    is_verified_purchase BOOLEAN DEFAULT FALSE,
    metadata VARIANT
);

-- Insert a sample row with JSON in VARIANT
INSERT INTO product_reviews (product_id, reviewer_name, rating, review_text, metadata)
VALUES (123, 'Alice', 4, 'Great product!', 
        PARSE_JSON('{"source": "web", "helpful_count": 12}'));

-- Query the VARIANT column
SELECT review_id, metadata:source::VARCHAR AS source
FROM product_reviews;
Output
+-----------+--------+
| REVIEW_ID | SOURCE |
|-----------+--------|
| 1 | web |
+-----------+--------+
⚠ VARIANT Performance
📊 Production Insight
In production, avoid using VARIANT for columns that are queried often. Instead, use structured columns and store the raw JSON in a VARIANT column only if needed for archival.
🎯 Key Takeaway
Choose data types carefully. Use VARIANT for semi-structured data, but extract frequently queried fields into regular columns.

Working with Semi-Structured Data: VARIANT, OBJECT, and ARRAY

Snowflake excels at handling semi-structured data like JSON, Avro, Parquet, and XML. The key data types are: - VARIANT: Can hold any value of any other data type, including OBJECT and ARRAY. - OBJECT: A collection of key-value pairs (like a JSON object). - ARRAY: An ordered list of elements.

You can load JSON data directly into a VARIANT column. Snowflake provides powerful functions to query this data, such as: - : (colon) operator to access object fields. - [] bracket notation for array elements. - FLATTEN function to expand arrays into rows. - PARSE_JSON to convert a string to VARIANT.

Let's see an example with an ARRAY of objects.

semi_structured.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
-- Create a table to store customer interactions
CREATE TABLE customer_interactions (
    interaction_id INTEGER AUTOINCREMENT,
    customer_id INTEGER,
    interaction_data VARIANT
);

-- Insert JSON data
INSERT INTO customer_interactions (customer_id, interaction_data)
VALUES (1, PARSE_JSON('{
    "type": "purchase",
    "timestamp": "2025-03-15T10:30:00",
    "items": [
        {"product_id": 101, "quantity": 2},
        {"product_id": 102, "quantity": 1}
    ]
}'));

-- Query: get all product IDs from the first interaction
SELECT interaction_id,
       interaction_data:items[0].product_id::INTEGER AS first_product
FROM customer_interactions;

-- Use FLATTEN to expand array
SELECT i.interaction_id,
       f.value:product_id::INTEGER AS product_id,
       f.value:quantity::INTEGER AS quantity
FROM customer_interactions i,
LATERAL FLATTEN(input => i.interaction_data:items) f;
Output
+----------------+---------------+
| INTERACTION_ID | FIRST_PRODUCT |
|----------------+---------------|
| 1 | 101 |
+----------------+---------------+
+----------------+------------+----------+
| INTERACTION_ID | PRODUCT_ID | QUANTITY |
|----------------+------------+----------|
| 1 | 101 | 2 |
| 1 | 102 | 1 |
+----------------+------------+----------+
💡Using LATERAL FLATTEN
📊 Production Insight
When loading JSON logs, consider using the COPY INTO command with file format options to automatically parse JSON into VARIANT columns.
🎯 Key Takeaway
Use VARIANT, OBJECT, and ARRAY to store semi-structured data. Use the colon operator and FLATTEN to query nested data.

Altering and Dropping Tables

After creating tables, you may need to modify them. Snowflake supports ALTER TABLE to add, drop, or modify columns, rename tables, and change other properties. You can also drop tables when they are no longer needed.

Important points
  • Adding a column: ALTER TABLE table_name ADD COLUMN column_name data_type;
  • Dropping a column: ALTER TABLE table_name DROP COLUMN column_name;
  • Renaming a column: ALTER TABLE table_name RENAME COLUMN old_name TO new_name;
  • Modifying a column's data type: ALTER TABLE table_name ALTER COLUMN column_name SET DATA TYPE new_type; (only if compatible)
  • Renaming a table: ALTER TABLE table_name RENAME TO new_name;
  • Dropping a table: DROP TABLE table_name;

Be cautious when altering tables in production. Dropping a column or changing data types can cause data loss or application errors. Always test in a development environment first.

Let's see some examples.

alter_drop.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
-- Add a new column 'discount' to orders table
ALTER TABLE orders ADD COLUMN discount DECIMAL(5,2) DEFAULT 0.00;

-- Rename 'total_amount' to 'gross_amount'
ALTER TABLE orders RENAME COLUMN total_amount TO gross_amount;

-- Modify data type of 'discount' to allow larger values
ALTER TABLE orders ALTER COLUMN discount SET DATA TYPE DECIMAL(10,2);

-- Rename table 'order_items' to 'line_items'
ALTER TABLE order_items RENAME TO line_items;

-- Drop the 'line_items' table (be careful!)
-- DROP TABLE line_items;

-- Show table details
DESCRIBE TABLE orders;
Output
+-------------+---------------+--------+-------+---------+-------------+------------+-------+------------+---------+
| name | type | kind | null? | default | primary key | unique key | check | expression | comment |
|-------------+---------------+--------+-------+---------+-------------+------------+-------+------------+---------|
| ORDER_ID | NUMBER(38,0) | COLUMN | Y | NULL | Y | N | NULL | NULL | NULL |
| CUSTOMER_ID | NUMBER(38,0) | COLUMN | N | NULL | N | N | NULL | NULL | NULL |
| ORDER_DATE | DATE | COLUMN | N | NULL | N | N | NULL | NULL | NULL |
| GROSS_AMOUNT| NUMBER(10,2) | COLUMN | Y | NULL | N | N | NULL | NULL | NULL |
| STATUS | VARCHAR(20) | COLUMN | Y | 'PENDING'| N | N | NULL | NULL | NULL |
| DISCOUNT | NUMBER(10,2) | COLUMN | Y | 0.00 | N | N | NULL | NULL | NULL |
+-------------+---------------+--------+-------+---------+-------------+------------+-------+------------+---------+
⚠ Dropping Tables
📊 Production Insight
In production, avoid renaming or dropping columns that are referenced by views or stored procedures. Use versioned deployments to manage schema changes.
🎯 Key Takeaway
Use ALTER TABLE to modify table structure. Always test changes in a non-production environment first.

Best Practices for Database, Schema, and Table Design

Designing your Snowflake environment properly from the start saves headaches later. Here are some best practices:

  1. Use separate databases for different environments (dev, test, prod). This prevents accidental data corruption.
  2. Use schemas to organize by domain (e.g., sales, inventory, marketing). This makes permissions easier.
  3. Name objects consistently – use lowercase with underscores (e.g., customer_orders). Snowflake converts to uppercase unless quoted, but using lowercase in scripts is more readable.
  4. Set appropriate data types – use the smallest type that fits your data. For example, use DATE instead of TIMESTAMP if time is not needed.
  5. Use NOT NULL constraints for columns that must have values. This improves data quality.
  6. Add comments to tables and columns using COMMENT statement. This helps other developers understand the schema.
  7. Plan for growth – use AUTOINCREMENT for surrogate keys, but consider using sequences for high-volume tables.
  8. Avoid using reserved words as object names (e.g., TABLE, SELECT). If you must, use double quotes.

Let's see an example of a well-designed schema with comments.

best_practices.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
-- Create a database for production
CREATE DATABASE IF NOT EXISTS prod_ecommerce;

-- Create schema for sales
CREATE SCHEMA IF NOT EXISTS prod_ecommerce.sales;

-- Create a table with comments
CREATE TABLE prod_ecommerce.sales.orders (
    order_id INTEGER AUTOINCREMENT COMMENT 'Unique order identifier',
    customer_id INTEGER NOT NULL COMMENT 'Foreign key to customers',
    order_date DATE NOT NULL COMMENT 'Date order was placed',
    total_amount DECIMAL(10,2) COMMENT 'Total order amount before discounts',
    status VARCHAR(20) DEFAULT 'PENDING' COMMENT 'Order status: PENDING, SHIPPED, DELIVERED, CANCELLED',
    PRIMARY KEY (order_id)
) COMMENT = 'Stores customer orders';

-- Add table comment
COMMENT ON TABLE prod_ecommerce.sales.orders IS 'Main orders table for e-commerce platform';

-- Add column comments
ALTER TABLE prod_ecommerce.sales.orders MODIFY COLUMN total_amount COMMENT 'Total amount in USD';

-- View comments
SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'ORDERS';
SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'ORDERS';
Output
Comments are stored in INFORMATION_SCHEMA views. You can query them to see documentation.
🔥Information Schema
📊 Production Insight
In production, use a schema migration tool (like Flyway or Liquibase) to manage changes. This ensures consistency across environments.
🎯 Key Takeaway
Follow naming conventions, use appropriate data types, and document your schema with comments for maintainability.
● Production incidentPOST-MORTEMseverity: high

The Case of the Missing Schema: A Snowflake Permission Disaster

Symptom
Users could see tables they shouldn't have access to.
Assumption
The developer assumed that creating a new database would automatically create a private schema for their team.
Root cause
The developer created tables in the default PUBLIC schema, which had broad access permissions.
Fix
Created a dedicated schema for the team, moved tables using ALTER TABLE ... RENAME TO, and revoked permissions on PUBLIC schema.
Key lesson
  • Always create a dedicated schema for each team or project.
  • Never use the default PUBLIC schema for production data.
  • Grant permissions at the schema level, not the database level.
  • Use descriptive schema names to avoid confusion.
  • Review default permissions on new databases.
Production debug guideSymptom to Action4 entries
Symptom · 01
Table not found error when querying
Fix
Check if you are using the correct database and schema. Use SHOW TABLES IN SCHEMA <db>.<schema>; to list tables.
Symptom · 02
Permission denied when creating table
Fix
Verify you have CREATE TABLE privilege on the schema. Use SHOW GRANTS TO USER <username>; to check permissions.
Symptom · 03
Cannot insert data due to data type mismatch
Fix
Check the table definition with DESCRIBE TABLE <table_name>; and compare with your INSERT statement.
Symptom · 04
Semi-structured data query returning NULL
Fix
Ensure you are using the correct path in the FLATTEN or bracket notation. Use TRY_PARSE_JSON to validate JSON.
★ Quick Debug Cheat SheetCommon Snowflake hierarchy issues and immediate fixes.
Table not found
Immediate action
Set context: USE DATABASE <db>; USE SCHEMA <schema>;
Commands
SHOW TABLES;
SELECT * FROM <db>.<schema>.<table> LIMIT 10;
Fix now
Fully qualify table name with database and schema.
Permission denied+
Immediate action
Check current role: SELECT CURRENT_ROLE();
Commands
SHOW GRANTS TO USER <username>;
GRANT USAGE ON DATABASE <db> TO ROLE <role>;
Fix now
Request appropriate role or schema privileges.
Data type mismatch+
Immediate action
Describe table: DESCRIBE TABLE <table>;
Commands
SELECT TYPEOF(<column>) FROM <table> LIMIT 1;
ALTER TABLE <table> ALTER <column> SET DATA TYPE <new_type>;
Fix now
Cast the value in the INSERT: INSERT INTO <table> VALUES (CAST('value' AS <type>));
Data TypeCategoryExampleUse Case
INTEGERNumeric100Counts, IDs
DECIMAL(10,2)Numeric1234.56Currency, precise decimals
VARCHAR(100)String'Alice'Names, short text
DATEDate/Time'2025-03-15'Dates without time
TIMESTAMPDate/Time'2025-03-15 14:30:00'Full timestamps
BOOLEANLogicalTRUEFlags, yes/no
VARIANTSemi-structured{"key": "value"}JSON, flexible data
⚙ Quick Reference
6 commands from this guide
FileCommand / CodePurpose
create_db_schema.sqlCREATE DATABASE IF NOT EXISTS ecommerce_analytics;Understanding Snowflake's Object Hierarchy
create_tables.sqlCREATE TABLE customers (Creating Tables in Snowflake
data_types_example.sqlCREATE TABLE product_reviews (Snowflake Data Types
semi_structured.sqlCREATE TABLE customer_interactions (Working with Semi-Structured Data
alter_drop.sqlALTER TABLE orders ADD COLUMN discount DECIMAL(5,2) DEFAULT 0.00;Altering and Dropping Tables
best_practices.sqlCREATE DATABASE IF NOT EXISTS prod_ecommerce;Best Practices for Database, Schema, and Table Design

Key takeaways

1
Snowflake's hierarchical structure (Account > Database > Schema > Table) helps organize data and manage permissions.
2
Create dedicated schemas for different projects or teams to avoid cluttering the PUBLIC schema.
3
Choose data types carefully
use DATE for dates, DECIMAL for currency, VARCHAR with length, and VARIANT for semi-structured data.
4
Use ALTER TABLE to modify tables, but test changes in a non-production environment first.
5
Document your schema with comments and follow naming conventions for maintainability.

Common mistakes to avoid

5 patterns
×

Creating tables in the PUBLIC schema without realizing it's shared.

×

Using VARCHAR without specifying length (defaults to 16 MB).

×

Assuming PRIMARY KEY enforces uniqueness.

×

Using TIMESTAMP instead of DATE when time is not needed.

×

Forgetting to set context before querying.

INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01JUNIOR
What is the hierarchy of objects in Snowflake?
Q02JUNIOR
How do you create a table with a default value and an auto-incrementing ...
Q03SENIOR
Explain the difference between VARIANT, OBJECT, and ARRAY data types.
Q04SENIOR
How do you query nested JSON data in Snowflake?
Q05SENIOR
What are the limitations of primary keys in Snowflake?
Q01 of 05JUNIOR

What is the hierarchy of objects in Snowflake?

ANSWER
Account > Database > Schema > Table (and other objects like views, functions).
FAQ · 5 QUESTIONS

Frequently Asked Questions

01
What is the difference between a database and a schema in Snowflake?
02
Can I have tables with the same name in different schemas?
03
How do I change the data type of a column in Snowflake?
04
What is the maximum size of a VARCHAR column?
05
How do I recover a dropped table?
N
Naren Founder & Principal Engineer

20+ years shipping high-throughput database systems. Written from production experience, not tutorials.

Follow
Verified
production tested
July 17, 2026
last updated
2,439
articles · all by Naren
🔥

That's Snowflake. Mark it forged?

4 min read · try the examples if you haven't

Previous
Getting Started: Account Setup, Web UI, and First Queries
3 / 33 · Snowflake
Next
Virtual Warehouses: Compute Resources, Sizing, and Auto-Scaling