Building the Customers Table: A Practical Guide to SQL Schema
Learn to build a robust customers table in PostgreSQL. Master data types, field selection, and column constraints to ensure clean, reliable user data.

Previously in this course, we covered the foundational concepts of designing the products table using SQL DDL. In this lesson, we shift our focus to the human side of our store application by building a customers table to store profile information.
A well-structured table is the difference between an application that "just works" and one that handles real-world data gracefully. We will define the essential fields for a customer, choose the correct PostgreSQL data types for text and contact information, and apply constraints to ensure our data remains accurate.
Defining Customer Fields and Data Types
When designing a schema, you must choose data types that accurately represent your data while minimizing storage overhead. For a customers table, we generally need fields for identity, names, and contact details.
Here is the breakdown of our field selection:
id: We will use a numeric identifier (this will become a Primary Key in later lessons).first_name&last_name: We use theVARCHARtype.VARCHAR(n)is ideal for names as it enforces a maximum length, which is a simple way to prevent excessively long strings from polluting your database.email: We useVARCHAR(255). It is important to leave enough room for long email addresses while keeping the column bounded.phone: Even though phone numbers consist of digits, we store them asVARCHAR. This prevents issues with leading zeros (e.g.,091...) and allows for optional formatting characters like+,-, or().
Implementing the Customers Table

Let's put this into practice. Open your SQL editor (or psql) connected to your store database and execute the following statement:
SQLCREATE TABLE customers ( id SERIAL PRIMARY KEY, first_name VARCHAR(100) NOT NULL, last_name VARCHAR(100) NOT NULL, email VARCHAR(255) NOT NULL, phone VARCHAR(20), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );
Understanding the Constraints
In the code above, we introduced three critical concepts:
SERIAL: This automatically handles incrementing the ID for every new customer. It ensures every row has a unique identifier without you having to manually manage the counter.NOT NULL: By markingfirst_name,last_name, andemailasNOT NULL, we tell PostgreSQL to reject any row insertion that lacks these essential values. This is your first line of defense against "dirty" data.DEFAULT CURRENT_TIMESTAMP: This automatically populates thecreated_atfield with the exact time the row was inserted, providing an audit trail for your users.
Hands-on Exercise
Now it's your turn to expand the schema.
Task: Modify your customers table (or create a new one called customers_v2 for practice) to include a mailing_address field.
- Requirement: Use a
TEXTdata type, as addresses vary significantly in length. - Challenge: Add a constraint to ensure the
emailfield is unique, preventing the same user from registering multiple times with the same address. (Hint: AddUNIQUEafter theVARCHAR(255)definition for the email column).
Common Pitfalls to Avoid

- Over-constraining field lengths: While
VARCHAR(10)might seem sufficient for a first name, it will fail for names like "Christopher" or international names. Always provide a generous buffer (like100). - Using
INTfor phone numbers: Never store phone numbers as integers. You will lose leading zeros and be unable to store international country codes that start with+. - Ignoring NULLs: If a field is required for your business logic (like an email address), always define it as
NOT NULL. Relying on application-side validation is risky; database-level constraints are your final safety net.
Frequently Asked Questions
Q: Should I use CHAR or VARCHAR for names?
A: Always use VARCHAR. CHAR is a fixed-length type; if you define CHAR(100) and store "John", PostgreSQL will pad the remaining 96 spaces with blanks, wasting storage.
Q: Is TEXT better than VARCHAR?
A: In modern PostgreSQL, there is no performance difference between TEXT and VARCHAR. Use VARCHAR(n) when you want to enforce a specific business limit (like a name length), and use TEXT for open-ended fields like addresses or bio descriptions.
Q: Can I change these constraints later?
A: Yes. As you learn more about defining entities and attributes in data modeling, you may find the need to alter tables. PostgreSQL makes it easy to add or drop constraints using ALTER TABLE commands.
Recap

In this lesson, we established the foundation for user management by creating a customers table. We selected appropriate data types like VARCHAR for strings and SERIAL for IDs, while using NOT NULL constraints to ensure high-quality, reliable data. These schema-level guardrails ensure your application remains stable as you scale.
Up next: We will begin retrieving the data we've stored by mastering the basics of SELECT queries.
Work with me

Laravel SaaS MVP & Multi-Tenant App Development
Launch your SaaS MVP on Laravel โ multi-tenant, subscription-ready, and built by the engineer behind a platform serving 10,000+ paying users.

AI Automation & Agentic Workflow Development
Automate the repetitive work eating your time โ content pipelines, data workflows, and agentic AI tasks that run themselves.

