{"id":51123280,"url":"https://github.com/iamskyy666/postgresql-resources","last_synced_at":"2026-06-25T05:01:14.935Z","repository":{"id":358198815,"uuid":"1239603773","full_name":"iamskyy666/postgresql-resources","owner":"iamskyy666","description":"PostgreSQL - resources 🔵","archived":false,"fork":false,"pushed_at":"2026-05-16T07:40:54.000Z","size":25,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-16T08:50:57.650Z","etag":null,"topics":["postgresql","sql"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/iamskyy666.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-15T08:55:30.000Z","updated_at":"2026-05-16T07:40:57.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/iamskyy666/postgresql-resources","commit_stats":null,"previous_names":["iamskyy666/postgresql-resources"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/iamskyy666/postgresql-resources","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iamskyy666%2Fpostgresql-resources","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iamskyy666%2Fpostgresql-resources/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iamskyy666%2Fpostgresql-resources/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iamskyy666%2Fpostgresql-resources/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/iamskyy666","download_url":"https://codeload.github.com/iamskyy666/postgresql-resources/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iamskyy666%2Fpostgresql-resources/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34760219,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-25T02:00:05.521Z","response_time":101,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["postgresql","sql"],"created_at":"2026-06-25T05:01:13.606Z","updated_at":"2026-06-25T05:01:14.911Z","avatar_url":"https://github.com/iamskyy666.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# SQL vs NoSQL Databases — In Depth\n\nDatabases are systems used to **store, organize, retrieve, and manage data**.\n\nThe two major categories are:\n\n1. **SQL Databases (Relational Databases)**\n2. **NoSQL Databases (Non-Relational Databases)**\n\n---\n\n# 1. SQL Databases (Relational Databases)\n\nSQL databases store data in **tables** with:\n\n* Rows\n* Columns\n* Relationships\n\nExample:\n\n## Users Table\n\n| id | name | email                                   |\n| -- | ---- | --------------------------------------- |\n| 1  | Skyy | [skyy@gmail.com](mailto:skyy@gmail.com) |\n\n## Orders Table\n\n| id | user_id | product |\n| -- | ------- | ------- |\n| 1  | 1       | Laptop  |\n\nHere:\n\n* `user_id` links the `orders` table with the `users` table.\n* This relationship is the core idea behind relational databases.\n\n---\n\n# SQL = Structured Query Language\n\nSQL is the language used to interact with relational databases.\n\nExample:\n\n```sql\nSELECT * FROM users;\n```\n\n---\n\n# Popular SQL Databases\n\n* PostgreSQL\n* MySQL\n* SQLite\n* Microsoft SQL Server\n* Oracle Database\n\n---\n\n# Core Features of SQL Databases\n\n---\n\n## A) Structured Schema\n\nSQL databases require a **fixed schema**.\n\nYou define:\n\n* table names\n* column names\n* data types\n* constraints\n\nExample:\n\n```sql\nCREATE TABLE users (\n    id INTEGER PRIMARY KEY,\n    name TEXT NOT NULL,\n    age INTEGER\n);\n```\n\nThis means:\n\n* `name` must exist\n* `age` must be integer\n* structure is predefined\n\n---\n\n## B) Relationships\n\nSQL databases are designed for relationships.\n\nExample:\n\n* users\n* orders\n* payments\n* products\n\ncan all be connected using:\n\n* foreign keys\n* joins\n\nExample:\n\n```sql\nSELECT users.name, orders.product\nFROM users\nJOIN orders\nON users.id = orders.user_id;\n```\n\n---\n\n## C) ACID Transactions\n\nSQL databases strongly support:\n\n# ACID\n\nMeaning:\n\n| Letter | Meaning     |\n| ------ | ----------- |\n| A      | Atomicity   |\n| C      | Consistency |\n| I      | Isolation   |\n| D      | Durability  |\n\n---\n\n## Atomicity\n\nEither everything succeeds or nothing succeeds.\n\nExample:\n\nBank transfer:\n\n```text\n- Deduct ₹1000 from A\n- Add ₹1000 to B\n```\n\nIf one fails, both rollback.\n\n---\n\n## Consistency\n\nDatabase rules remain valid.\n\n---\n\n## Isolation\n\nMultiple transactions don't corrupt each other.\n\n---\n\n## Durability\n\nOnce committed, data survives crashes.\n\n---\n\n# SQL databases are excellent for:\n\n* banking\n* finance\n* accounting\n* ERP systems\n* ecommerce orders\n* inventory systems\n\nwhere correctness matters more than flexibility.\n\n---\n\n# Advantages of SQL Databases\n\n## 1. Strong consistency\n\nVery reliable.\n\n---\n\n## 2. Powerful querying\n\nComplex queries are easy.\n\nExample:\n\n```sql\nGROUP BY\nJOIN\nHAVING\nSUBQUERIES\nWINDOW FUNCTIONS\nCTEs\n```\n\nSQL is extremely powerful for analytics.\n\n---\n\n## 3. Relationships are natural\n\nPerfect for interconnected data.\n\n---\n\n## 4. Mature ecosystem\n\nSQL databases are decades old and battle-tested.\n\nEspecially:\n\n* PostgreSQL\n* MySQL\n\n---\n\n# Disadvantages of SQL Databases\n\n---\n\n## 1. Rigid schema\n\nChanging structure later can be harder.\n\nExample:\n\nAdding/removing columns in massive production systems.\n\n---\n\n## 2. Horizontal scaling is harder\n\nScaling across many servers is more difficult.\n\nTraditionally SQL prefers:\n\n```text\nVertical Scaling\n↑\nMore RAM\nMore CPU\nBetter machine\n```\n\ninstead of:\n\n```text\nHorizontal Scaling\n↑\nMore servers\n```\n\nThough modern SQL systems improved a lot here.\n\n---\n\n## 3. Less flexible for rapidly changing data\n\nNot ideal when data structure changes frequently.\n\n---\n\n---\n\n# 2. NoSQL Databases\n\nNoSQL means:\n\n# \"Not Only SQL\"\n\nIt does NOT mean:\n\n```text\n\"No SQL\"\n```\n\nMany NoSQL databases still support query languages.\n\n---\n\n# Main Idea\n\nNoSQL databases prioritize:\n\n* flexibility\n* scalability\n* speed\n* distributed systems\n\nover strict relational structure.\n\n---\n\n# Types of NoSQL Databases\n\nThere are 4 major categories.\n\n---\n\n# A) Document Databases\n\nStore data as:\n\n* JSON\n* BSON\n* documents\n\nExample document:\n\n```json\n{\n  \"name\": \"Skyy\",\n  \"age\": 29,\n  \"skills\": [\"React\", \"Go\", \"Node.js\"]\n}\n```\n\nPopular examples:\n\n* MongoDB\n* CouchDB\n\n---\n\n# B) Key-Value Databases\n\nStore:\n\n```text\nkey → value\n```\n\nExample:\n\n```text\n\"user:1\" → \"{name:'Skyy'}\"\n```\n\nVery fast.\n\nPopular examples:\n\n* Redis\n* DynamoDB\n\n---\n\n# C) Column-Family Databases\n\nOptimized for huge distributed data.\n\nExamples:\n\n* Apache Cassandra\n* HBase\n\nUsed in:\n\n* big data\n* analytics\n* distributed systems\n\n---\n\n# D) Graph Databases\n\nDesigned for relationship-heavy graph data.\n\nExamples:\n\n* social networks\n* recommendation engines\n* fraud detection\n\nPopular examples:\n\n* Neo4j\n\n---\n\n# Core Features of NoSQL Databases\n\n---\n\n# A) Flexible Schema\n\nHuge advantage.\n\nDocuments can differ.\n\nExample:\n\nDocument 1:\n\n```json\n{\n  \"name\": \"Skyy\"\n}\n```\n\nDocument 2:\n\n```json\n{\n  \"name\": \"Alex\",\n  \"skills\": [\"Go\", \"Rust\"]\n}\n```\n\nNo migration required.\n\n---\n\n# B) Horizontal Scaling\n\nNoSQL databases are usually designed for:\n\n# Distributed Systems\n\nEasy to spread across many machines.\n\nExample:\n\n```text\nServer 1\nServer 2\nServer 3\n```\n\nThis is called:\n\n# Sharding\n\n---\n\n# C) High Performance\n\nMany NoSQL databases optimize for:\n\n* fast writes\n* massive scale\n* caching\n* real-time systems\n\n---\n\n# Advantages of NoSQL Databases\n\n---\n\n## 1. Flexible structure\n\nExcellent for rapidly changing applications.\n\n---\n\n## 2. Easy scaling\n\nPerfect for internet-scale systems.\n\n---\n\n## 3. Fast for certain workloads\n\nEspecially:\n\n* caching\n* logging\n* realtime analytics\n* event streams\n\n---\n\n## 4. Great for unstructured data\n\nLike:\n\n* JSON\n* social media\n* IoT\n* sensor data\n\n---\n\n# Disadvantages of NoSQL Databases\n\n---\n\n## 1. Weaker consistency (sometimes)\n\nMany NoSQL systems prefer:\n\n# BASE\n\ninstead of ACID.\n\n| Letter | Meaning              |\n| ------ | -------------------- |\n| B      | Basically Available  |\n| A      | Soft State           |\n| S      | Eventual Consistency |\n\nMeaning:\n\ndata may become consistent later.\n\n---\n\n## 2. Complex relationships\n\nJoins are often weak or absent.\n\nYou usually duplicate data instead.\n\n---\n\n## 3. Less standardized\n\nEach NoSQL database behaves differently.\n\nUnlike SQL:\n\n```sql\nSELECT * FROM users;\n```\n\nwhich works similarly everywhere.\n\n---\n\n# SQL vs NoSQL — Side by Side\n\n| Feature        | SQL                        | NoSQL                          |\n| -------------- | -------------------------- | ------------------------------ |\n| Structure      | Tables                     | Documents/Key-Value/etc        |\n| Schema         | Fixed                      | Flexible                       |\n| Relationships  | Strong                     | Usually weaker                 |\n| Scaling        | Vertical                   | Horizontal                     |\n| Transactions   | Strong ACID                | Often eventual consistency     |\n| Query Language | Standard SQL               | Database-specific              |\n| Best For       | Structured relational data | Massive scalable flexible data |\n| Examples       | PostgreSQL, MySQL          | MongoDB, Redis                 |\n\n---\n\n# Real World Examples\n\n---\n\n# When SQL is Better\n\n## Banking App\n\nNeed:\n\n* precise transactions\n* consistency\n* rollback\n* integrity\n\nSQL wins.\n\n---\n\n## Ecommerce Orders\n\nProducts, customers, payments, invoices all relate together.\n\nSQL is usually best.\n\n---\n\n## Analytics Dashboards\n\nComplex aggregations:\n\n```sql\nGROUP BY\nSUM\nAVG\nWINDOW FUNCTIONS\n```\n\nSQL dominates here.\n\n---\n\n# When NoSQL is Better\n\n---\n\n## Social Media Feed\n\nHuge scale.\n\nFlexible content.\n\nMillions of writes.\n\nNoSQL often fits better.\n\n---\n\n## Realtime Chat App\n\nMessages arrive extremely fast.\n\nDistributed scaling matters.\n\n---\n\n## Caching Layer\n\nUsing:\n\n* Redis\n\nfor ultra-fast reads.\n\n---\n\n# CAP Theorem (Very Important)\n\nDistributed systems usually discuss:\n\n# CAP Theorem\n\nA distributed database can only strongly guarantee 2 of 3:\n\n| Letter | Meaning             |\n| ------ | ------------------- |\n| C      | Consistency         |\n| A      | Availability        |\n| P      | Partition Tolerance |\n\nModern NoSQL systems often prioritize:\n\n```text\nAvailability + Partition Tolerance\n```\n\nwhile many SQL systems prioritize:\n\n```text\nConsistency + Reliability\n```\n\n---\n\n# Modern Reality: The Line is Blurring\n\nToday:\n\n## SQL databases added:\n\n* JSON support\n* horizontal scaling\n* replication\n\nEspecially:\n\n* PostgreSQL\n\n---\n\n## NoSQL databases added:\n\n* transactions\n* indexing\n* query languages\n\nEspecially:\n\n* MongoDB\n\nSo modern systems are becoming hybrids.\n\n---\n\n# Which One Should We Learn?\n\nFor backend engineering:\n\n# Learn SQL FIRST.\n\nEspecially:\n\n* PostgreSQL\n\nWhy?\n\nBecause SQL teaches:\n\n* data modeling\n* normalization\n* joins\n* indexing\n* transactions\n* query optimization\n\nThese concepts make us better backend engineers overall.\n\nAfter that:\n\nlearn NoSQL systems like:\n\n* MongoDB\n* Redis\n\nbecause real-world systems often use both.\n\nExample architecture:\n\n```text\nPostgreSQL  → main database\nRedis       → caching\nMongoDB     → flexible document storage\n```\n\n---\n\n# Practical Industry Truth\n\nMost production systems today are NOT:\n\n```text\nSQL OR NoSQL\n```\n\nThey are:\n\n# SQL + NoSQL together\n\nbecause each solves different problems.\n\n---\n\n# Simple Mental Model\n\n## SQL\n\nThink:\n\n```text\nStructure\nRelationships\nConsistency\nCorrectness\n```\n\n---\n\n## NoSQL\n\nThink:\n\n```text\nFlexibility\nScale\nSpeed\nDistributed systems\n```\n\n# What is PostgreSQL?\n\nPostgreSQL (often called **Postgres**) is an:\n\n# Open-source Relational Database Management System (RDBMS)\n\nIt is one of the most respected and widely used databases in the world.\n\nBig companies use it for:\n\n* banking systems\n* ecommerce platforms\n* SaaS products\n* fintech\n* analytics\n* government systems\n* AI platforms\n* enterprise applications\n\nbecause it is:\n\n```text\nReliable\nPowerful\nExtensible\nStandards-compliant\nProduction-grade\n```\n\n---\n\n# The Core Purpose of PostgreSQL\n\nAt its heart, PostgreSQL solves this problem:\n\n# \"How do we safely store, organize, retrieve, and protect massive amounts of important data?\"\n\nExample:\n\nImagine building:\n\n* Amazon\n* Instagram\n* Uber\n* Banking software\n* Hospital systems\n\nYou need to store:\n\n* users\n* payments\n* orders\n* messages\n* logs\n* transactions\n* analytics\n\nand you need guarantees that:\n\n* data won't corrupt\n* crashes won't destroy data\n* multiple users won't overwrite each other\n* queries remain fast\n* relationships remain valid\n\nThat is exactly what PostgreSQL is designed to solve.\n\n---\n\n# Why Not Just Use Files?\n\nWithout databases, we'd store data in:\n\n```text\nJSON files\nTXT files\nCSV files\nExcel sheets\n```\n\nBut that becomes a disaster at scale.\n\n---\n\n# Problems With File-Based Storage\n\n## 1. No Concurrency\n\nIf 1000 users update the same file:\n\n```text\nData corruption happens\n```\n\n---\n\n## 2. Slow Searching\n\nFinding data becomes extremely inefficient.\n\nExample:\n\n```text\nFind all users from Kolkata\n```\n\nIn files:\n\n```text\nScan entire file manually\n```\n\nIn PostgreSQL:\n\n```sql id=\"m4u9xm\"\nSELECT * FROM users WHERE city='Kolkata';\n```\n\nOptimized using indexes.\n\n---\n\n## 3. No Relationships\n\nFiles don't naturally handle:\n\n* users ↔ orders\n* students ↔ courses\n* doctors ↔ appointments\n\nPostgreSQL does.\n\n---\n\n## 4. No Transactions\n\nCritical systems need:\n\n# \"All-or-nothing operations\"\n\nExample:\n\nBank transfer:\n\n```text\nDeduct ₹5000 from A\nAdd ₹5000 to B\n```\n\nIf power fails midway:\n\n```text\nMoney disappears\n```\n\nPostgreSQL prevents this using ACID transactions.\n\n---\n\n# Why PostgreSQL Became So Popular\n\nMany databases exist.\n\nExamples:\n\n* MySQL\n* SQLite\n* MongoDB\n* Oracle Database\n\nBut PostgreSQL has a unique reputation.\n\n---\n\n# PostgreSQL's Philosophy\n\nPostgreSQL prioritizes:\n\n```text\nCorrectness\nStandards\nReliability\nData integrity\nAdvanced features\n```\n\nover shortcuts.\n\nThat is why engineers trust it deeply.\n\n---\n\n# Why Companies Prefer PostgreSQL\n\n---\n\n# 1. Extremely Reliable\n\nPostgreSQL is famous for:\n\n# Data Integrity\n\nMeaning:\n\n```text\nYour data stays correct.\n```\n\nThis matters massively in:\n\n* finance\n* banking\n* healthcare\n* ecommerce\n* government\n\nCompanies cannot afford silent corruption.\n\n---\n\n# 2. ACID Transactions\n\nPostgreSQL has world-class transaction support.\n\n# ACID\n\n| Letter | Meaning     |\n| ------ | ----------- |\n| A      | Atomicity   |\n| C      | Consistency |\n| I      | Isolation   |\n| D      | Durability  |\n\n---\n\n## Example\n\nSuppose:\n\n```text\nUser buys a product\n```\n\nDatabase operations:\n\n```text\n1. Deduct inventory\n2. Charge payment\n3. Create order\n4. Generate invoice\n```\n\nIf step 3 fails:\n\nPostgreSQL can rollback everything safely.\n\nWithout transactions:\n\n```text\nInventory may reduce\nbut order may not exist\n```\n\nHuge disaster.\n\n---\n\n# 3. Powerful Query Engine\n\nPostgreSQL is incredibly powerful for querying data.\n\nExample capabilities:\n\n```sql id=\"b1e3qv\"\nJOIN\nGROUP BY\nWINDOW FUNCTIONS\nCTEs\nSUBQUERIES\nPARTITIONING\nJSON Queries\nFULL TEXT SEARCH\n```\n\nThis makes it useful for:\n\n* analytics\n* dashboards\n* reporting\n* business intelligence\n\n---\n\n# 4. Advanced SQL Compliance\n\nPostgreSQL follows SQL standards more strictly than many competitors.\n\nThis matters because:\n\n* cleaner architecture\n* portability\n* predictable behavior\n* enterprise trust\n\n---\n\n# 5. Extensible Architecture\n\nThis is one of PostgreSQL's superpowers.\n\nYou can extend it heavily.\n\nExample:\n\n* custom data types\n* custom operators\n* extensions\n* procedural languages\n\nPopular extensions:\n\n| Extension   | Purpose         |\n| ----------- | --------------- |\n| PostGIS     | GIS/geolocation |\n| pgvector    | AI embeddings   |\n| TimescaleDB | Time-series     |\n| uuid-ossp   | UUID generation |\n\n---\n\n# PostgreSQL + AI Boom\n\nRecently PostgreSQL became extremely popular in AI systems because of:\n\n# pgvector\n\nThis extension allows PostgreSQL to store:\n\n* vector embeddings\n* semantic search\n* AI similarity search\n\nMeaning PostgreSQL can now behave partially like a vector database.\n\nHuge reason companies love it now.\n\n---\n\n# 6. JSON Support (Hybrid SQL + NoSQL)\n\nModern apps often use JSON heavily.\n\nPostgreSQL supports:\n\n# JSON and JSONB\n\nExample:\n\n```json id=\"lhm2hp\"\n{\n  \"skills\": [\"Go\", \"React\"],\n  \"socials\": {\n    \"github\": \"skyy\"\n  }\n}\n```\n\nStored directly inside PostgreSQL.\n\nThis gives:\n\n```text\nSQL + NoSQL hybrid power\n```\n\nThis is massive.\n\n---\n\n# 7. Open Source\n\nPostgreSQL is:\n\n# Completely free\n\nNo expensive licensing like:\n\n* Oracle Database\n\nCompanies save enormous money.\n\nYet PostgreSQL still delivers enterprise-grade quality.\n\n---\n\n# 8. Strong Community\n\nPostgreSQL has one of the best engineering communities in databases.\n\nBenefits:\n\n* stability\n* documentation\n* ecosystem\n* tooling\n* security updates\n\n---\n\n# 9. Great Scalability\n\nPostgreSQL scales surprisingly well.\n\nSupports:\n\n* replication\n* partitioning\n* indexing\n* read replicas\n* connection pooling\n\nLarge companies run massive workloads on it.\n\n---\n\n# What Problems PostgreSQL Solves\n\n---\n\n# Problem 1 — Data Organization\n\nInstead of messy files:\n\n```text\nusers.json\norders.json\npayments.json\n```\n\nPostgreSQL organizes data relationally.\n\n---\n\n# Problem 2 — Data Relationships\n\nExample:\n\n```text\nUser → Orders\nOrder → Products\nProduct → Reviews\n```\n\nHandled elegantly using relational modeling.\n\n---\n\n# Problem 3 — Safe Concurrent Access\n\nThousands of users can access the database simultaneously.\n\nPostgreSQL handles:\n\n* locks\n* MVCC\n* transactions\n* isolation\n\nsafely.\n\n---\n\n# Problem 4 — Data Integrity\n\nConstraints enforce correctness.\n\nExample:\n\n```sql id=\"7wz6dq\"\nemail TEXT UNIQUE NOT NULL\n```\n\nPrevents duplicate emails.\n\n---\n\n# Problem 5 — Query Performance\n\nIndexes make queries fast.\n\nWithout indexes:\n\n```text\nO(n) full scans\n```\n\nWith indexes:\n\n```text\nNear O(log n)\n```\n\nHuge performance gains.\n\n---\n\n# Problem 6 — Crash Recovery\n\nIf the server crashes:\n\nPostgreSQL uses:\n\n# WAL (Write Ahead Logging)\n\nto recover safely.\n\nThis is a massive engineering feature.\n\n---\n\n# MVCC — One of PostgreSQL's Biggest Strengths\n\n# Multi-Version Concurrency Control\n\nThis is one reason PostgreSQL feels so smooth under concurrency.\n\nInstead of locking entire tables aggressively:\n\nPostgreSQL creates multiple versions of rows.\n\nBenefits:\n\n* readers don't block writers\n* writers don't block readers much\n* high concurrency\n* better scalability\n\nThis is extremely important in real-world systems.\n\n---\n\n# PostgreSQL vs MySQL\n\nThis is a famous comparison.\n\n---\n\n# MySQL\n\nTraditionally known for:\n\n```text\nSimplicity\nSpeed\nEase of use\n```\n\n---\n\n# PostgreSQL\n\nKnown for:\n\n```text\nCorrectness\nAdvanced features\nComplex queries\nStandards compliance\n```\n\n---\n\n# Many engineers say:\n\n## MySQL is easier initially.\n\n## PostgreSQL grows with complexity better.\n\n---\n\n# Why Modern Startups Love PostgreSQL\n\nBecause it can do MANY things at once:\n\n---\n\n## Relational Database\n\nTraditional SQL.\n\n---\n\n## JSON Store\n\nActs partially like NoSQL.\n\n---\n\n## Full Text Search\n\nSearch engine features.\n\n---\n\n## Vector Database\n\nAI embeddings.\n\n---\n\n## GIS Database\n\nUsing PostGIS.\n\n---\n\n## Time-Series Database\n\nUsing TimescaleDB.\n\n---\n\n# So PostgreSQL became:\n\n# \"The Swiss Army Knife of Databases\"\n\n---\n\n# Important PostgreSQL Concepts\n\n---\n\n# 1. Tables\n\nStore structured data.\n\n---\n\n# 2. Rows\n\nSingle records.\n\n---\n\n# 3. Columns\n\nFields/data attributes.\n\n---\n\n# 4. Primary Keys\n\nUnique row identifiers.\n\nExample:\n\n```sql id=\"v0qq8k\"\nid SERIAL PRIMARY KEY\n```\n\n---\n\n# 5. Foreign Keys\n\nRelationships between tables.\n\n---\n\n# 6. Indexes\n\nSpeed up searching.\n\n---\n\n# 7. Transactions\n\nSafe grouped operations.\n\n---\n\n# 8. WAL\n\nCrash recovery system.\n\n---\n\n# 9. MVCC\n\nConcurrency model.\n\n---\n\n# 10. Schemas\n\nLogical organization inside databases.\n\n---\n\n# 11. Views\n\nVirtual tables based on queries.\n\n---\n\n# 12. Materialized Views\n\nCached query results.\n\n---\n\n# 13. Replication\n\nCopy database data across servers.\n\n---\n\n# 14. Partitioning\n\nSplit huge tables into smaller chunks.\n\n---\n\n# 15. Extensions\n\nAdd extra functionality.\n\n---\n\n# Real-World Example\n\nImagine building your MERN ecommerce app.\n\nYou need:\n\n* users\n* carts\n* orders\n* inventory\n* payments\n* reviews\n\nThis data is highly relational.\n\nPostgreSQL handles this beautifully.\n\nExample:\n\n```text\nusers\n  ↓\norders\n  ↓\norder_items\n  ↓\nproducts\n```\n\nThis is where relational databases dominate.\n\n---\n\n# Why Backend Engineers Should Learn PostgreSQL\n\nBecause PostgreSQL teaches:\n\n* real database design\n* normalization\n* indexing\n* query optimization\n* transactions\n* concurrency\n* scalability\n* data modeling\n\nThese are core backend engineering skills.\n\n---\n\n# Industry Reality\n\nMany modern companies use:\n\n```text\nPostgreSQL as the primary database\nRedis for caching\nKafka for events\nElasticsearch for search\n```\n\nPostgreSQL often becomes the system of record.\n\nMeaning:\n\n# \"The source of truth\"\n\n---\n\n# Final Mental Model\n\nThink of PostgreSQL as:\n\n# A highly reliable engine for structured data systems\n\noptimized for:\n\n```text\nCorrectness\nRelationships\nSafety\nComplex querying\nConcurrency\nScalability\nExtensibility\n```\n\nThat combination is why PostgreSQL is respected so heavily across the software industry.\n\n# CRUD in PostgreSQL\n\nCRUD is the foundation of almost all backend/database applications.\n\n| Letter | Meaning | SQL Command |\n| ------ | ------- | ----------- |\n| C      | Create  | `INSERT`    |\n| R      | Read    | `SELECT`    |\n| U      | Update  | `UPDATE`    |\n| D      | Delete  | `DELETE`    |\n\nEvery major application does these constantly:\n\n* ecommerce\n* banking\n* social media\n* hospital systems\n* chat apps\n* inventory systems\n\n---\n\n# First Create a Table\n\nWe’ll use this throughout.\n\n```sql id=\"zwwg7m\"\nCREATE TABLE users(\n    id SERIAL PRIMARY KEY,\n    name VARCHAR(100) NOT NULL,\n    email VARCHAR(255) UNIQUE NOT NULL,\n    age INTEGER,\n    is_active BOOLEAN DEFAULT true,\n    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n);\n```\n\n---\n\n# Understanding This Table\n\n| Column       | Purpose              |\n| ------------ | -------------------- |\n| `id`         | unique user ID       |\n| `name`       | user name            |\n| `email`      | unique email         |\n| `age`        | user age             |\n| `is_active`  | active/inactive user |\n| `created_at` | creation timestamp   |\n\n---\n\n# CREATE → `INSERT`\n\nUsed to add data into a table.\n\n---\n\n# Insert One Row\n\n```sql id=\"4n8k2v\"\nINSERT INTO users(name, email, age)\nVALUES(\n    'Skyy',\n    'skyy@gmail.com',\n    29\n);\n```\n\n---\n\n# Breakdown\n\n## `INSERT INTO`\n\nMeans:\n\n\u003e add data into table\n\n---\n\n## `users`\n\nTarget table.\n\n---\n\n## `(name, email, age)`\n\nColumns receiving data.\n\n---\n\n## `VALUES`\n\nActual row data.\n\n---\n\n# Result\n\nA new row gets created:\n\n| id | name | email                                   | age |\n| -- | ---- | --------------------------------------- | --- |\n| 1  | Skyy | [skyy@gmail.com](mailto:skyy@gmail.com) | 29  |\n\n---\n\n# Insert Multiple Rows\n\n```sql id=\"k1gnlm\"\nINSERT INTO users(name, email, age)\nVALUES\n('John', 'john@gmail.com', 25),\n('Alice', 'alice@gmail.com', 31),\n('Bob', 'bob@gmail.com', 22);\n```\n\nVery common for:\n\n* seed data\n* testing\n* bulk inserts\n\n---\n\n# RETURNING\n\nPostgreSQL-specific powerful feature.\n\n```sql id=\"wzjlwm\"\nINSERT INTO users(name, email, age)\nVALUES(\n    'Mike',\n    'mike@gmail.com',\n    40\n)\nRETURNING *;\n```\n\nReturns inserted row immediately.\n\nExtremely useful in backend APIs.\n\n---\n\n# READ → `SELECT`\n\nUsed to retrieve data.\n\nMost used SQL command by far.\n\n---\n\n# Select Everything\n\n```sql id=\"fjlwm4\"\nSELECT * FROM users;\n```\n\n---\n\n# `*`\n\nMeans:\n\n```txt id=\"eqqjlwm\"\nall columns\n```\n\n---\n\n# Result\n\n| id | name | email | age |\n| -- | ---- | ----- | --- |\n\n---\n\n# Select Specific Columns\n\n```sql id=\"jlwm1z\"\nSELECT name, email\nFROM users;\n```\n\nReturns only requested columns.\n\n---\n\n# WHERE Clause\n\nFilters rows.\n\n---\n\n# Example\n\n```sql id=\"jlwm2z\"\nSELECT *\nFROM users\nWHERE age \u003e 25;\n```\n\n---\n\n# Comparison Operators\n\n| Operator | Meaning       |\n| -------- | ------------- |\n| `=`      | equal         |\n| `!=`     | not equal     |\n| `\u003e`      | greater than  |\n| `\u003c`      | less than     |\n| `\u003e=`     | greater/equal |\n| `\u003c=`     | less/equal    |\n\n---\n\n# Boolean Filtering\n\n```sql id=\"jlwm3z\"\nSELECT *\nFROM users\nWHERE is_active = true;\n```\n\nShortcut:\n\n```sql id=\"jlwm4z\"\nWHERE is_active;\n```\n\nBecause boolean already evaluates true/false.\n\n---\n\n# AND / OR\n\n```sql id=\"jlwm5z\"\nSELECT *\nFROM users\nWHERE age \u003e 20\nAND is_active = true;\n```\n\n---\n\n# ORDER BY\n\nSorting results.\n\n```sql id=\"jlwm6z\"\nSELECT *\nFROM users\nORDER BY age DESC;\n```\n\n---\n\n# ASC vs DESC\n\n| Keyword | Meaning    |\n| ------- | ---------- |\n| `ASC`   | ascending  |\n| `DESC`  | descending |\n\n---\n\n# LIMIT\n\nRestrict number of rows.\n\n```sql id=\"jlwm7z\"\nSELECT *\nFROM users\nLIMIT 5;\n```\n\nVery common in:\n\n* pagination\n* APIs\n* dashboards\n\n---\n\n# UPDATE → `UPDATE`\n\nModify existing rows.\n\n---\n\n# Update Single User\n\n```sql id=\"jlwm8z\"\nUPDATE users\nSET age = 30\nWHERE id = 1;\n```\n\n---\n\n# Breakdown\n\n| Part           | Meaning              |\n| -------------- | -------------------- |\n| `UPDATE users` | target table         |\n| `SET`          | new values           |\n| `WHERE`        | which rows to update |\n\n---\n\n# CRITICAL WARNING\n\nWithout `WHERE`:\n\n```sql id=\"jlwm9z\"\nUPDATE users\nSET age = 30;\n```\n\nEVERY row gets updated.\n\nClassic beginner mistake.\n\n---\n\n# Update Multiple Columns\n\n```sql id=\"jlwmaz\"\nUPDATE users\nSET\n    age = 35,\n    is_active = false\nWHERE id = 2;\n```\n\n---\n\n# RETURNING with UPDATE\n\n```sql id=\"j0ht8x\"\nUPDATE users\nSET age = 50\nWHERE id = 1\nRETURNING *;\n```\n\nVery useful.\n\n---\n\n# DELETE → `DELETE`\n\nRemoves rows.\n\n---\n\n# Delete One Row\n\n```sql id=\"jlwmbz\"\nDELETE FROM users\nWHERE id = 1;\n```\n\n---\n\n# CRITICAL WARNING\n\nWithout WHERE:\n\n```sql id=\"jlwmcz\"\nDELETE FROM users;\n```\n\nALL rows deleted.\n\n---\n\n# Difference Between DELETE \u0026 DROP\n\nHuge distinction.\n\n---\n\n# DELETE\n\n```sql id=\"jlwmdz\"\nDELETE FROM users;\n```\n\nRemoves:\n\n* rows only\n\nTable still exists.\n\n---\n\n# DROP\n\n```sql id=\"jlwmez\"\nDROP TABLE users;\n```\n\nRemoves:\n\n* table itself\n* structure\n* data\n* constraints\n* indexes\n\nCompletely gone.\n\n---\n\n# TRUNCATE\n\nFast delete-all operation.\n\n```sql id=\"jlwmfz\"\nTRUNCATE TABLE users;\n```\n\nRemoves all rows quickly.\n\nOften faster than DELETE.\n\n---\n\n# CRUD Flow Example\n\n---\n\n# Create User\n\n```sql id=\"jlwmgz\"\nINSERT INTO users(name, email, age)\nVALUES('Skyy', 'skyy@gmail.com', 29);\n```\n\n---\n\n# Read User\n\n```sql id=\"ժմlwq1\"\nSELECT *\nFROM users\nWHERE email = 'skyy@gmail.com';\n```\n\n---\n\n# Update User\n\n```sql id=\"jlwmhz\"\nUPDATE users\nSET age = 30\nWHERE email = 'skyy@gmail.com';\n```\n\n---\n\n# Delete User\n\n```sql id=\"jlwmiz\"\nDELETE FROM users\nWHERE email = 'skyy@gmail.com';\n```\n\n---\n\n# Real Backend Mapping\n\n| API               | SQL    |\n| ----------------- | ------ |\n| POST `/users`     | INSERT |\n| GET `/users`      | SELECT |\n| PATCH `/users/1`  | UPDATE |\n| DELETE `/users/1` | DELETE |\n\nThis is why CRUD is fundamental backend knowledge.\n\n---\n\n# Most Important Beginner Mistakes\n\n---\n\n# 1. Forgetting WHERE\n\nDangerous in:\n\n* UPDATE\n* DELETE\n\n---\n\n# 2. Wrong Data Types\n\nExample:\n\n```sql id=\"jlwmjz\"\nage = 'hello'\n```\n\ninvalid for INTEGER.\n\n---\n\n# 3. Inserting NULL into NOT NULL\n\nExample:\n\n```sql id=\"jlwmkz\"\nname VARCHAR(100) NOT NULL\n```\n\nCannot insert NULL.\n\n---\n\n# 4. Duplicate UNIQUE Values\n\nExample:\n\n```sql id=\"jlwmlz\"\nemail VARCHAR(255) UNIQUE\n```\n\nCannot reuse same email.\n\n---\n\n# PostgreSQL-Specific Powerful Features\n\nPostgreSQL CRUD becomes extremely powerful because of:\n\n* `RETURNING`\n* JSON support\n* CTEs\n* UPSERTS\n* Transactions\n* Window functions\n\nYou’ll eventually use those heavily in production apps.\n\n---\n\n# Most Important Commands Cheat Sheet\n\n---\n\n# CREATE\n\n```sql id=\"wletd3\"\nINSERT INTO table(columns)\nVALUES(values);\n```\n\n---\n\n# READ\n\n```sql id=\"jlwmmz\"\nSELECT * FROM table;\n```\n\n---\n\n# FILTER\n\n```sql id=\"jlwmnz\"\nWHERE condition\n```\n\n---\n\n# UPDATE\n\n```sql id=\"jwjlwm0\"\nUPDATE table\nSET column = value\nWHERE condition;\n```\n\n---\n\n# DELETE\n\n```sql id=\"jlwmoz\"\nDELETE FROM table\nWHERE condition;\n```\n\n---\n\n# SAFETY RULE\n\nAlways mentally check:\n\n```txt id=\"jlwmpz\"\nDo I REALLY want this affecting ALL rows?\n```\n\nbefore running:\n\n* UPDATE\n* DELETE\n\nThat habit saves developers from catastrophic production mistakes.\n\nThis is actually a very good introduction to some of PostgreSQL’s strongest features:\n\n* UUIDs\n* JSONB\n* JSON operators\n* dynamic event storage\n* semi-structured data\n\nThese are things companies heavily use in real systems.\n\n---\n\n# Full Query\n\n```sql id=\"mjlwm1\"\nDROP TABLE IF EXISTS basics.app_events;\n\nCREATE TABLE basics.app_events(\n    -- UUID --\n    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),\n\n    event_name TEXT NOT NULL,\n\n    -- JSONB --\n    metadata JSONB DEFAULT '{}'::jsonb,\n\n    created_at TIMESTAMP DEFAULT NOW()\n);\n\nINSERT INTO basics.app_events(event_name,metadata)\nVALUES\n('sign-up','{\"browser\":\"chrome\"}'),\n('sign-in','{\"user\":\"skyy\"}');\n\nSELECT * FROM basics.app_events;\n\nSELECT\n    event_name,\n    metadata -\u003e\u003e 'browser' AS browser\nFROM basics.app_events\nWHERE metadata ? 'browser';\n```\n\n---\n\n# High-Level Goal of This Table\n\nThis table stores application events/logs.\n\nExamples:\n\n* user signups\n* user logins\n* payments\n* clicks\n* analytics\n* API events\n\nThis is VERY common in:\n\n* SaaS apps\n* monitoring systems\n* analytics pipelines\n* audit logs\n\n---\n\n# 1. `DROP TABLE IF EXISTS`\n\n```sql id=\"8jlwm2\"\nDROP TABLE IF EXISTS basics.app_events;\n```\n\n---\n\n# Meaning\n\nDelete table if it already exists.\n\n---\n\n# Why use this?\n\nDuring development:\n\n* rerun scripts safely\n* avoid “table already exists” errors\n\n---\n\n# Without `IF EXISTS`\n\nThis:\n\n```sql id=\"8jlwm3\"\nDROP TABLE basics.app_events;\n```\n\nwould fail if table doesn’t exist.\n\n---\n\n# 2. `CREATE TABLE`\n\n```sql id=\"8jlwm4\"\nCREATE TABLE basics.app_events(\n```\n\nCreates table:\n\n* inside schema `basics`\n* named `app_events`\n\n---\n\n# PostgreSQL Hierarchy Reminder\n\n```txt id=\"8jlwm5\"\ndatabase\n   └── schema\n           └── table\n```\n\nSo:\n\n```sql id=\"8jlwm6\"\nbasics.app_events\n```\n\nmeans:\n\n| Part         | Meaning |\n| ------------ | ------- |\n| `basics`     | schema  |\n| `app_events` | table   |\n\n---\n\n# 3. UUID Column\n\n```sql id=\"8jlwm7\"\nid UUID PRIMARY KEY DEFAULT gen_random_uuid(),\n```\n\nThis is extremely important.\n\n---\n\n# What is UUID?\n\nUUID =\nUniversal Unique Identifier\n\nExample:\n\n```txt id=\"8jlwm8\"\n550e8400-e29b-41d4-a716-446655440000\n```\n\n---\n\n# Why UUID exists\n\nInstead of numeric IDs:\n\n```txt id=\"8jlwm9\"\n1\n2\n3\n4\n```\n\nUUIDs generate globally unique identifiers.\n\n---\n\n# Why companies use UUIDs\n\n---\n\n# Problem with Sequential IDs\n\nSuppose API returns:\n\n```txt id=\"8jlwm10\"\n/users/1\n/users/2\n/users/3\n```\n\nAttackers can guess IDs easily.\n\n---\n\n# UUID Solves This\n\n```txt id=\"8jlwm11\"\n/users/a12f8d91-4d...\n```\n\nHard to guess.\n\nBetter for:\n\n* security\n* distributed systems\n* microservices\n* merging databases\n\n---\n\n# `PRIMARY KEY`\n\n```sql id=\"8jlwm12\"\nPRIMARY KEY\n```\n\nMeans:\n\n* unique\n* indexed\n* identifies each row\n\nNo duplicates allowed.\n\n---\n\n# `DEFAULT gen_random_uuid()`\n\n```sql id=\"8jlwm13\"\nDEFAULT gen_random_uuid()\n```\n\nAutomatically generates UUID when inserting rows.\n\nSo we don't manually provide IDs.\n\n---\n\n# Example Generated UUID\n\n```txt id=\"8jlwm14\"\n3c7f5d78-8d0c-44b5-b7a9-4c5a12c7f908\n```\n\n---\n\n# Important\n\n`gen_random_uuid()` comes from PostgreSQL extension:\n\n```sql id=\"8jlwm15\"\nCREATE EXTENSION IF NOT EXISTS pgcrypto;\n```\n\nYou may need this first.\n\n---\n\n# 4. `TEXT`\n\n```sql id=\"8jlwm16\"\nevent_name TEXT NOT NULL,\n```\n\nStores variable-length text.\n\nExamples:\n\n* sign-up\n* sign-in\n* payment-success\n\n---\n\n# `NOT NULL`\n\nMeans:\n\n* value required\n* cannot be empty NULL\n\n---\n\n# 5. JSONB\n\nThis is the BIG PostgreSQL feature.\n\n```sql id=\"8jlwm17\"\nmetadata JSONB DEFAULT '{}'::jsonb,\n```\n\n---\n\n# What is JSONB?\n\nBinary JSON storage format.\n\nAllows PostgreSQL to store JSON efficiently.\n\n---\n\n# Example JSON\n\n```json id=\"8jlwm18\"\n{\n  \"browser\": \"chrome\",\n  \"country\": \"India\"\n}\n```\n\n---\n\n# Why JSONB is powerful\n\nTraditional SQL databases are rigid.\n\nNormally every field needs a column:\n\n| id | browser | country | ip |\n\nBut event systems are dynamic.\n\nDifferent events contain different data.\n\n---\n\n# Example\n\nSignup event:\n\n```json id=\"8jlwm19\"\n{\n  \"browser\":\"chrome\"\n}\n```\n\nPayment event:\n\n```json id=\"8jlwm20\"\n{\n  \"amount\":500,\n  \"currency\":\"USD\"\n}\n```\n\nLogin event:\n\n```json id=\"8jlwm21\"\n{\n  \"ip\":\"1.2.3.4\"\n}\n```\n\nJSONB lets us store flexible structures.\n\n---\n\n# Why PostgreSQL is loved\n\nBecause it combines:\n\n| SQL Structure | NoSQL Flexibility |\n| ------------- | ----------------- |\n| tables        | JSONB             |\n| constraints   | nested JSON       |\n| joins         | document storage  |\n\nIt’s like:\n\n* relational DB\n* partial document DB\n\nat the same time.\n\n---\n\n# `DEFAULT '{}'::jsonb`\n\n```sql id=\"8jlwm22\"\nDEFAULT '{}'::jsonb\n```\n\n---\n\n# `{}`\n\nEmpty JSON object.\n\nEquivalent to:\n\n```json id=\"8jlwm23\"\n{}\n```\n\n---\n\n# `::jsonb`\n\nType casting.\n\nMeans:\n\n\u003e convert this into JSONB type\n\n---\n\n# PostgreSQL Type Casting\n\n```sql id=\"8jlwm24\"\n'value'::datatype\n```\n\nExamples:\n\n```sql id=\"8jlwm25\"\n'123'::integer\n'true'::boolean\n'{}'::jsonb\n```\n\n---\n\n# 6. Timestamp\n\n```sql id=\"8jlwm26\"\ncreated_at TIMESTAMP DEFAULT NOW()\n```\n\n---\n\n# TIMESTAMP\n\nStores:\n\n* date\n* time\n\nExample:\n\n```txt id=\"8jlwm27\"\n2026-05-15 18:22:01\n```\n\n---\n\n# `NOW()`\n\nPostgreSQL function returning current timestamp.\n\nAutomatically fills creation time.\n\n---\n\n# 7. INSERT\n\n```sql id=\"8jlwm28\"\nINSERT INTO basics.app_events(event_name,metadata)\nVALUES\n('sign-up','{\"browser\":\"chrome\"}'),\n('sign-in','{\"user\":\"skyy\"}');\n```\n\n---\n\n# What gets inserted\n\n---\n\n# Row 1\n\n```json id=\"8jlwm29\"\n{\n  \"event_name\":\"sign-up\",\n  \"metadata\":{\n    \"browser\":\"chrome\"\n  }\n}\n```\n\n---\n\n# Row 2\n\n```json id=\"8jlwm30\"\n{\n  \"event_name\":\"sign-in\",\n  \"metadata\":{\n    \"user\":\"skyy\"\n  }\n}\n```\n\n---\n\n# Notice\n\nDifferent rows have different JSON structure.\n\nVery powerful.\n\n---\n\n# 8. `SELECT *`\n\n```sql id=\"8jlwm31\"\nSELECT * FROM basics.app_events;\n```\n\nReturns all rows and columns.\n\n---\n\n# 9. JSON Operators\n\nThis is the advanced PostgreSQL magic.\n\n---\n\n# `-\u003e\u003e`\n\n```sql id=\"8jlwm32\"\nmetadata -\u003e\u003e 'browser'\n```\n\nMeans:\n\n\u003e extract JSON value as TEXT\n\n---\n\n# Example\n\nFrom:\n\n```json id=\"8jlwm33\"\n{\n  \"browser\":\"chrome\"\n}\n```\n\nit extracts:\n\n```txt id=\"8jlwm34\"\nchrome\n```\n\n---\n\n# Difference Between `-\u003e` and `-\u003e\u003e`\n\n---\n\n# `-\u003e`\n\nReturns JSON.\n\n```sql id=\"8jlwm35\"\nmetadata -\u003e 'browser'\n```\n\nreturns:\n\n```json id=\"8jlwm36\"\n\"chrome\"\n```\n\n(still JSON)\n\n---\n\n# `-\u003e\u003e`\n\nReturns plain text.\n\n```sql id=\"8jlwm37\"\nmetadata -\u003e\u003e 'browser'\n```\n\nreturns:\n\n```txt id=\"8jlwm38\"\nchrome\n```\n\n(text value)\n\n---\n\n# 10. `AS`\n\n```sql id=\"8jlwm39\"\nAS browser\n```\n\nCreates alias/temporary column name.\n\n---\n\n# Without AS\n\nColumn name becomes ugly:\n\n```txt id=\"8jlwm40\"\n?column?\n```\n\n---\n\n# With AS\n\nCleaner result:\n\n| browser |\n| ------- |\n\n---\n\n# 11. `WHERE metadata ? 'browser'`\n\nThis is another PostgreSQL JSONB operator.\n\n---\n\n# `?`\n\nMeans:\n\n\u003e does this JSON key exist?\n\n---\n\n# Example\n\nThis row:\n\n```json id=\"8jlwm41\"\n{\n  \"browser\":\"chrome\"\n}\n```\n\ncontains key:\n\n```txt id=\"8jlwm42\"\nbrowser\n```\n\nSo condition becomes TRUE.\n\n---\n\n# This row\n\n```json id=\"8jlwm43\"\n{\n  \"user\":\"skyy\"\n}\n```\n\ndoes NOT contain:\n\n* browser\n\nSo it gets filtered out.\n\n---\n\n# Final Query Meaning\n\n```sql id=\"8jlwm44\"\nSELECT\n    event_name,\n    metadata -\u003e\u003e 'browser' AS browser\nFROM basics.app_events\nWHERE metadata ? 'browser';\n```\n\nmeans:\n\n\u003e Find all events whose metadata contains `browser`, then extract browser value as text.\n\n---\n\n# Result\n\n| event_name | browser |\n| ---------- | ------- |\n| sign-up    | chrome  |\n\n---\n\n# Why JSONB Is Huge in Industry\n\nUsed heavily for:\n\n* event tracking\n* analytics\n* audit logs\n* flexible settings\n* API payloads\n* metadata systems\n* feature flags\n\nCompanies love PostgreSQL because JSONB gives:\n\n* relational DB power\n* NoSQL flexibility\n\nwithout switching databases.\n\n---\n\n# Important PostgreSQL JSONB Operators\n\n| Operator | Meaning          |\n| -------- | ---------------- |\n| `-\u003e`     | get JSON object  |\n| `-\u003e\u003e`    | get text value   |\n| `?`      | key exists       |\n| `@\u003e`     | contains JSON    |\n| `#\u003e`     | nested JSON path |\n\n---\n\n# Real Backend Example\n\nSuppose Node.js app tracks events:\n\n```json id=\"8jlwm45\"\n{\n  \"event\":\"purchase\",\n  \"metadata\":{\n    \"amount\":500,\n    \"currency\":\"USD\",\n    \"device\":\"mobile\"\n  }\n}\n```\n\nInstead of constantly changing schema, JSONB stores flexible event metadata cleanly.\n\nThat’s one reason PostgreSQL dominates modern backend systems.\n\n# LIMIT, OFFSET, and Pagination in PostgreSQL\n\nThese concepts are used to:\n\n# Control how much data we fetch from the database\n\nThis becomes extremely important in real-world applications because tables can contain:\n\n```text id=\"b8ng5f\"\nThousands\nMillions\nBillions\n```\n\nof rows.\n\nWe almost NEVER want:\n\n```sql id=\"w0w1di\"\nSELECT * FROM products;\n```\n\non huge production tables.\n\nWhy?\n\nBecause:\n\n* slow queries\n* huge memory usage\n* network overhead\n* bad user experience\n\nInstead, we fetch data in chunks.\n\nThat is where:\n\n* `LIMIT`\n* `OFFSET`\n* pagination\n\ncome in.\n\n---\n\n# 1. LIMIT\n\n# What LIMIT Does\n\n`LIMIT` restricts:\n\n# \"How many rows PostgreSQL should return\"\n\n---\n\n# Basic Syntax\n\n```sql id=\"i86v4r\"\nSELECT *\nFROM products\nLIMIT 5;\n```\n\nMeaning:\n\n```text id=\"dy5eqs\"\nReturn only 5 rows\n```\n\neven if the table has 10 million rows.\n\n---\n\n# Example\n\nSuppose table:\n\n| id | name     |\n| -- | -------- |\n| 1  | iPhone   |\n| 2  | Mouse    |\n| 3  | Keyboard |\n| 4  | Monitor  |\n| 5  | Chair    |\n| 6  | Camera   |\n\nQuery:\n\n```sql id=\"csmg9q\"\nSELECT *\nFROM products\nLIMIT 3;\n```\n\nResult:\n\n| id | name     |\n| -- | -------- |\n| 1  | iPhone   |\n| 2  | Mouse    |\n| 3  | Keyboard |\n\nOnly first 3 rows returned.\n\n---\n\n# Why LIMIT is Important\n\n---\n\n## A) Performance\n\nHuge tables become manageable.\n\n---\n\n## B) APIs\n\nMost APIs never return entire datasets.\n\nExample:\n\n```text id=\"3drw85\"\nGET /products\n```\n\nUsually returns maybe:\n\n```text id=\"69v5u5\"\n10\n20\n50\n```\n\nitems.\n\n---\n\n## C) Infinite Scrolling\n\nSocial media feeds use limited chunks.\n\n---\n\n# LIMIT Without ORDER BY is Dangerous\n\nThis is VERY important.\n\n---\n\n# Bad Practice\n\n```sql id=\"c8e7nv\"\nSELECT *\nFROM products\nLIMIT 5;\n```\n\nProblem:\n\n# PostgreSQL does NOT guarantee row order\n\nMeaning results may differ.\n\n---\n\n# Correct Practice\n\n```sql id=\"t4d0pj\"\nSELECT *\nFROM products\nORDER BY created_at DESC\nLIMIT 5;\n```\n\nNow results are deterministic.\n\n---\n\n# Mental Model\n\n`LIMIT` means:\n\n# \"Stop after N rows\"\n\n---\n\n# 2. OFFSET\n\n# What OFFSET Does\n\n`OFFSET` skips rows.\n\n---\n\n# Syntax\n\n```sql id=\"ob44w2\"\nSELECT *\nFROM products\nOFFSET 5;\n```\n\nMeaning:\n\n```text id=\"cw3kri\"\nSkip first 5 rows\n```\n\nand return the rest.\n\n---\n\n# Example\n\nTable:\n\n| id | name |\n| -- | ---- |\n| 1  | A    |\n| 2  | B    |\n| 3  | C    |\n| 4  | D    |\n| 5  | E    |\n| 6  | F    |\n| 7  | G    |\n\nQuery:\n\n```sql id=\"vmptpn\"\nSELECT *\nFROM products\nOFFSET 3;\n```\n\nResult:\n\n| id | name |\n| -- | ---- |\n| 4  | D    |\n| 5  | E    |\n| 6  | F    |\n| 7  | G    |\n\nFirst 3 skipped.\n\n---\n\n# OFFSET is Usually Used WITH LIMIT\n\nBecause OFFSET alone is uncommon.\n\n---\n\n# Example\n\n```sql id=\"4m6z7z\"\nSELECT *\nFROM products\nLIMIT 5\nOFFSET 10;\n```\n\nMeaning:\n\n```text id=\"66whjz\"\nSkip first 10 rows\nThen return next 5 rows\n```\n\n---\n\n# Visual Understanding\n\nSuppose rows:\n\n```text id=\"a85yzv\"\n1 2 3 4 5 6 7 8 9 10 11 12\n```\n\nQuery:\n\n```sql id=\"thq29u\"\nLIMIT 3 OFFSET 4\n```\n\nSteps:\n\n---\n\n## Step 1\n\nSkip:\n\n```text id=\"wt9bf0\"\n1 2 3 4\n```\n\n---\n\n## Step 2\n\nTake next 3:\n\n```text id=\"mgbn6m\"\n5 6 7\n```\n\nResult:\n\n```text id=\"m0i6md\"\n5 6 7\n```\n\n---\n\n# ORDER MATTERS\n\nAlways combine with `ORDER BY`.\n\nCorrect:\n\n```sql id=\"r3o1uo\"\nSELECT *\nFROM products\nORDER BY created_at DESC\nLIMIT 10\nOFFSET 20;\n```\n\n---\n\n# 3. Pagination\n\nPagination means:\n\n# Splitting large datasets into pages\n\nExample:\n\n```text id=\"m8sdmz\"\nPage 1\nPage 2\nPage 3\n```\n\nCommon in:\n\n* ecommerce\n* blogs\n* admin dashboards\n* APIs\n\n---\n\n# Real Example\n\nSuppose:\n\n```text id=\"3mth3j\"\n10 products per page\n```\n\n---\n\n# Page 1\n\n```sql id=\"up9z6r\"\nSELECT *\nFROM products\nORDER BY id\nLIMIT 10\nOFFSET 0;\n```\n\n---\n\n# Page 2\n\n```sql id=\"98gcsi\"\nSELECT *\nFROM products\nORDER BY id\nLIMIT 10\nOFFSET 10;\n```\n\n---\n\n# Page 3\n\n```sql id=\"2g3ty4\"\nSELECT *\nFROM products\nORDER BY id\nLIMIT 10\nOFFSET 20;\n```\n\n---\n\n# Pagination Formula\n\nThis is VERY important.\n\n# Formula\n\n\\text{OFFSET}=(\\text{page}-1)\\times\\text{limit}\n\n---\n\n# Example\n\nSuppose:\n\n```text id=\"r1k0x4\"\npage = 4\nlimit = 10\n```\n\nThen:\n\n(4-1)\\times10=30\n\nQuery:\n\n```sql id=\"jk4x0q\"\nSELECT *\nFROM products\nORDER BY id\nLIMIT 10\nOFFSET 30;\n```\n\n---\n\n# Backend Example\n\nSuppose frontend sends:\n\n```text id=\"ay7jlwm\"\n?page=3\u0026limit=10\n```\n\nBackend calculates:\n\n```javascript id=\"1cshaj\"\nconst offset = (page - 1) * limit;\n```\n\nSQL:\n\n```sql id=\"wn7qv7\"\nSELECT *\nFROM products\nORDER BY id\nLIMIT 10\nOFFSET 20;\n```\n\n---\n\n# Why Pagination Matters\n\nWithout pagination:\n\n```text id=\"5mv2m8\"\nFrontend freezes\nHuge API responses\nMemory waste\nSlow loading\nBad UX\n```\n\nImagine returning:\n\n```text id=\"4odn1w\"\n2 million products\n```\n\nto browser.\n\nDisaster.\n\n---\n\n# Real-World API Usage\n\nExample response:\n\n```json id=\"4c3ayh\"\n{\n  \"page\": 2,\n  \"limit\": 10,\n  \"total\": 100,\n  \"data\": [...]\n}\n```\n\nVery common REST API design.\n\n---\n\n# LIMIT/OFFSET Execution Internally\n\nThis is important theoretically.\n\n---\n\n# PostgreSQL Still Reads Rows\n\nMany beginners think:\n\n```text id=\"d1n1r7\"\nOFFSET 1000000\n```\n\nmeans PostgreSQL jumps magically.\n\nNot exactly.\n\nPostgreSQL often still scans/skips rows internally.\n\nMeaning:\n\n```text id=\"bjlwmc\"\nLarge OFFSET becomes slow\n```\n\n---\n\n# Problem with Large OFFSET\n\nExample:\n\n```sql id=\"e99pza\"\nSELECT *\nFROM products\nORDER BY id\nLIMIT 10\nOFFSET 1000000;\n```\n\nPostgreSQL may still process 1 million rows first.\n\nVery expensive.\n\n---\n\n# Why OFFSET Pagination Becomes Slow\n\nBecause database must:\n\n```text id=\"s88a6r\"\nRead\nSort\nSkip\nThen return\n```\n\nlarge amounts of rows.\n\n---\n\n# Better Alternative: Cursor Pagination (Keyset Pagination)\n\nAdvanced systems often avoid OFFSET for huge datasets.\n\nInstead use:\n\n# WHERE-based pagination\n\nExample:\n\n```sql id=\"i4b9mr\"\nSELECT *\nFROM products\nWHERE id \u003e 100\nORDER BY id\nLIMIT 10;\n```\n\nThis is MUCH faster for massive datasets.\n\nUsed heavily in:\n\n* Twitter/X\n* Instagram\n* Facebook feeds\n* large APIs\n\n---\n\n# OFFSET Pagination vs Cursor Pagination\n\n| Feature                   | OFFSET | Cursor    |\n| ------------------------- | ------ | --------- |\n| Simple                    | Yes    | Moderate  |\n| Good for small apps       | Yes    | Yes       |\n| Large dataset performance | Poor   | Excellent |\n| Random page access        | Easy   | Hard      |\n| Infinite scrolling        | Okay   | Excellent |\n\n---\n\n# COUNT(*) With Pagination\n\nOften APIs need total rows.\n\nExample:\n\n```sql id=\"8v7f2k\"\nSELECT COUNT(*)\nFROM products;\n```\n\nCombined with pagination metadata.\n\n---\n\n# Common Pagination API Structure\n\nExample:\n\n```json id=\"aj0fsr\"\n{\n  \"totalItems\": 500,\n  \"currentPage\": 2,\n  \"pageSize\": 10,\n  \"totalPages\": 50,\n  \"data\": [...]\n}\n```\n\n---\n\n# Important Best Practices\n\n---\n\n# 1. ALWAYS Use ORDER BY\n\nBad:\n\n```sql id=\"8rqmhh\"\nSELECT * FROM products LIMIT 10;\n```\n\nGood:\n\n```sql id=\"zjlwm9\"\nSELECT *\nFROM products\nORDER BY id\nLIMIT 10;\n```\n\n---\n\n# 2. Index Your ORDER BY Column\n\nExample:\n\n```sql id=\"3wg5nz\"\nCREATE INDEX idx_products_created_at\nON products(created_at);\n```\n\nImproves pagination performance.\n\n---\n\n# 3. Avoid Huge OFFSET\n\nBad:\n\n```sql id=\"0ew1he\"\nOFFSET 5000000\n```\n\n---\n\n# 4. Use Cursor Pagination for Massive Apps\n\nEspecially:\n\n* social media\n* real-time feeds\n* infinite scrolling\n\n---\n\n# Real-World Mental Model\n\n---\n\n# LIMIT\n\nThink:\n\n# \"How many rows do we want?\"\n\n---\n\n# OFFSET\n\nThink:\n\n# \"How many rows should we skip first?\"\n\n---\n\n# Pagination\n\nThink:\n\n# \"How do we split massive data into manageable pages?\"\n\n# Joins in PostgreSQL — In Depth\n\nJoins are the heart of relational databases.\n\nWithout joins:\n\n* our tables become isolated\n* our database loses most of its relational power\n\nJoins allow us to combine related data from multiple tables.\n\nThis is how real applications work:\n\n* users + posts\n* customers + orders\n* products + categories\n* payments + invoices\n* comments + authors\n\nAlmost every serious backend application relies heavily on joins.\n\n---\n\n# Why Joins Exist\n\nRelational databases follow a concept called:\n\n# Normalization\n\nThis means we split data into related tables to:\n\n* reduce duplication\n* improve consistency\n* organize data properly\n\n---\n\n# Example Without Normalization (Bad Design)\n\n```txt id=\"x1c8z7\"\nposts\n------------------------------------------------------\npost_id | title       | author_name | author_email\n------------------------------------------------------\n1       | SQL Tips    | Skyy        | skyy@gmail.com\n2       | GoLang      | Skyy        | skyy@gmail.com\n```\n\nProblems:\n\n* repeated user data\n* difficult updates\n* wasted storage\n* inconsistent records possible\n\n---\n\n# Normalized Structure (Good Design)\n\n## users\n\n| id | name | email                                   |\n| -- | ---- | --------------------------------------- |\n| 1  | Skyy | [skyy@gmail.com](mailto:skyy@gmail.com) |\n\n---\n\n## posts\n\n| id | title    | user_id |\n| -- | -------- | ------- |\n| 1  | SQL Tips | 1       |\n| 2  | GoLang   | 1       |\n\nNow:\n\n* user information exists once\n* relationships are maintained through foreign keys\n\nThen joins help us reconstruct related data whenever we need it.\n\n---\n\n# Relationship Types\n\nBefore learning joins deeply, we should understand relationships.\n\n---\n\n# 1. One-to-One\n\n```txt id=\"e9wq4p\"\nusers ↔ profiles\n```\n\nOne user:\n\n* has one profile\n\n---\n\n# 2. One-to-Many\n\n```txt id=\"4g1zuv\"\nusers → posts\n```\n\nOne user:\n\n* can write many posts\n\nOne post:\n\n* belongs to one user\n\nThis is the most common relationship type.\n\n---\n\n# 3. Many-to-Many\n\n```txt id=\"j9yb1q\"\nposts ↔ tags\n```\n\nOne post:\n\n* can have many tags\n\nOne tag:\n\n* can belong to many posts\n\nThis requires a junction table.\n\n---\n\n# Core Idea of a Join\n\nA join matches related rows between tables.\n\nUsually through:\n\n```sql id=\"n8c7vl\"\nON parent.id = child.foreign_key\n```\n\n---\n\n# Example Tables\n\n---\n\n# users\n\n| id | name  |\n| -- | ----- |\n| 1  | Skyy  |\n| 2  | Bruce |\n| 3  | Tony  |\n\n---\n\n# posts\n\n| id  | title      | user_id |\n| --- | ---------- | ------- |\n| 101 | SQL Tips   | 1       |\n| 102 | Batman DB  | 2       |\n| 103 | Ironman AI | 3       |\n| 104 | Unknown    | NULL    |\n\n---\n\n# INNER JOIN\n\nThis is the most important join.\n\n---\n\n# Query\n\n```sql id=\"1yk2sr\"\nSELECT\n    users.name,\n    posts.title\nFROM users\nINNER JOIN posts\nON users.id = posts.user_id;\n```\n\n---\n\n# Meaning\n\nWe only return rows where:\n\n* a matching relationship exists\n\n---\n\n# Matching Logic\n\nPostgreSQL checks:\n\n```txt id=\"4dnq7x\"\nusers.id == posts.user_id\n```\n\n---\n\n# Matches\n\n| users.id | posts.user_id |\n| -------- | ------------- |\n| 1        | 1             |\n| 2        | 2             |\n| 3        | 3             |\n\n---\n\n# Result\n\n| name  | title      |\n| ----- | ---------- |\n| Skyy  | SQL Tips   |\n| Bruce | Batman DB  |\n| Tony  | Ironman AI |\n\n---\n\n# Important\n\nThe post:\n\n```txt id=\"v2j7na\"\nUnknown\n```\n\ngets excluded because:\n\n* it has no matching user\n\n---\n\n# INNER JOIN = Intersection\n\nWe can think of INNER JOIN as:\n\n```txt id=\"0mn4ze\"\nonly matching rows survive\n```\n\n---\n\n# LEFT JOIN\n\nExtremely common in real applications.\n\n---\n\n# Query\n\n```sql id=\"v2y4w1\"\nSELECT\n    users.name,\n    posts.title\nFROM users\nLEFT JOIN posts\nON users.id = posts.user_id;\n```\n\n---\n\n# Meaning\n\nWe return:\n\n* ALL rows from the LEFT table\n* matching rows from the RIGHT table\n\nIf no match exists:\n\n* PostgreSQL fills RIGHT-side columns with NULL\n\n---\n\n# Example\n\nSuppose:\n\n## users\n\n| id | name  |\n| -- | ----- |\n| 1  | Skyy  |\n| 2  | Bruce |\n| 3  | Tony  |\n| 4  | Peter |\n\n---\n\n## posts\n\n| title    | user_id |\n| -------- | ------- |\n| SQL Tips | 1       |\n| Batman   | 2       |\n\n---\n\n# Result\n\n| name  | title    |\n| ----- | -------- |\n| Skyy  | SQL Tips |\n| Bruce | Batman   |\n| Tony  | NULL     |\n| Peter | NULL     |\n\n---\n\n# Why LEFT JOIN Matters\n\nWe use it constantly for:\n\n* dashboards\n* analytics\n* reports\n* optional relationships\n* finding missing data\n\n---\n\n# RIGHT JOIN\n\nRIGHT JOIN is the opposite of LEFT JOIN.\n\n---\n\n# Query\n\n```sql id=\"7n4m3v\"\nSELECT\n    users.name,\n    posts.title\nFROM users\nRIGHT JOIN posts\nON users.id = posts.user_id;\n```\n\n---\n\n# Meaning\n\nWe return:\n\n* ALL rows from the RIGHT table\n* matching rows from the LEFT table\n\n---\n\n# FULL OUTER JOIN\n\nReturns everything.\n\n---\n\n# Query\n\n```sql id=\"z7x1m2\"\nSELECT\n    users.name,\n    posts.title\nFROM users\nFULL OUTER JOIN posts\nON users.id = posts.user_id;\n```\n\n---\n\n# Meaning\n\nWe get:\n\n* matched rows\n* unmatched LEFT rows\n* unmatched RIGHT rows\n\n---\n\n# CROSS JOIN\n\nPotentially dangerous if misunderstood.\n\n---\n\n# Query\n\n```sql id=\"0c2v1b\"\nSELECT *\nFROM users\nCROSS JOIN posts;\n```\n\n---\n\n# Meaning\n\nEvery user combines with every post.\n\n---\n\n# Example\n\nIf we have:\n\n* 3 users\n* 4 posts\n\nthen PostgreSQL generates:\n\n```txt id=\"0pk9sj\"\n3 × 4 = 12 rows\n```\n\n---\n\n# Cartesian Product\n\nFormula:\n\n```txt id=\"a1mf8x\"\nrowsA × rowsB\n```\n\nThis can explode into millions of rows accidentally.\n\n---\n\n# SELF JOIN\n\nA table joining itself.\n\n---\n\n# Example Table\n\n| id | name  | manager_id |\n| -- | ----- | ---------- |\n| 1  | Bruce | NULL       |\n| 2  | Clark | 1          |\n\n---\n\n# Query\n\n```sql id=\"m8z0rk\"\nSELECT\n    e.name AS employee,\n    m.name AS manager\nFROM employees e\nLEFT JOIN employees m\nON e.manager_id = m.id;\n```\n\n---\n\n# Why Aliases Matter\n\nAliases make queries:\n\n* shorter\n* cleaner\n* easier to read\n\nEspecially in joins.\n\n---\n\n# Example\n\n```sql id=\"4v1wqe\"\nFROM users u\nINNER JOIN posts p\nON u.id = p.user_id\n```\n\n---\n\n# Multi-Table Joins\n\nReal applications usually join many tables together.\n\n---\n\n# Example\n\n```sql id=\"2c9y1l\"\nSELECT\n    users.name,\n    posts.title,\n    comments.body\nFROM users\nINNER JOIN posts\nON users.id = posts.user_id\nINNER JOIN comments\nON posts.id = comments.post_id;\n```\n\n---\n\n# Relationship Flow\n\n```txt id=\"4q2vzo\"\nusers\n   ↓\nposts\n   ↓\ncomments\n```\n\n---\n\n# Many-to-Many Joins\n\n---\n\n# Tables\n\n```txt id=\"6xt7wp\"\nposts\ntags\npost_tags\n```\n\n---\n\n# Query\n\n```sql id=\"1mz9cp\"\nSELECT\n    posts.title,\n    tags.name\nFROM posts\nINNER JOIN post_tags\nON posts.id = post_tags.post_id\nINNER JOIN tags\nON tags.id = post_tags.tag_id;\n```\n\n---\n\n# Why Junction Tables Exist\n\nRelational databases cannot directly store:\n\n* many-to-many relationships\n\nSo we create a bridge table.\n\n---\n\n# NULL Behavior in Joins\n\nVery important.\n\n---\n\n# INNER JOIN\n\nRows without matches usually disappear.\n\n---\n\n# LEFT JOIN\n\nUnmatched RIGHT-side rows become:\n\n```txt id=\"2w8m4v\"\nNULL\n```\n\n---\n\n# Example Query\n\n```sql id=\"7j2m8p\"\nSELECT\n    users.name,\n    posts.title\nFROM users\nLEFT JOIN posts\nON users.id = posts.user_id\nWHERE posts.id IS NULL;\n```\n\n---\n\n# Meaning\n\nFind users who have:\n\n* no posts\n\nThis is a very common real-world query.\n\n---\n\n# How PostgreSQL Executes Joins Internally\n\nPostgreSQL may choose different strategies:\n\n| Strategy         | Typical Usage              |\n| ---------------- | -------------------------- |\n| Nested Loop Join | small datasets             |\n| Hash Join        | very common efficient join |\n| Merge Join       | sorted joins               |\n\nThe query planner chooses the best one automatically.\n\n---\n\n# Indexes Matter a Lot\n\nJoin performance heavily depends on indexes.\n\n---\n\n# Common Indexed Columns\n\n```sql id=\"1c8v5m\"\nusers.id\nposts.user_id\n```\n\nForeign keys are often indexed because joins rely on them constantly.\n\nWithout indexes:\n\n* joins become slow on large datasets\n\n---\n\n# Real Backend Examples\n\n---\n\n# Blog Application\n\n```txt id=\"7p9x2l\"\nusers ↔ posts ↔ comments\n```\n\n---\n\n# Ecommerce\n\n```txt id=\"8k0w1n\"\ncustomers ↔ orders ↔ order_items ↔ products\n```\n\n---\n\n# Social Media\n\n```txt id=\"4t6n8q\"\nusers ↔ posts ↔ likes ↔ comments\n```\n\n---\n\n# SaaS Billing\n\n```txt id=\"3z1m8r\"\nusers ↔ subscriptions ↔ invoices ↔ payments\n```\n\n---\n\n# Most Important Mental Model\n\nA join is simply:\n\n```txt id=\"9f3c1x\"\nmatching related rows across tables\n```\n\nusing:\n\n* primary keys\n* foreign keys\n\n---\n\n# Most Common Beginner Mistakes\n\n---\n\n# 1. Missing ON Condition\n\n```sql id=\"6r2w8v\"\nSELECT *\nFROM users\nJOIN posts;\n```\n\nCan accidentally create a huge cartesian product.\n\n---\n\n# 2. Wrong Join Condition\n\nIncorrect:\n\n```sql id=\"8n4c1m\"\nON users.id = posts.id\n```\n\nCorrect:\n\n```sql id=\"7v1m9x\"\nON users.id = posts.user_id\n```\n\n---\n\n# 3. Ambiguous Columns\n\nThis is unclear:\n\n```sql id=\"0w3x8m\"\nSELECT id\n```\n\nWhich table’s `id`?\n\nBetter:\n\n```sql id=\"9m2c7p\"\nusers.id\n```\n\n---\n\n# 4. Using INNER JOIN When LEFT JOIN Is Needed\n\nThis can accidentally hide rows.\n\nVery common bug in:\n\n* reports\n* dashboards\n* analytics systems\n\n---\n\n# Most Common Joins Used in Industry\n\n| Join       | Usage Frequency  |\n| ---------- | ---------------- |\n| INNER JOIN | extremely common |\n| LEFT JOIN  | extremely common |\n| RIGHT JOIN | rare             |\n| FULL JOIN  | rare             |\n| CROSS JOIN | niche/dangerous  |\n\nIn real backend development, we mostly master:\n\n* INNER JOIN\n* LEFT JOIN\n\nbecause those solve the majority of production problems.\n\n# Aggregate Functions in PostgreSQL — In Depth\n\nAggregate functions allow us to calculate values from multiple rows.\n\nInstead of returning:\n\n* individual rows\n\nthey return:\n\n* summarized/computed results\n\nThese are heavily used in:\n\n* analytics\n* dashboards\n* reports\n* business metrics\n* backend APIs\n* admin panels\n\nWithout aggregates, SQL would be far less useful for real applications.\n\n---\n\n# What Aggregate Functions Do\n\nSuppose we have:\n\n| name  | salary |\n| ----- | ------ |\n| Skyy  | 50000  |\n| Bruce | 70000  |\n| Tony  | 90000  |\n\nNormally:\n\n```sql id=\"2v9q1x\"\nSELECT salary FROM employees;\n```\n\nreturns:\n\n```txt id=\"7m1x2w\"\n50000\n70000\n90000\n```\n\nBut aggregate functions summarize rows.\n\nExample:\n\n```sql id=\"9w2m6q\"\nSELECT AVG(salary) FROM employees;\n```\n\nreturns:\n\n```txt id=\"1z0x7v\"\n70000\n```\n\n(single computed result)\n\n---\n\n# Most Important Aggregate Functions\n\n| Function  | Purpose        |\n| --------- | -------------- |\n| `COUNT()` | count rows     |\n| `SUM()`   | total values   |\n| `AVG()`   | average        |\n| `MIN()`   | smallest value |\n| `MAX()`   | largest value  |\n\nThese are the core aggregates we constantly use.\n\n---\n\n# Example Table\n\nWe’ll use:\n\n```sql id=\"0v4x9m\"\nCREATE TABLE orders(\n    id SERIAL PRIMARY KEY,\n    customer_name TEXT,\n    amount NUMERIC(10,2),\n    status TEXT\n);\n```\n\n---\n\n# Sample Data\n\n| id | customer_name | amount | status  |\n| -- | ------------- | ------ | ------- |\n| 1  | Skyy          | 500    | paid    |\n| 2  | Bruce         | 300    | pending |\n| 3  | Tony          | 800    | paid    |\n| 4  | Skyy          | 200    | paid    |\n\n---\n\n# 1. COUNT()\n\nCounts rows.\n\n---\n\n# Count All Rows\n\n```sql id=\"5m8x2q\"\nSELECT COUNT(*)\nFROM orders;\n```\n\n---\n\n# Result\n\n```txt id=\"8x7m1v\"\n4\n```\n\nbecause table contains:\n\n* 4 rows\n\n---\n\n# Why `*`?\n\n```sql id=\"1m4x9q\"\nCOUNT(*)\n```\n\nmeans:\n\n\u003e count every row\n\n---\n\n# Count Specific Column\n\n```sql id=\"9q2m1x\"\nSELECT COUNT(status)\nFROM orders;\n```\n\nCounts:\n\n* non-NULL values only\n\nImportant distinction.\n\n---\n\n# COUNT(column) vs COUNT(*)\n\n---\n\n# `COUNT(*)`\n\nCounts ALL rows.\n\n---\n\n# `COUNT(column)`\n\nCounts only:\n\n* non-NULL values\n\n---\n\n# Example\n\n| name  | age  |\n| ----- | ---- |\n| Skyy  | 29   |\n| Bruce | NULL |\n\n---\n\n```sql id=\"6x2m8w\"\nSELECT COUNT(age)\nFROM users;\n```\n\nreturns:\n\n```txt id=\"3m9x1v\"\n1\n```\n\nbecause NULL ignored.\n\n---\n\n# 2. SUM()\n\nAdds numeric values.\n\n---\n\n# Query\n\n```sql id=\"4w8m1x\"\nSELECT SUM(amount)\nFROM orders;\n```\n\n---\n\n# Result\n\n```txt id=\"0v2m9x\"\n1800\n```\n\nbecause:\n\n```txt id=\"6m1x8q\"\n500 + 300 + 800 + 200\n```\n\n---\n\n# Used For\n\n* total revenue\n* total sales\n* total views\n* total expenses\n\nVery common in business systems.\n\n---\n\n# 3. AVG()\n\nCalculates average.\n\n---\n\n# Query\n\n```sql id=\"8m2x0v\"\nSELECT AVG(amount)\nFROM orders;\n```\n\n---\n\n# Result\n\n```txt id=\"5q1x9m\"\n450\n```\n\n---\n\n# Formula\n\n```txt id=\"1x9m2q\"\nSUM / COUNT\n```\n\n---\n\n# Used For\n\n* average salary\n* average rating\n* average order value\n* average response time\n\n---\n\n# 4. MIN()\n\nSmallest value.\n\n---\n\n# Query\n\n```sql id=\"7m1q8x\"\nSELECT MIN(amount)\nFROM orders;\n```\n\n---\n\n# Result\n\n```txt id=\"2x8m1v\"\n200\n```\n\n---\n\n# 5. MAX()\n\nLargest value.\n\n---\n\n# Query\n\n```sql id=\"9m4x2q\"\nSELECT MAX(amount)\nFROM orders;\n```\n\n---\n\n# Result\n\n```txt id=\"0x7m1v\"\n800\n```\n\n---\n\n# Combining Multiple Aggregates\n\nVery common.\n\n---\n\n# Query\n\n```sql id=\"3x1m8q\"\nSELECT\n    COUNT(*) AS total_orders,\n    SUM(amount) AS total_revenue,\n    AVG(amount) AS avg_order,\n    MIN(amount) AS smallest_order,\n    MAX(amount) AS biggest_order\nFROM orders;\n```\n\n---\n\n# Result\n\n| total_orders | total_revenue | avg_order | smallest_order | biggest_order |\n| ------------ | ------------- | --------- | -------------- | ------------- |\n| 4            | 1800          | 450       | 200            | 800           |\n\n---\n\n# GROUP BY — Extremely Important\n\nThis is where aggregates become powerful.\n\n---\n\n# Problem\n\nWithout grouping:\n\n```sql id=\"8x1m2q\"\nSELECT AVG(amount)\nFROM orders;\n```\n\ngives one average for ALL rows.\n\nBut what if we want:\n\n```txt id=\"9m2x1v\"\naverage per customer\n```\n\n?\n\n---\n\n# GROUP BY Solves This\n\n---\n\n# Query\n\n```sql id=\"5x8m1q\"\nSELECT\n    customer_name,\n    SUM(amount) AS total_spent\nFROM orders\nGROUP BY customer_name;\n```\n\n---\n\n# Result\n\n| customer_name | total_spent |\n| ------------- | ----------- |\n| Skyy          | 700         |\n| Bruce         | 300         |\n| Tony          | 800         |\n\n---\n\n# Mental Model\n\n`GROUP BY` creates buckets/groups.\n\n---\n\n# Example\n\nBefore grouping:\n\n```txt id=\"4m2x9q\"\nSkyy 500\nBruce 300\nTony 800\nSkyy 200\n```\n\n---\n\n# After grouping\n\n```txt id=\"6x1m8v\"\nSkyy → [500, 200]\nBruce → [300]\nTony → [800]\n```\n\nThen aggregates apply inside each group.\n\n---\n\n# GROUP BY Rule\n\nVery important SQL rule.\n\n---\n\n# Wrong Query\n\n```sql id=\"8m1x4q\"\nSELECT customer_name, amount\nFROM orders\nGROUP BY customer_name;\n```\n\nError occurs because:\n\n* `amount` not aggregated\n* not grouped\n\n---\n\n# Correct\n\n```sql id=\"2x9m1q\"\nSELECT\n    customer_name,\n    SUM(amount)\nFROM orders\nGROUP BY customer_name;\n```\n\n---\n\n# HAVING\n\nUsed to filter groups.\n\n---\n\n# Example\n\n```sql id=\"7x2m1q\"\nSELECT\n    customer_name,\n    SUM(amount) AS total_spent\nFROM orders\nGROUP BY customer_name\nHAVING SUM(amount) \u003e 500;\n```\n\n---\n\n# Result\n\n| customer_name | total_spent |\n| ------------- | ----------- |\n| Skyy          | 700         |\n| Tony          | 800         |\n\n---\n\n# Difference Between WHERE and HAVING\n\nHuge concept.\n\n---\n\n# WHERE\n\nFilters rows BEFORE grouping.\n\n---\n\n# HAVING\n\nFilters groups AFTER grouping.\n\n---\n\n# Execution Order (Important)\n\nSQL roughly processes:\n\n```txt id=\"0m2x8v\"\nFROM\nWHERE\nGROUP BY\nHAVING\nSELECT\nORDER BY\nLIMIT\n```\n\nUnderstanding this explains many SQL behaviors.\n\n---\n\n# DISTINCT with Aggregates\n\n---\n\n# Example\n\n```sql id=\"3m8x1q\"\nSELECT COUNT(DISTINCT customer_name)\nFROM orders;\n```\n\n---\n\n# Result\n\n```txt id=\"1x2m9v\"\n3\n```\n\nbecause:\n\n* Skyy counted once\n\n---\n\n# NULL Behavior\n\nMost aggregates ignore NULL.\n\n---\n\n# Example\n\n| amount |\n| ------ |\n| 100    |\n| NULL   |\n| 200    |\n\n---\n\n# SUM()\n\nreturns:\n\n```txt id=\"5x1m8v\"\n300\n```\n\nNULL ignored.\n\n---\n\n# AVG()\n\nreturns:\n\n```txt id=\"2m9x1q\"\n150\n```\n\nNULL ignored.\n\n---\n\n# Real Backend Examples\n\n---\n\n# Ecommerce Dashboard\n\n```sql id=\"8x2m1q\"\nSELECT SUM(amount)\nFROM orders;\n```\n\nTotal revenue.\n\n---\n\n# Social Media\n\n```sql id=\"7m1x2q\"\nSELECT COUNT(*)\nFROM posts;\n```\n\nTotal posts.\n\n---\n\n# Analytics\n\n```sql id=\"4x9m1q\"\nSELECT AVG(session_duration)\nFROM analytics;\n```\n\nAverage session time.\n\n---\n\n# Blog Platform\n\n```sql id=\"9x1m2q\"\nSELECT\n    user_id,\n    COUNT(*) AS total_posts\nFROM posts\nGROUP BY user_id;\n```\n\nPosts per author.\n\n---\n\n# Aggregate + JOIN\n\nVery common.\n\n---\n\n# Example\n\n```sql id=\"1m8x2q\"\nSELECT\n    users.name,\n    COUNT(posts.id) AS total_posts\nFROM users\nLEFT JOIN posts\nON users.id = posts.user_id\nGROUP BY users.name;\n```\n\n---\n\n# Meaning\n\nCount posts written by each user.\n\n---\n\n# Result\n\n| name  | total_posts |\n| ----- | ----------- |\n| Skyy  | 5           |\n| Bruce | 2           |\n\n---\n\n# Most Common Beginner Mistakes\n\n---\n\n# 1. Forgetting GROUP BY\n\nVery common error.\n\n---\n\n# 2. Mixing Aggregated + Non-Aggregated Columns\n\nIncorrect:\n\n```sql id=\"6m2x1q\"\nSELECT name, COUNT(*)\nFROM users;\n```\n\nNeed:\n\n```sql id=\"3x1m9q\"\nGROUP BY name\n```\n\n---\n\n# 3. Using WHERE Instead of HAVING\n\nIncorrect:\n\n```sql id=\"0x8m1q\"\nWHERE COUNT(*) \u003e 5\n```\n\nCorrect:\n\n```sql id=\"2x1m8q\"\nHAVING COUNT(*) \u003e 5\n```\n\n---\n\n# 4. Forgetting NULL Behavior\n\nAggregates usually ignore NULL values.\n\n---\n\n# Most Important Mental Model\n\nAggregate functions:\n\n```txt id=\"5m2x1v\"\nconvert many rows into summarized information\n```\n\nwhile:\n\n```txt id=\"7x1m2v\"\nGROUP BY\n```\n\nlets us summarize:\n\n* per category\n* per user\n* per product\n* per status\n* per day\n\nThis is the foundation of SQL analytics and reporting systems.\n\n# `GROUP BY` in PostgreSQL — In Depth\n\n`GROUP BY` is one of the most important SQL concepts.\n\nIt allows us to:\n\n* organize rows into groups\n* calculate summaries per group\n* build reports\n* generate analytics\n* power dashboards\n\nWithout `GROUP BY`, aggregate functions only give us:\n\n* one result for the entire table\n\nWith `GROUP BY`, we can calculate results:\n\n* per user\n* per category\n* per product\n* per day\n* per status\n\nThis is fundamental in real backend systems.\n\n---\n\n# Core Idea\n\n`GROUP BY` groups rows that share the same value.\n\nThen aggregate functions operate:\n\n* inside each group\n\n---\n\n# Example Table\n\nSuppose we have:\n\n| id | customer | amount | status  |\n| -- | -------- | ------ | ------- |\n| 1  | Skyy     | 500    | paid    |\n| 2  | Bruce    | 300    | pending |\n| 3  | Skyy     | 200    | paid    |\n| 4  | Tony     | 800    | paid    |\n| 5  | Bruce    | 150    | paid    |\n\n---\n\n# Without GROUP BY\n\nIf we run:\n\n```sql id=\"3m1x8q\"\nSELECT SUM(amount)\nFROM orders;\n```\n\nResult:\n\n```txt id=\"7x2m1v\"\n1950\n```\n\nThis summarizes:\n\n* entire table\n\n---\n\n# Problem\n\nWhat if we want:\n\n```txt id=\"8m1x2v\"\ntotal amount per customer\n```\n\n?\n\nThat’s where `GROUP BY` comes in.\n\n---\n\n# Basic GROUP BY\n\n```sql id=\"5x1m9q\"\nSELECT\n    customer,\n    SUM(amount) AS total_spent\nFROM orders\nGROUP BY customer;\n```\n\n---\n\n# Result\n\n| customer | total_spent |\n| -------- | ----------- |\n| Skyy     | 700         |\n| Bruce    | 450         |\n| Tony     | 800         |\n\n---\n\n# What Happened Internally?\n\n---\n\n# Original Rows\n\n```txt id=\"1x2m8v\"\nSkyy  500\nBruce 300\nSkyy  200\nTony  800\nBruce 150\n```\n\n---\n\n# GROUP BY Creates Buckets\n\n```txt id=\"2m1x9v\"\nSkyy  → [500, 200]\nBruce → [300, 150]\nTony  → [800]\n```\n\nThen:\n\n```sql id=\"8x1m4q\"\nSUM(amount)\n```\n\nruns separately inside each group.\n\n---\n\n# Important Mental Model\n\n`GROUP BY` does NOT summarize entire table anymore.\n\nIt summarizes:\n\n* each group independently\n\n---\n\n# Syntax Structure\n\n```sql id=\"4m1x8q\"\nSELECT\n    grouped_column,\n    aggregate_function()\nFROM table\nGROUP BY grouped_column;\n```\n\n---\n\n# Another Example\n\n---\n\n# Count Orders Per Customer\n\n```sql id=\"7x1m3q\"\nSELECT\n    customer,\n    COUNT(*) AS total_orders\nFROM orders\nGROUP BY customer;\n```\n\n---\n\n# Result\n\n| customer | total_orders |\n| -------- | ------------ |\n| Skyy     | 2            |\n| Bruce    | 2            |\n| Tony     | 1            |\n\n---\n\n# GROUP BY with Multiple Columns\n\nVery common.\n\n---\n\n# Example\n\n```sql id=\"6x2m1q\"\nSELECT\n    customer,\n    status,\n    COUNT(*) AS total\nFROM orders\nGROUP BY customer, status;\n```\n\n---\n\n# Result\n\n| customer | status  | total |\n| -------- | ------- | ----- |\n| Skyy     | paid    | 2     |\n| Bruce    | pending | 1     |\n| Bruce    | paid    | 1     |\n| Tony     | paid    | 1     |\n\n---\n\n# What Happened?\n\nNow grouping uses BOTH columns.\n\nSo groups become:\n\n```txt id=\"9x1m2v\"\n(Skyy, paid)\n(Bruce, pending)\n(Bruce, paid)\n(Tony, paid)\n```\n\nEach unique combination creates a group.\n\n---\n\n# Important SQL Rule\n\nThis is one of the biggest beginner issues.\n\n---\n\n# Wrong Query\n\n```sql id=\"1m8x4q\"\nSELECT customer, amount\nFROM orders\nGROUP BY customer;\n```\n\n---\n\n# Why Error Happens\n\nBecause:\n\n* `customer` grouped\n* `amount` neither:\n\n  * grouped\n  * aggregated\n\nPostgreSQL does not know:\n\n* WHICH amount to show\n\n---\n\n# Correct Query\n\n```sql id=\"5x2m8q\"\nSELECT\n    customer,\n    SUM(amount)\nFROM orders\nGROUP BY customer;\n```\n\nNow:\n\n* `customer` grouped\n* `amount` aggregated\n\nValid.\n\n---\n\n# Important GROUP BY Rule\n\nEvery selected column must be either:\n\n| Allowed?   | Example       |\n| ---------- | ------------- |\n| grouped    | `customer`    |\n| aggregated | `SUM(amount)` |\n\nOtherwise SQL errors.\n\n---\n\n# Aggregate Functions Commonly Used with GROUP BY\n\n| Function  | Purpose    |\n| --------- | ---------- |\n| `COUNT()` | count rows |\n| `SUM()`   | total      |\n| `AVG()`   | average    |\n| `MIN()`   | smallest   |\n| `MAX()`   | largest    |\n\n---\n\n# Example\n\n```sql id=\"2x1m9q\"\nSELECT\n    customer,\n    COUNT(*) AS orders,\n    SUM(amount) AS total,\n    AVG(amount) AS average_order,\n    MAX(amount) AS biggest_order\nFROM orders\nGROUP BY customer;\n```\n\n---\n\n# HAVING — Filtering Groups\n\nVery important concept.\n\n---\n\n# Problem\n\nSuppose we only want customers whose spending exceeds 500.\n\nWe cannot use:\n\n```sql id=\"8x2m1q\"\nWHERE SUM(amount) \u003e 500\n```\n\nbecause:\n\n* WHERE runs BEFORE grouping\n\n---\n\n# Correct Solution\n\n```sql id=\"6m1x2q\"\nSELECT\n    customer,\n    SUM(amount) AS total_spent\nFROM orders\nGROUP BY customer\nHAVING SUM(amount) \u003e 500;\n```\n\n---\n\n# Result\n\n| customer | total_spent |\n| -------- | ----------- |\n| Skyy     | 700         |\n| Tony     | 800         |\n\n---\n\n# Difference Between WHERE and HAVING\n\nHuge interview/backend concept.\n\n---\n\n# WHERE\n\nFilters:\n\n* rows BEFORE grouping\n\n---\n\n# HAVING\n\nFilters:\n\n* groups AFTER grouping\n\n---\n\n# Visual Flow\n\n```txt id=\"0x1m8v\"\nRows\n  ↓\nWHERE\n  ↓\nGROUP BY\n  ↓\nHAVING\n  ↓\nFinal Result\n```\n\n---\n\n# Example Combining WHERE + GROUP BY + HAVING\n\n```sql id=\"3m1x9q\"\nSELECT\n    customer,\n    SUM(amount) AS total_paid\nFROM orders\nWHERE status = 'paid'\nGROUP BY customer\nHAVING SUM(amount) \u003e 300;\n```\n\n---\n\n# Step-by-Step\n\n---\n\n# 1. WHERE\n\nKeeps only:\n\n```txt id=\"5x1m2v\"\npaid rows\n```\n\n---\n\n# 2. GROUP BY\n\nGroups remaining rows by customer.\n\n---\n\n# 3. SUM()\n\nCalculates totals per customer.\n\n---\n\n# 4. HAVING\n\nFilters grouped totals.\n\n---\n\n# GROUP BY + ORDER BY\n\nVery common.\n\n---\n\n# Example\n\n```sql id=\"7m1x8q\"\nSELECT\n    customer,\n    SUM(amount) AS total_spent\nFROM orders\nGROUP BY customer\nORDER BY total_spent DESC;\n```\n\n---\n\n# Result\n\nHighest spending customers first.\n\n---\n\n# GROUP BY + JOIN\n\nExtremely common in backend systems.\n\n---\n\n# Example Tables\n\n## users\n\n| id | name  |\n| -- | ----- |\n| 1  | Skyy  |\n| 2  | Bruce |\n\n---\n\n## posts\n\n| id  | title  | user_id |\n| --- | ------ | ------- |\n| 101 | SQL    | 1       |\n| 102 | Go     | 1       |\n| 103 | Batman | 2       |\n\n---\n\n# Query\n\n```sql id=\"2m8x1q\"\nSELECT\n    users.name,\n    COUNT(posts.id) AS total_posts\nFROM users\nLEFT JOIN posts\nON users.id = posts.user_id\nGROUP BY users.name;\n```\n\n---\n\n# Result\n\n| name  | total_posts |\n| ----- | ----------- |\n| Skyy  | 2           |\n| Bruce | 1           |\n\n---\n\n# Why LEFT JOIN Here?\n\nBecause we may want:\n\n* users with zero posts too\n\nINNER JOIN could hide them.\n\n---\n\n# NULL Behavior\n\nImportant.\n\n---\n\n# Example\n\n| customer | amount |\n| -------- | ------ |\n| Skyy     | NULL   |\n| Skyy     | 500    |\n\n---\n\n# Query\n\n```sql id=\"8m2x1q\"\nSELECT\n    customer,\n    AVG(amount)\nFROM orders\nGROUP BY customer;\n```\n\n---\n\n# Result\n\n```txt id=\"1x9m4v\"\n500\n```\n\nNULL ignored by aggregates.\n\n---\n\n# GROUP BY Execution Order\n\nSQL roughly processes:\n\n```txt id=\"0m2x7v\"\nFROM\nWHERE\nGROUP BY\nHAVING\nSELECT\nORDER BY\nLIMIT\n```\n\nUnderstanding this explains:\n\n* why HAVING exists\n* why aggregates fail in WHERE\n* many SQL errors\n\n---\n\n# Real Backend Examples\n\n---\n\n# Ecommerce Dashboard\n\n```sql id=\"4x1m8q\"\nSELECT\n    product_id,\n    SUM(quantity)\nFROM order_items\nGROUP BY product_id;\n```\n\nTotal sales per product.\n\n---\n\n# Social Media\n\n```sql id=\"6x1m2q\"\nSELECT\n    user_id,\n    COUNT(*)\nFROM posts\nGROUP BY user_id;\n```\n\nPosts per user.\n\n---\n\n# SaaS Analytics\n\n```sql id=\"9m1x2q\"\nSELECT\n    DATE(created_at),\n    COUNT(*)\nFROM signups\nGROUP BY DATE(created_at);\n```\n\nDaily signups.\n\n---\n\n# Banking\n\n```sql id=\"2x1m7q\"\nSELECT\n    account_id,\n    SUM(amount)\nFROM transactions\nGROUP BY account_id;\n```\n\nAccount balances.\n\n---\n\n# Most Common Beginner Mistakes\n\n---\n\n# 1. Forgetting GROUP BY\n\nVery common.\n\n---\n\n# 2. Selecting Non-Aggregated Columns\n\nIncorrect:\n\n```sql id=\"5x1m8v\"\nSELECT customer, amount\nFROM orders\nGROUP BY customer;\n```\n\n---\n\n# 3. Using WHERE Instead of HAVING\n\nIncorrect:\n\n```sql id=\"8m1x2q\"\nWHERE COUNT(*) \u003e 5\n```\n\nCorrect:\n\n```sql id=\"6x2m1v\"\nHAVING COUNT(*) \u003e 5\n```\n\n---\n\n# 4. Confusing GROUP BY with ORDER BY\n\nHuge distinction.\n\n---\n\n# GROUP BY\n\nCreates groups.\n\n---\n\n# ORDER BY\n\nSorts results.\n\nEntirely different operations.\n\n---\n\n# Most Important Mental Model\n\n`GROUP BY`:\n\n```txt id=\"7m2x1v\"\nsplits rows into groups\n```\n\nThen aggregate functions:\n\n* summarize each group independently\n\nThis is the foundation of:\n\n* SQL analytics\n* reporting systems\n* admin dashboards\n* business intelligence\n* backend metrics systems\n\n# `HAVING` in PostgreSQL — In Depth\n\n`HAVING` is used to filter groups AFTER `GROUP BY`.\n\nThis is one of the most important SQL concepts because beginners often confuse:\n\n* `WHERE`\n* `HAVING`\n\nThe difference is fundamental.\n\n---\n\n# Core Idea\n\n---\n\n# `WHERE`\n\nFilters:\n\n* individual rows\n\nBEFORE grouping happens.\n\n---\n\n# `HAVING`\n\nFilters:\n\n* grouped results\n\nAFTER grouping happens.\n\n---\n\n# Mental Model\n\nThink of SQL execution like this:\n\n```txt id=\"4m8x1v\"\nRows\n  ↓\nWHERE\n  ↓\nGROUP BY\n  ↓\nHAVING\n  ↓\nSELECT\n  ↓\nORDER BY\n```\n\nThis order explains:\n\n* why `HAVING` exists\n* why aggregate functions fail inside `WHERE`\n\n---\n\n# Example Table\n\nSuppose we have:\n\n| id | customer | amount | status  |\n| -- | -------- | ------ | ------- |\n| 1  | Skyy     | 500    | paid    |\n| 2  | Bruce    | 300    | pending |\n| 3  | Skyy     | 200    | paid    |\n| 4  | Tony     | 800    | paid    |\n| 5  | Bruce    | 150    | paid    |\n\n---\n\n# Step 1 — GROUP BY Without HAVING\n\n```sql id=\"2x1m9v\"\nSELECT\n    customer,\n    SUM(amount) AS total_spent\nFROM orders\nGROUP BY customer;\n```\n\n---\n\n# Result\n\n| customer | total_spent |\n| -------- | ----------- |\n| Skyy     | 700         |\n| Bruce    | 450         |\n| Tony     | 800         |\n\n---\n\n# Problem\n\nSuppose we only want customers who spent more than:\n\n```txt id=\"6m1x2v\"\n500\n```\n\nWe need to filter GROUPS.\n\nThat’s what `HAVING` does.\n\n---\n\n# Basic HAVING Example\n\n```sql id=\"8x1m4q\"\nSELECT\n    customer,\n    SUM(amount) AS total_spent\nFROM orders\nGROUP BY customer\nHAVING SUM(amount) \u003e 500;\n```\n\n---\n\n# Result\n\n| customer | total_spent |\n| -------- | ----------- |\n| Skyy     | 700         |\n| Tony     | 800         |\n\nBruce excluded because:\n\n```txt id=\"7x2m1v\"\n450 \u003c= 500\n```\n\n---\n\n# What Happened Internally?\n\n---\n\n# Original Rows\n\n```txt id=\"0m1x8v\"\nSkyy  500\nBruce 300\nSkyy  200\nTony  800\nBruce 150\n```\n\n---\n\n# GROUP BY Creates Groups\n\n```txt id=\"1x2m9v\"\nSkyy  → [500, 200]\nBruce → [300, 150]\nTony  → [800]\n```\n\n---\n\n# Aggregates Run\n\n```txt id=\"5m1x2v\"\nSkyy  → 700\nBruce → 450\nTony  → 800\n```\n\n---\n\n# HAVING Filters Groups\n\n```txt id=\"3x1m8v\"\n700 \u003e 500 ✅\n450 \u003e 500 ❌\n800 \u003e 500 ✅\n```\n\nFinal result:\n\n* Skyy\n* Tony\n\n---\n\n# Biggest Beginner Mistake\n\nTrying to use aggregates in `WHERE`.\n\n---\n\n# WRONG\n\n```sql id=\"9x1m2v\"\nSELECT\n    customer,\n    SUM(amount)\nFROM orders\nWHERE SUM(amount) \u003e 500\nGROUP BY customer;\n```\n\n---\n\n# Why Wrong?\n\nBecause:\n\n* `WHERE` runs BEFORE grouping\n* `SUM(amount)` does not exist yet\n\nAt WHERE stage:\n\n* PostgreSQL still sees raw rows\n\nnot grouped totals.\n\n---\n\n# Correct\n\n```sql id=\"7m1x8q\"\nSELECT\n    customer,\n    SUM(amount)\nFROM orders\nGROUP BY customer\nHAVING SUM(amount) \u003e 500;\n```\n\n---\n\n# Key Difference\n\n| Clause   | Filters |\n| -------- | ------- |\n| `WHERE`  | rows    |\n| `HAVING` | groups  |\n\n---\n\n# WHERE vs HAVING Visually\n\n---\n\n# WHERE Example\n\n```sql id=\"2m1x9q\"\nSELECT *\nFROM orders\nWHERE amount \u003e 300;\n```\n\nFilters INDIVIDUAL rows.\n\n---\n\n# Result\n\n| customer | amount |\n| -------- | ------ |\n| Skyy     | 500    |\n| Tony     | 800    |\n\n---\n\n# HAVING Example\n\n```sql id=\"4x1m8q\"\nSELECT\n    customer,\n    SUM(amount)\nFROM orders\nGROUP BY customer\nHAVING SUM(amount) \u003e 300;\n```\n\nFilters GROUPS.\n\n---\n\n# Result\n\n| customer | total |\n| -------- | ----- |\n| Skyy     | 700   |\n| Bruce    | 450   |\n| Tony     | 800   |\n\nHuge conceptual difference.\n\n---\n\n# HAVING Without GROUP BY\n\nPossible, though less common.\n\n---\n\n# Example\n\n```sql id=\"8m2x1q\"\nSELECT COUNT(*)\nFROM orders\nHAVING COUNT(*) \u003e 3;\n```\n\n---\n\n# Meaning\n\nReturn result only if:\n\n* total row count exceeds 3\n\n---\n\n# HAVING with Multiple Conditions\n\n```sql id=\"1x9m2q\"\nSELECT\n    customer,\n    COUNT(*) AS total_orders,\n    SUM(amount) AS total_spent\nFROM orders\nGROUP BY customer\nHAVING\n    COUNT(*) \u003e= 2\n    AND SUM(amount) \u003e 400;\n```\n\n---\n\n# Result\n\n| customer | total_orders | total_spent |\n| -------- | ------------ | ----------- |\n| Skyy     | 2            | 700         |\n| Bruce    | 2            | 450         |\n\n---\n\n# HAVING + AVG()\n\nVery common.\n\n---\n\n# Example\n\n```sql id=\"5x2m1q\"\nSELECT\n    customer,\n    AVG(amount) AS avg_order\nFROM orders\nGROUP BY customer\nHAVING AVG(amount) \u003e 300;\n```\n\n---\n\n# Result\n\n| customer | avg_order |\n| -------- | --------- |\n| Skyy     | 350       |\n| Tony     | 800       |\n\n---\n\n# HAVING + JOIN\n\nExtremely common in backend systems.\n\n---\n\n# Example Tables\n\n## users\n\n| id | name  |\n| -- | ----- |\n| 1  | Skyy  |\n| 2  | Bruce |\n| 3  | Tony  |\n\n---\n\n## posts\n\n| id  | title  | user_id |\n| --- | ------ | ------- |\n| 101 | SQL    | 1       |\n| 102 | Go     | 1       |\n| 103 | Batman | 2       |\n\n---\n\n# Query\n\n```sql id=\"3m8x1q\"\nSELECT\n    users.name,\n    COUNT(posts.id) AS total_posts\nFROM users\nLEFT JOIN posts\nON users.id = posts.user_id\nGROUP BY users.name\nHAVING COUNT(posts.id) \u003e= 2;\n```\n\n---\n\n# Result\n\n| name | total_posts |\n| ---- | ----------- |\n| Skyy | 2           |\n\n---\n\n# Meaning\n\nFind users with:\n\n* at least 2 posts\n\nThis is a very real production query.\n\n---\n\n# HAVING + ORDER BY\n\nVery common.\n\n---\n\n# Example\n\n```sql id=\"6x1m9q\"\nSELECT\n    customer,\n    SUM(amount) AS total_spent\nFROM orders\nGROUP BY customer\nHAVING SUM(amount) \u003e 300\nORDER BY total_spent DESC;\n```\n\n---\n\n# Execution Flow\n\n```txt id=\"8x1m2v\"\n1. FROM\n2. GROUP BY\n3. SUM()\n4. HAVING\n5. ORDER BY\n```\n\n---\n\n# HAVING + DISTINCT\n\nExample:\n\n```sql id=\"4m1x8v\"\nSELECT\n    customer,\n    COUNT(DISTINCT status)\nFROM orders\nGROUP BY customer\nHAVING COUNT(DISTINCT status) \u003e 1;\n```\n\n---\n\n# Meaning\n\nFind customers having:\n\n* multiple different statuses\n\n---\n\n# Real Backend Examples\n\n---\n\n# Ecommerce\n\n```sql id=\"7x1m2q\"\nSELECT\n    customer_id,\n    SUM(amount)\nFROM orders\nGROUP BY customer_id\nHAVING SUM(amount) \u003e 10000;\n```\n\nVIP customers.\n\n---\n\n# Social Media\n\n```sql id=\"2x1m8q\"\nSELECT\n    user_id,\n    COUNT(*)\nFROM posts\nGROUP BY user_id\nHAVING COUNT(*) \u003e 100;\n```\n\nHighly active users.\n\n---\n\n# Analytics\n\n```sql id=\"9m1x2q\"\nSELECT\n    DATE(created_at),\n    COUNT(*)\nFROM signups\nGROUP BY DATE(created_at)\nHAVING COUNT(*) \u003e 500;\n```\n\nHigh signup days.\n\n---\n\n# SaaS Billing\n\n```sql id=\"5m2x1q\"\nSELECT\n    company_id,\n    SUM(invoice_total)\nFROM invoices\nGROUP BY company_id\nHAVING SUM(invoice_total) \u003e 50000;\n```\n\nLarge customers.\n\n---\n\n# Common Beginner Mistakes\n\n---\n\n# 1. Using WHERE Instead of HAVING\n\nMost common mistake.\n\n---\n\n# WRONG\n\n```sql id=\"1x8m2q\"\nWHERE COUNT(*) \u003e 5\n```\n\n---\n\n# Correct\n\n```sql id=\"8m1x2q\"\nHAVING COUNT(*) \u003e 5\n```\n\n---\n\n# 2. Forgetting GROUP BY\n\nIncorrect:\n\n```sql id=\"4x1m9q\"\nSELECT customer, SUM(amount)\nFROM orders\nHAVING SUM(amount) \u003e 500;\n```\n\nNeed:\n\n```sql id=\"7m2x1q\"\nGROUP BY customer\n```\n\n---\n\n# 3. Confusing Row Filtering vs Group Filtering\n\nHuge conceptual distinction.\n\n---\n\n# WHERE\n\nFilters:\n\n* rows\n\n---\n\n# HAVING\n\nFilters:\n\n* grouped summaries\n\n---\n\n# Most Important Mental Model\n\n`HAVING` is basically:\n\n```txt id=\"0x2m1v\"\nWHERE for grouped data\n```\n\nBut specifically:\n\n* AFTER aggregation\n* AFTER grouping\n\nThat’s why aggregate functions work inside:\n\n* `HAVING`\n\nbut not inside:\n\n* `WHERE`\n\n# Indexes in PostgreSQL — In Depth\n\nIndexes are one of the most important performance concepts in PostgreSQL.\n\nWithout indexes:\n\n* queries become slow\n* searches scan entire tables\n* joins become expensive\n* sorting becomes slower\n\nIndexes help PostgreSQL:\n\n* find data faster\n\nThey work similarly to:\n\n* an index in a book\n\n---\n\n# Real-World Analogy\n\nSuppose we have a 1000-page book.\n\nWithout an index:\n\n* we scan page-by-page\n\nWith an index:\n\n* we jump directly to the correct page\n\nDatabase indexes work similarly.\n\n---\n\n# Core Problem\n\nSuppose we have:\n\n```sql id=\"7x1m2q\"\nSELECT *\nFROM users\nWHERE email = 'skyy@gmail.com';\n```\n\nWithout an index:\n\n* PostgreSQL scans EVERY row\n\nThis is called:\n\n# Sequential Scan\n\n---\n\n# Sequential Scan\n\nPostgreSQL checks:\n\n```txt id=\"1x9m2v\"\nrow 1\nrow 2\nrow 3\n...\nrow 1,000,000\n```\n\nuntil it finds a match.\n\nVery slow on large tables.\n\n---\n\n# Index Solves This\n\nAn index creates a special optimized data structure.\n\nThen PostgreSQL can:\n\n* jump directly to matching rows\n\ninstead of scanning entire table.\n\n---\n\n# What an Index Actually Is\n\nAn index is a separate data structure stored by PostgreSQL.\n\nUsually based on:\n\n# B-Tree\n\n(default index type)\n\n---\n\n# Simplified Mental Model\n\nSuppose table:\n\n| id | email                             |\n| -- | --------------------------------- |\n| 1  | [a@gmail.com](mailto:a@gmail.com) |\n| 2  | [b@gmail.com](mailto:b@gmail.com) |\n| 3  | [c@gmail.com](mailto:c@gmail.com) |\n\nAn index on `email` might internally organize:\n\n```txt id=\"4m1x8v\"\na@gmail.com → row pointer\nb@gmail.com → row pointer\nc@gmail.com → row pointer\n```\n\nsorted efficiently.\n\nPostgreSQL can search this structure very quickly.\n\n---\n\n# Creating an Index\n\n---\n\n# Basic Syntax\n\n```sql id=\"2x1m9q\"\nCREATE INDEX index_name\nON table_name(column_name);\n```\n\n---\n\n# Example\n\n```sql id=\"5m2x1q\"\nCREATE INDEX idx_users_email\nON users(email);\n```\n\n---\n\n# Meaning\n\nCreate index:\n\n* named `idx_users_email`\n* on `users.email`\n\nNow queries filtering by email become much faster.\n\n---\n\n# Why Naming Matters\n\nConvention:\n\n```txt id=\"7m1x2v\"\nidx_\u003ctable\u003e_\u003ccolumn\u003e\n```\n\nExample:\n\n```txt id=\"1x8m2v\"\nidx_posts_user_id\nidx_orders_created_at\n```\n\nKeeps schema readable.\n\n---\n\n# Most Commonly Indexed Columns\n\n| Column Type      | Why               |\n| ---------------- | ----------------- |\n| Primary keys     | heavily searched  |\n| Foreign keys     | joins             |\n| Emails/usernames | lookups           |\n| created_at       | sorting/filtering |\n| status           | filtering         |\n| category_id      | relationships     |\n\n---\n\n# Primary Keys Automatically Create Indexes\n\nExample:\n\n```sql id=\"8x1m2q\"\nid SERIAL PRIMARY KEY\n```\n\nautomatically creates:\n\n* unique index\n\nNo need to manually create one.\n\n---\n\n# UNIQUE Also Creates Index\n\nExample:\n\n```sql id=\"4x1m9q\"\nemail TEXT UNIQUE\n```\n\nautomatically creates:\n\n* unique index\n\nbecause uniqueness must be enforced efficiently.\n\n---\n\n# How Indexes Improve WHERE\n\n---\n\n# Without Index\n\n```sql id=\"6m1x2q\"\nSELECT *\nFROM users\nWHERE email='skyy@gmail.com';\n```\n\nPostgreSQL:\n\n* scans entire table\n\n---\n\n# With Index\n\nPostgreSQL:\n\n* jumps directly to matching row\n\nMassive speed difference.\n\n---\n\n# Indexes and JOINs\n\nExtremely important.\n\n---\n\n# Example\n\n```sql id=\"9m1x2q\"\nSELECT *\nFROM posts\nINNER JOIN users\nON posts.user_id = users.id;\n```\n\n---\n\n# Important Indexed Columns\n\n```txt id=\"2x1m8v\"\nusers.id\nposts.user_id\n```\n\nWhy?\n\nBecause joins constantly compare them.\n\nWithout indexes:\n\n* joins become expensive on large datasets\n\n---\n\n# Indexes and ORDER BY\n\nIndexes can help sorting too.\n\n---\n\n# Example\n\n```sql id=\"3m8x1q\"\nSELECT *\nFROM posts\nORDER BY created_at DESC;\n```\n\nIf indexed:\n\n```sql id=\"5x1m2q\"\nCREATE INDEX idx_posts_created_at\nON posts(created_at);\n```\n\nsorting becomes faster.\n\n---\n\n# Indexes and Range Queries\n\n---\n\n# Example\n\n```sql id=\"7x2m1q\"\nSELECT *\nFROM orders\nWHERE amount \u003e 500;\n```\n\nIndexes help:\n\n* range filtering\n* comparisons\n* BETWEEN queries\n\n---\n\n# B-Tree Index\n\nDefault PostgreSQL index type.\n\n---\n\n# Syntax\n\n```sql id=\"1x2m9q\"\nCREATE INDEX idx_name\nON table(column);\n```\n\nimplicitly creates:\n\n* B-tree index\n\n---\n\n# Best For\n\n| Operation  | Supported |\n| ---------- | --------- |\n| `=`        | yes       |\n| `\u003c` `\u003e`    | yes       |\n| `BETWEEN`  | yes       |\n| `ORDER BY` | yes       |\n\nMost common/general-purpose index.\n\n---\n\n# Composite Indexes (Multi-Column)\n\nVery important.\n\n---\n\n# Example\n\n```sql id=\"8m1x2q\"\nCREATE INDEX idx_orders_customer_status\nON orders(customer_id, status);\n```\n\n---\n\n# Meaning\n\nIndex stores BOTH columns together.\n\nUseful for queries like:\n\n```sql id=\"4m1x8q\"\nSELECT *\nFROM orders\nWHERE customer_id = 1\nAND status = 'paid';\n```\n\n---\n\n# Column Order Matters\n\nHuge concept.\n\n---\n\n# Example Index\n\n```sql id=\"6x1m2q\"\n(customer_id, status)\n```\n\nworks well for:\n\n```sql id=\"9x1m2v\"\nWHERE customer_id = ?\n```\n\nand:\n\n```sql id=\"0x2m1v\"\nWHERE customer_id = ?\nAND status = ?\n```\n\nBUT NOT great for:\n\n```sql id=\"5m1x2v\"\nWHERE status = ?\n```\n\nbecause leftmost column matters.\n\n---\n\n# Unique Index\n\nEnforces uniqueness.\n\n---\n\n# Example\n\n```sql id=\"2m8x1q\"\nCREATE UNIQUE INDEX idx_users_email\nON users(email);\n```\n\nNow duplicate emails impossible.\n\n---\n\n# Partial Indexes\n\nVery powerful PostgreSQL feature.\n\n---\n\n# Example\n\n```sql id=\"1m9x2q\"\nCREATE INDEX idx_active_users\nON users(email)\nWHERE is_active = true;\n```\n\n---\n\n# Meaning\n\nIndex only stores:\n\n* active users\n\nSmaller + faster.\n\n---\n\n# Useful When\n\nMost queries target:\n\n* subset of rows\n\n---\n\n# Expression Indexes\n\nIndexes based on expressions.\n\n---\n\n# Example\n\n```sql id=\"3x1m8q\"\nCREATE INDEX idx_lower_email\nON users(LOWER(email));\n```\n\nUseful for:\n\n```sql id=\"8x1m2q\"\nSELECT *\nFROM users\nWHERE LOWER(email)='skyy@gmail.com';\n```\n\n---\n\n# Without expression index:\n\n* PostgreSQL may ignore normal email index\n\n---\n\n# Hash Index\n\nOptimized mainly for:\n\n```txt id=\"1x2m8v\"\n=\n```\n\ncomparisons.\n\nLess common than B-tree.\n\n---\n\n# GIN Index\n\nVery important PostgreSQL feature.\n\nUsed heavily for:\n\n* JSONB\n* arrays\n* full-text search\n\n---\n\n# Example\n\n```sql id=\"5x2m1q\"\nCREATE INDEX idx_metadata\nON app_events\nUSING GIN(metadata);\n```\n\nUseful for JSONB queries.\n\n---\n\n# Example Query\n\n```sql id=\"7m1x2q\"\nSELECT *\nFROM app_events\nWHERE metadata ? 'browser';\n```\n\nGIN makes this much faster.\n\n---\n\n# BRIN Index\n\nUsed for:\n\n* huge tables\n* sequentially ordered data\n\nVery storage-efficient.\n\nCommon for:\n\n* logs\n* analytics\n* time-series data\n\n---\n\n# Viewing Indexes\n\n---\n\n# Query\n\n```sql id=\"9m2x1q\"\n\\d table_name\n```\n\nShows:\n\n* indexes\n* constraints\n* schema info\n\n---\n\n# Dropping Indexes\n\n---\n\n# Syntax\n\n```sql id=\"4x1m8q\"\nDROP INDEX idx_users_email;\n```\n\n---\n\n# EXPLAIN — Seeing Query Plans\n\nExtremely important.\n\n---\n\n# Example\n\n```sql id=\"2x1m9q\"\nEXPLAIN\nSELECT *\nFROM users\nWHERE email='skyy@gmail.com';\n```\n\n---\n\n# Without Index\n\nWe may see:\n\n```txt id=\"6m1x2v\"\nSeq Scan\n```\n\n---\n\n# With Index\n\nWe may see:\n\n```txt id=\"1x9m2v\"\nIndex Scan\n```\n\nMeaning PostgreSQL used index.\n\n---\n\n# Indexes Are NOT Free\n\nVery important.\n\nIndexes improve reads BUT hurt writes.\n\n---\n\n# Why?\n\nEvery:\n\n* INSERT\n* UPDATE\n* DELETE\n\nmust also update indexes.\n\n---\n\n# Tradeoff\n\n| Operation | Effect |\n| --------- | ------ |\n| SELECT    | faster |\n| INSERT    | slower |\n| UPDATE    | slower |\n| DELETE    | slower |\n\nToo many indexes hurt performance.\n\n---\n\n# Storage Cost\n\nIndexes consume disk space.\n\nLarge tables:\n\n* large indexes\n\n---\n\n# When NOT to Index\n\n---\n\n# Small Tables\n\nSequential scan may actually be faster.\n\n---\n\n# Low Selectivity Columns\n\nExample:\n\n```txt id=\"4m1x2v\"\nis_active = true/false\n```\n\nOnly 2 values.\n\nIndex may not help much.\n\n---\n\n# Frequently Updated Columns\n\nCan cause heavy maintenance cost.\n\n---\n\n# Real Backend Examples\n\n---\n\n# User Login\n\n```sql id=\"8x1m2q\"\nWHERE email = ?\n```\n\nIndex email.\n\n---\n\n# Social Media Feed\n\n```sql id=\"5m2x1q\"\nORDER BY created_at DESC\n```\n\nIndex created_at.\n\n---\n\n# Ecommerce\n\n```sql id=\"9x1m2q\"\nWHERE category_id = ?\n```\n\nIndex foreign keys.\n\n---\n\n# Analytics\n\n```sql id=\"3x1m8v\"\nWHERE created_at BETWEEN ...\n```\n\nIndex timestamps.\n\n---\n\n# Most Common Beginner Mistakes\n\n---\n\n# 1. Indexing Everything\n\nBad idea.\n\nToo many indexes:\n\n* slow writes\n* waste storage\n\n---\n\n# 2. Forgetting Foreign Key Indexes\n\nHuge performance issue in joins.\n\n---\n\n# 3. Ignoring Composite Index Order\n\nOrder matters greatly.\n\n---\n\n# 4. Assuming Index Always Used\n\nPostgreSQL query planner decides.\n\nSometimes sequential scan faster.\n\n---\n\n# 5. Not Using EXPLAIN\n\nEssential performance tool.\n\n---\n\n# Most Important Mental Model\n\nIndexes are basically:\n\n```txt id=\"7m1x2v\"\noptimized lookup structures\n```\n\nthat help PostgreSQL:\n\n* avoid scanning entire tables\n\nThey are critical for:\n\n* scalable applications\n* fast queries\n* efficient joins\n* analytics systems\n* production databases\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fiamskyy666%2Fpostgresql-resources","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fiamskyy666%2Fpostgresql-resources","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fiamskyy666%2Fpostgresql-resources/lists"}