Learn to query data across multiple tables and understand complex database relationships.
Understanding the fundamentals of table relationships:
Master the art of querying across multiple tables:
Learn to write efficient database access methods:
Foreign keys are a fundamental concept in relational databases that establish connections between tables, enforcing referential integrity and defining relationships.
Example of creating foreign key relationships:
-- Creating tables with foreign key relationships CREATE TABLE departments ( id INTEGER PRIMARY KEY, name TEXT NOT NULL UNIQUE ); CREATE TABLE employees ( id INTEGER PRIMARY KEY, first_name TEXT NOT NULL, last_name TEXT NOT NULL, department_id INTEGER, FOREIGN KEY (department_id) REFERENCES departments(id) ON DELETE SET NULL -- If department is deleted, set to NULL ON UPDATE CASCADE -- If department ID changes, update automatically );
With Knex.js, you can create foreign key relationships in migrations:
exports.up = function(knex) { return knex.schema .createTable('departments', table => { table.increments('id'); table.string('name').notNullable().unique(); }) .createTable('employees', table => { table.increments('id'); table.string('first_name').notNullable(); table.string('last_name').notNullable(); table.integer('department_id') .references('id') .inTable('departments') .onDelete('SET NULL') .onUpdate('CASCADE'); }); };
Querying data from multiple tables is a core capability of relational databases, allowing you to combine and analyze related information across your database.
Example SQL JOIN queries:
-- INNER JOIN: Get employees with their department names SELECT employees.id, employees.first_name, employees.last_name, departments.name AS department_name FROM employees INNER JOIN departments ON employees.department_id = departments.id; -- LEFT JOIN: Get all employees, even those without departments SELECT employees.id, employees.first_name, employees.last_name, departments.name AS department_name FROM employees LEFT JOIN departments ON employees.department_id = departments.id;
Using Knex.js for joins:
// INNER JOIN with Knex knex('employees') .select( 'employees.id', 'employees.first_name', 'employees.last_name', 'departments.name as department_name' ) .join('departments', 'employees.department_id', 'departments.id') .then(employees => { console.log(employees); }); // LEFT JOIN with Knex knex('employees') .select( 'employees.id', 'employees.first_name', 'employees.last_name', 'departments.name as department_name' ) .leftJoin('departments', 'employees.department_id', 'departments.id') .then(employees => { console.log(employees); });
Well-designed database access methods encapsulate your database interactions, making your code more maintainable, testable, and reusable.
Example of database access methods with Knex.js:
// employees-model.js const db = require('../data/db-config'); module.exports = { findAll, findById, findByDepartment, create, update, remove }; function findAll() { return db('employees') .select( 'employees.id', 'employees.first_name', 'employees.last_name', 'departments.name as department_name' ) .leftJoin('departments', 'employees.department_id', 'departments.id'); } function findById(id) { return db('employees') .select( 'employees.id', 'employees.first_name', 'employees.last_name', 'departments.name as department_name' ) .leftJoin('departments', 'employees.department_id', 'departments.id') .where('employees.id', id) .first(); } function findByDepartment(departmentId) { return db('employees') .where('department_id', departmentId); } async function create(employee) { const [id] = await db('employees').insert(employee); return findById(id); } function update(id, changes) { return db('employees') .where('id', id) .update(changes) .then(() => findById(id)); } function remove(id) { return db('employees') .where('id', id) .del(); }
In this module's guided project, you will:
After completing this module, you'll move on to Module 4: Data Modeling, where you'll learn about: