{"id":2836,"date":"2026-05-06T01:18:23","date_gmt":"2026-05-06T01:18:23","guid":{"rendered":"https:\/\/sreschool.com\/blog\/?p=2836"},"modified":"2026-05-06T01:18:24","modified_gmt":"2026-05-06T01:18:24","slug":"mysql-vs-postgresql-comprehensive-guide-for-mysql-users-learning-postgresql","status":"publish","type":"post","link":"https:\/\/sreschool.com\/blog\/mysql-vs-postgresql-comprehensive-guide-for-mysql-users-learning-postgresql\/","title":{"rendered":"MySQL vs PostgreSQL: Comprehensive Guide for MySQL Users Learning PostgreSQL"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">Introduction<\/h2>\n\n\n\n<p>If you already know MySQL well, learning PostgreSQL becomes much easier when you stop trying to memorize PostgreSQL from zero and instead ask:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>\u201cWhat is the PostgreSQL equivalent of the thing I already know in MySQL?\u201d<\/p>\n<\/blockquote>\n\n\n\n<p>That is exactly how this guide is structured.<\/p>\n\n\n\n<p>We will compare <strong>MySQL and PostgreSQL components, resources, architecture, commands, tools, users, roles, schemas, storage, logs, backup, replication, monitoring, performance, and SQL syntax<\/strong>.<\/p>\n\n\n\n<p>The goal is not just to say:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>MySQL thing = PostgreSQL thing\n<\/code><\/pre>\n\n\n\n<p>The goal is to understand:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>What does it mean?\nHow does it behave?\nHow should I think about it as a MySQL user?\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">1. Big Picture Mental Model<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL Mental Model<\/h2>\n\n\n\n<p>In MySQL, you usually think like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>MySQL Server\n \u251c\u2500\u2500 Database \/ Schema\n \u2502    \u251c\u2500\u2500 Tables\n \u2502    \u251c\u2500\u2500 Views\n \u2502    \u251c\u2500\u2500 Procedures\n \u2502    \u2514\u2500\u2500 Triggers\n \u251c\u2500\u2500 Users\n \u251c\u2500\u2500 Privileges\n \u251c\u2500\u2500 Storage Engines\n \u2502    \u251c\u2500\u2500 InnoDB\n \u2502    \u251c\u2500\u2500 MyISAM\n \u2502    \u2514\u2500\u2500 MEMORY\n \u2514\u2500\u2500 Logs\n      \u251c\u2500\u2500 Error log\n      \u251c\u2500\u2500 Binary log\n      \u251c\u2500\u2500 Slow query log\n      \u2514\u2500\u2500 General log\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL Mental Model<\/h2>\n\n\n\n<p>In PostgreSQL, think like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>PostgreSQL Server \/ Cluster\n \u251c\u2500\u2500 Databases\n \u2502    \u251c\u2500\u2500 Schemas\n \u2502    \u2502    \u251c\u2500\u2500 Tables\n \u2502    \u2502    \u251c\u2500\u2500 Views\n \u2502    \u2502    \u251c\u2500\u2500 Functions\n \u2502    \u2502    \u251c\u2500\u2500 Sequences\n \u2502    \u2502    \u2514\u2500\u2500 Types\n \u251c\u2500\u2500 Roles\n \u2502    \u251c\u2500\u2500 Login roles\n \u2502    \u2514\u2500\u2500 Group roles\n \u251c\u2500\u2500 Tablespaces\n \u251c\u2500\u2500 WAL\n \u251c\u2500\u2500 Background processes\n \u2514\u2500\u2500 Extensions\n<\/code><\/pre>\n\n\n\n<p>The most important difference:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>MySQL:\nDatabase and schema usually mean the same thing.\n\nPostgreSQL:\nA database contains schemas.\nA schema contains tables.\n<\/code><\/pre>\n\n\n\n<p>So:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>MySQL:\nserver \u2192 database \u2192 table\n\nPostgreSQL:\nserver\/cluster \u2192 database \u2192 schema \u2192 table\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">2. Core Component Mapping<\/h1>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>MySQL Component<\/th><th>PostgreSQL Equivalent<\/th><th>Meaning<\/th><\/tr><\/thead><tbody><tr><td>MySQL server<\/td><td>PostgreSQL server \/ cluster<\/td><td>Running database service<\/td><\/tr><tr><td>Database<\/td><td>Database<\/td><td>Logical database container<\/td><\/tr><tr><td>Schema<\/td><td>Schema inside database<\/td><td>Namespace inside a database<\/td><\/tr><tr><td>Table<\/td><td>Table<\/td><td>Stores rows and columns<\/td><\/tr><tr><td>View<\/td><td>View<\/td><td>Saved query<\/td><\/tr><tr><td>Materialized view<\/td><td>Materialized view<\/td><td>Stored query result<\/td><\/tr><tr><td>User<\/td><td>Role with <code>LOGIN<\/code><\/td><td>PostgreSQL users are login roles<\/td><\/tr><tr><td>Group<\/td><td>Role without <code>LOGIN<\/code><\/td><td>Used to group permissions<\/td><\/tr><tr><td>Privilege<\/td><td>Privilege<\/td><td>Controls access<\/td><\/tr><tr><td>Storage engine<\/td><td>PostgreSQL storage system<\/td><td>PostgreSQL does not use engines like MySQL<\/td><\/tr><tr><td>InnoDB redo log<\/td><td>WAL<\/td><td>Write-Ahead Log<\/td><\/tr><tr><td>Binary log<\/td><td>WAL \/ logical replication<\/td><td>Used for recovery and replication<\/td><\/tr><tr><td>AUTO_INCREMENT<\/td><td>Identity \/ sequence<\/td><td>Auto-generated numbers<\/td><\/tr><tr><td>Performance Schema<\/td><td><code>pg_stat_*<\/code> views<\/td><td>Monitoring and metrics<\/td><\/tr><tr><td><code>my.cnf<\/code><\/td><td><code>postgresql.conf<\/code><\/td><td>Main config<\/td><\/tr><tr><td>User host grants<\/td><td><code>pg_hba.conf<\/code><\/td><td>Client authentication rules<\/td><\/tr><tr><td><code>mysqldump<\/code><\/td><td><code>pg_dump<\/code><\/td><td>Backup tool<\/td><\/tr><tr><td><code>mysql<\/code> CLI<\/td><td><code>psql<\/code><\/td><td>Command-line client<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">3. Server, Instance, and Cluster<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL<\/h2>\n\n\n\n<p>In MySQL, you usually say:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>MySQL instance\nMySQL server\nmysqld process\n<\/code><\/pre>\n\n\n\n<p>A MySQL instance usually means one running MySQL server process using one data directory.<\/p>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>mysqld\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL<\/h2>\n\n\n\n<p>PostgreSQL commonly uses the word <strong>cluster<\/strong>.<\/p>\n\n\n\n<p>A PostgreSQL cluster means:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>One PostgreSQL data directory initialized by initdb\n<\/code><\/pre>\n\n\n\n<p>It contains:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Databases\nRoles\nWAL\nSystem catalogs\nConfiguration\n<\/code><\/pre>\n\n\n\n<p>Important: in PostgreSQL, a <strong>cluster does not mean multiple servers<\/strong> in this context.<\/p>\n\n\n\n<p>That confuses many people.<\/p>\n\n\n\n<p>In PostgreSQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Cluster = one initialized PostgreSQL data directory\n<\/code><\/pre>\n\n\n\n<p>Example data directory:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/var\/lib\/postgresql\/16\/main\n<\/code><\/pre>\n\n\n\n<p>or:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/var\/lib\/pgsql\/data\n<\/code><\/pre>\n\n\n\n<p>depending on Linux distribution.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">4. Database and Schema Mapping<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL Database<\/h2>\n\n\n\n<p>In MySQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE DATABASE shop;\nUSE shop;\nCREATE TABLE customers (\n    id INT AUTO_INCREMENT PRIMARY KEY,\n    name VARCHAR(100)\n);\n<\/code><\/pre>\n\n\n\n<p>Here, <code>shop<\/code> is the database.<\/p>\n\n\n\n<p>Many MySQL people also call this a schema.<\/p>\n\n\n\n<p>So in MySQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Database \u2248 Schema\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL Database<\/h2>\n\n\n\n<p>In PostgreSQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE DATABASE shop;\n<\/code><\/pre>\n\n\n\n<p>Then connect to it:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\\c shop\n<\/code><\/pre>\n\n\n\n<p>Inside that database, you usually have a schema called:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>public\n<\/code><\/pre>\n\n\n\n<p>Then you create tables inside the schema:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE TABLE public.customers (\n    id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,\n    name VARCHAR(100)\n);\n<\/code><\/pre>\n\n\n\n<p>So in PostgreSQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Database contains schemas.\nSchema contains tables.\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL Object Path<\/h2>\n\n\n\n<p>A full table name in PostgreSQL can look like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>database.schema.table\n<\/code><\/pre>\n\n\n\n<p>But inside SQL queries, you usually use:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT * FROM public.customers;\n<\/code><\/pre>\n\n\n\n<p>You do not usually write the database name in front of the table because you are already connected to one database.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Important Difference<\/h2>\n\n\n\n<p>In MySQL, you can commonly do:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT *\nFROM db1.customers c\nJOIN db2.orders o ON c.id = o.customer_id;\n<\/code><\/pre>\n\n\n\n<p>In PostgreSQL, cross-database queries are not naturally done this way. You usually work inside one database and use multiple schemas:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>shop database\n \u251c\u2500\u2500 sales.customers\n \u251c\u2500\u2500 sales.orders\n \u251c\u2500\u2500 accounting.invoices\n \u2514\u2500\u2500 audit.events\n<\/code><\/pre>\n\n\n\n<p>PostgreSQL encourages this style:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT *\nFROM sales.customers c\nJOIN accounting.invoices i ON c.id = i.customer_id;\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">5. System Databases and System Schemas<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL System Databases<\/h2>\n\n\n\n<p>Common MySQL system databases:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>MySQL Database<\/th><th>Purpose<\/th><\/tr><\/thead><tbody><tr><td><code>mysql<\/code><\/td><td>Users, privileges, system metadata<\/td><\/tr><tr><td><code>information_schema<\/code><\/td><td>SQL-standard metadata views<\/td><\/tr><tr><td><code>performance_schema<\/code><\/td><td>Performance and runtime metrics<\/td><\/tr><tr><td><code>sys<\/code><\/td><td>Friendly views over performance schema<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>You may see:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SHOW DATABASES;\n<\/code><\/pre>\n\n\n\n<p>Output:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>information_schema\nmysql\nperformance_schema\nsys\nmyapp\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL System Databases and Schemas<\/h2>\n\n\n\n<p>PostgreSQL has special databases and schemas.<\/p>\n\n\n\n<p>Common PostgreSQL databases:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>PostgreSQL Database<\/th><th>Purpose<\/th><\/tr><\/thead><tbody><tr><td><code>postgres<\/code><\/td><td>Default administrative database<\/td><\/tr><tr><td><code>template0<\/code><\/td><td>Clean database template<\/td><\/tr><tr><td><code>template1<\/code><\/td><td>Default template for new databases<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>Common PostgreSQL schemas:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>PostgreSQL Schema<\/th><th>Purpose<\/th><\/tr><\/thead><tbody><tr><td><code>pg_catalog<\/code><\/td><td>PostgreSQL system catalog<\/td><\/tr><tr><td><code>information_schema<\/code><\/td><td>SQL-standard metadata views<\/td><\/tr><tr><td><code>public<\/code><\/td><td>Default user schema<\/td><\/tr><tr><td><code>pg_toast<\/code><\/td><td>Internal storage for large values<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>In PostgreSQL, system metadata mostly lives in:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>pg_catalog\n<\/code><\/pre>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT *\nFROM pg_catalog.pg_tables;\n<\/code><\/pre>\n\n\n\n<p>But as a beginner, you often use <code>psql<\/code> commands:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\\l\n\\dt\n\\d table_name\n\\du\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">6. Users vs Roles<\/h1>\n\n\n\n<p>This is one of the biggest differences.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL User<\/h2>\n\n\n\n<p>In MySQL, a user is identified by username and host:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>'root'@'localhost'\n'appuser'@'%'\n'appuser'@'192.168.1.%'\n<\/code><\/pre>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE USER 'appuser'@'localhost' IDENTIFIED BY 'secret';\nGRANT SELECT ON shop.* TO 'appuser'@'localhost';\n<\/code><\/pre>\n\n\n\n<p>In MySQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>User identity = username + host\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL Role<\/h2>\n\n\n\n<p>PostgreSQL has <strong>roles<\/strong>.<\/p>\n\n\n\n<p>A role can be:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>A user\nA group\nBoth\n<\/code><\/pre>\n\n\n\n<p>A PostgreSQL \u201cuser\u201d is simply:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>A role with LOGIN permission\n<\/code><\/pre>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE ROLE appuser WITH LOGIN PASSWORD 'secret';\n<\/code><\/pre>\n\n\n\n<p>This is equivalent to:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE USER appuser WITH PASSWORD 'secret';\n<\/code><\/pre>\n\n\n\n<p>Because:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE USER\n<\/code><\/pre>\n\n\n\n<p>is shorthand for:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE ROLE ... LOGIN\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL Group-Like Role<\/h2>\n\n\n\n<p>Create a role that cannot log in:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE ROLE readonly;\n<\/code><\/pre>\n\n\n\n<p>Give permissions to that group role:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>GRANT CONNECT ON DATABASE shop TO readonly;\nGRANT USAGE ON SCHEMA public TO readonly;\nGRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly;\n<\/code><\/pre>\n\n\n\n<p>Assign it to a login role:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>GRANT readonly TO appuser;\n<\/code><\/pre>\n\n\n\n<p>Now <code>appuser<\/code> inherits permissions from <code>readonly<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL vs PostgreSQL User Mapping<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>MySQL<\/th><th>PostgreSQL<\/th><\/tr><\/thead><tbody><tr><td>User<\/td><td>Role with <code>LOGIN<\/code><\/td><\/tr><tr><td>Group of permissions<\/td><td>Role without <code>LOGIN<\/code><\/td><\/tr><tr><td><code>'user'@'host'<\/code><\/td><td>Role + <code>pg_hba.conf<\/code><\/td><\/tr><tr><td><code>CREATE USER<\/code><\/td><td><code>CREATE ROLE ... LOGIN<\/code><\/td><\/tr><tr><td><code>GRANT ON db.*<\/code><\/td><td><code>GRANT ON DATABASE<\/code>, <code>SCHEMA<\/code>, <code>TABLES<\/code>, <code>SEQUENCES<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Important PostgreSQL Access Layers<\/h2>\n\n\n\n<p>PostgreSQL access usually has four layers:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>1. Can the client reach the server?\n2. Does pg_hba.conf allow this connection?\n3. Can the role connect to the database?\n4. Does the role have privileges on schema\/table\/sequence?\n<\/code><\/pre>\n\n\n\n<p>This surprises MySQL users because MySQL grants often feel more direct.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">7. Authentication Mapping<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL Authentication<\/h2>\n\n\n\n<p>MySQL controls user access through user accounts like:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>'appuser'@'localhost'\n'appuser'@'%'\n<\/code><\/pre>\n\n\n\n<p>Examples:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE USER 'appuser'@'%' IDENTIFIED BY 'secret';\nGRANT SELECT ON shop.* TO 'appuser'@'%';\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL Authentication<\/h2>\n\n\n\n<p>PostgreSQL separates:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Who you are       \u2192 role\nCan you log in    \u2192 LOGIN attribute\nWhere from\/how    \u2192 pg_hba.conf\nWhat you can do   \u2192 GRANT privileges\n<\/code><\/pre>\n\n\n\n<p>The file:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>pg_hba.conf\n<\/code><\/pre>\n\n\n\n<p>controls connection rules.<\/p>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>host    shop    appuser    192.168.1.0\/24    scram-sha-256\n<\/code><\/pre>\n\n\n\n<p>Meaning:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Allow appuser to connect to database shop\nfrom 192.168.1.x\nusing password authentication.\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Common PostgreSQL Authentication Methods<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Method<\/th><th>Meaning<\/th><\/tr><\/thead><tbody><tr><td><code>trust<\/code><\/td><td>No password required<\/td><\/tr><tr><td><code>peer<\/code><\/td><td>OS user must match database role<\/td><\/tr><tr><td><code>md5<\/code><\/td><td>Password authentication<\/td><\/tr><tr><td><code>scram-sha-256<\/code><\/td><td>Stronger password authentication<\/td><\/tr><tr><td><code>cert<\/code><\/td><td>Client certificate authentication<\/td><\/tr><tr><td><code>ldap<\/code><\/td><td>LDAP authentication<\/td><\/tr><tr><td><code>pam<\/code><\/td><td>PAM authentication<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>For modern PostgreSQL, <code>scram-sha-256<\/code> is commonly preferred over <code>md5<\/code>.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">8. Privileges and Grants<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL Grant Style<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>GRANT SELECT, INSERT, UPDATE\nON shop.*\nTO 'appuser'@'localhost';\n<\/code><\/pre>\n\n\n\n<p>This grants privileges on all tables in database <code>shop<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL Grant Style<\/h2>\n\n\n\n<p>PostgreSQL is more layered.<\/p>\n\n\n\n<p>You may need:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>GRANT CONNECT ON DATABASE shop TO appuser;\nGRANT USAGE ON SCHEMA public TO appuser;\nGRANT SELECT, INSERT, UPDATE ON ALL TABLES IN SCHEMA public TO appuser;\nGRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO appuser;\n<\/code><\/pre>\n\n\n\n<p>Why sequences?<\/p>\n\n\n\n<p>Because PostgreSQL auto-increment values are often backed by sequence objects.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL Default Privileges<\/h2>\n\n\n\n<p>If you want future tables to automatically get permissions:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ALTER DEFAULT PRIVILEGES IN SCHEMA public\nGRANT SELECT, INSERT, UPDATE ON TABLES TO appuser;\n<\/code><\/pre>\n\n\n\n<p>For future sequences:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ALTER DEFAULT PRIVILEGES IN SCHEMA public\nGRANT USAGE, SELECT ON SEQUENCES TO appuser;\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Permission Mapping<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>MySQL Permission<\/th><th>PostgreSQL Equivalent<\/th><\/tr><\/thead><tbody><tr><td><code>SELECT<\/code><\/td><td><code>SELECT<\/code><\/td><\/tr><tr><td><code>INSERT<\/code><\/td><td><code>INSERT<\/code><\/td><\/tr><tr><td><code>UPDATE<\/code><\/td><td><code>UPDATE<\/code><\/td><\/tr><tr><td><code>DELETE<\/code><\/td><td><code>DELETE<\/code><\/td><\/tr><tr><td><code>CREATE<\/code><\/td><td><code>CREATE<\/code><\/td><\/tr><tr><td><code>DROP<\/code><\/td><td>object ownership \/ <code>DROP<\/code> as owner<\/td><\/tr><tr><td><code>ALTER<\/code><\/td><td>object ownership \/ specific privileges<\/td><\/tr><tr><td><code>INDEX<\/code><\/td><td>usually table ownership<\/td><\/tr><tr><td><code>EXECUTE<\/code><\/td><td><code>EXECUTE<\/code> on functions<\/td><\/tr><tr><td><code>USAGE<\/code><\/td><td>schema\/sequence\/type usage<\/td><\/tr><tr><td><code>ALL PRIVILEGES<\/code><\/td><td><code>ALL PRIVILEGES<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">9. Storage Engine vs PostgreSQL Storage<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL Storage Engines<\/h2>\n\n\n\n<p>MySQL supports multiple storage engines:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Engine<\/th><th>Meaning<\/th><\/tr><\/thead><tbody><tr><td>InnoDB<\/td><td>Default transactional engine<\/td><\/tr><tr><td>MyISAM<\/td><td>Older non-transactional engine<\/td><\/tr><tr><td>MEMORY<\/td><td>Stores data in memory<\/td><\/tr><tr><td>CSV<\/td><td>Stores data in CSV files<\/td><\/tr><tr><td>ARCHIVE<\/td><td>Compressed archival storage<\/td><\/tr><tr><td>FEDERATED<\/td><td>Remote table access<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>Most production MySQL systems use:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>InnoDB\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL Storage<\/h2>\n\n\n\n<p>PostgreSQL does not work the same way.<\/p>\n\n\n\n<p>You do not normally choose:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ENGINE=InnoDB\n<\/code><\/pre>\n\n\n\n<p>There is no typical equivalent of:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE TABLE t (...) ENGINE=InnoDB;\n<\/code><\/pre>\n\n\n\n<p>In PostgreSQL, table storage is native.<\/p>\n\n\n\n<p>You choose table types instead:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>PostgreSQL Table Type<\/th><th>Meaning<\/th><\/tr><\/thead><tbody><tr><td>Regular table<\/td><td>Normal persistent table<\/td><\/tr><tr><td>Temporary table<\/td><td>Session-local temporary table<\/td><\/tr><tr><td>Unlogged table<\/td><td>Faster table, not WAL-logged<\/td><\/tr><tr><td>Partitioned table<\/td><td>Logical parent table with partitions<\/td><\/tr><tr><td>Foreign table<\/td><td>Table backed by external data source<\/td><\/tr><tr><td>Materialized view<\/td><td>Stored result of a query<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>Example regular table:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE TABLE customers (\n    id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,\n    name TEXT NOT NULL\n);\n<\/code><\/pre>\n\n\n\n<p>Example unlogged table:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE UNLOGGED TABLE staging_imports (\n    id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,\n    payload JSONB\n);\n<\/code><\/pre>\n\n\n\n<p>Unlogged tables are faster for writes, but they are not crash-safe in the same way regular tables are.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">10. Tablespaces<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL<\/h2>\n\n\n\n<p>In MySQL\/InnoDB, tablespaces exist, but many users mostly deal with:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ibdata files\n.ibd files\ndatadir\n<\/code><\/pre>\n\n\n\n<p>With <code>innodb_file_per_table<\/code>, each table can have its own <code>.ibd<\/code> file.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL<\/h2>\n\n\n\n<p>PostgreSQL has <strong>tablespaces<\/strong> too.<\/p>\n\n\n\n<p>A tablespace lets you place database objects in a different filesystem location.<\/p>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE TABLESPACE fastspace\nLOCATION '\/mnt\/fastdisk\/postgres';\n<\/code><\/pre>\n\n\n\n<p>Create table in that tablespace:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE TABLE big_events (\n    id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,\n    event_data JSONB\n) TABLESPACE fastspace;\n<\/code><\/pre>\n\n\n\n<p>Mental model:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Tablespace = storage location for database objects\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">11. Auto Increment vs Sequence \/ Identity<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL AUTO_INCREMENT<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE TABLE users (\n    id BIGINT AUTO_INCREMENT PRIMARY KEY,\n    name VARCHAR(100)\n);\n<\/code><\/pre>\n\n\n\n<p>Insert:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>INSERT INTO users (name) VALUES ('Alice');\n<\/code><\/pre>\n\n\n\n<p>MySQL automatically generates <code>id<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL Identity<\/h2>\n\n\n\n<p>Modern PostgreSQL style:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE TABLE users (\n    id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,\n    name VARCHAR(100)\n);\n<\/code><\/pre>\n\n\n\n<p>Another style:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE TABLE users (\n    id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,\n    name VARCHAR(100)\n);\n<\/code><\/pre>\n\n\n\n<p>Difference:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>PostgreSQL Identity Type<\/th><th>Meaning<\/th><\/tr><\/thead><tbody><tr><td><code>GENERATED ALWAYS<\/code><\/td><td>PostgreSQL always generates value unless explicitly overridden<\/td><\/tr><tr><td><code>GENERATED BY DEFAULT<\/code><\/td><td>PostgreSQL generates value if you do not provide one<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>Older PostgreSQL style:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE TABLE users (\n    id SERIAL PRIMARY KEY,\n    name VARCHAR(100)\n);\n<\/code><\/pre>\n\n\n\n<p><code>SERIAL<\/code> still works, but identity columns are the modern SQL-standard style.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL Sequence<\/h2>\n\n\n\n<p>PostgreSQL identity and serial columns use sequences.<\/p>\n\n\n\n<p>A sequence is an independent object that generates numbers.<\/p>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE SEQUENCE order_id_seq;\n<\/code><\/pre>\n\n\n\n<p>Get next value:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT nextval('order_id_seq');\n<\/code><\/pre>\n\n\n\n<p>Important:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>PostgreSQL sequences are not rolled back.\n<\/code><\/pre>\n\n\n\n<p>If a transaction gets a sequence number and then rolls back, that number may be skipped.<\/p>\n\n\n\n<p>That is normal.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">12. Data Type Mapping<\/h1>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>MySQL Type<\/th><th>PostgreSQL Type<\/th><th>Notes<\/th><\/tr><\/thead><tbody><tr><td><code>TINYINT<\/code><\/td><td><code>SMALLINT<\/code><\/td><td>PostgreSQL has no <code>TINYINT<\/code><\/td><\/tr><tr><td><code>TINYINT(1)<\/code><\/td><td><code>BOOLEAN<\/code><\/td><td>Common MySQL boolean conversion<\/td><\/tr><tr><td><code>SMALLINT<\/code><\/td><td><code>SMALLINT<\/code><\/td><td>Same<\/td><\/tr><tr><td><code>INT<\/code><\/td><td><code>INTEGER<\/code> \/ <code>INT<\/code><\/td><td>Same<\/td><\/tr><tr><td><code>BIGINT<\/code><\/td><td><code>BIGINT<\/code><\/td><td>Same<\/td><\/tr><tr><td><code>DECIMAL<\/code><\/td><td><code>NUMERIC<\/code> \/ <code>DECIMAL<\/code><\/td><td>Same purpose<\/td><\/tr><tr><td><code>FLOAT<\/code><\/td><td><code>REAL<\/code><\/td><td>4-byte floating number<\/td><\/tr><tr><td><code>DOUBLE<\/code><\/td><td><code>DOUBLE PRECISION<\/code><\/td><td>8-byte floating number<\/td><\/tr><tr><td><code>CHAR(n)<\/code><\/td><td><code>CHAR(n)<\/code><\/td><td>Same<\/td><\/tr><tr><td><code>VARCHAR(n)<\/code><\/td><td><code>VARCHAR(n)<\/code><\/td><td>Same<\/td><\/tr><tr><td><code>TEXT<\/code><\/td><td><code>TEXT<\/code><\/td><td>PostgreSQL uses this heavily<\/td><\/tr><tr><td><code>DATE<\/code><\/td><td><code>DATE<\/code><\/td><td>Same<\/td><\/tr><tr><td><code>DATETIME<\/code><\/td><td><code>TIMESTAMP<\/code><\/td><td>PostgreSQL equivalent<\/td><\/tr><tr><td><code>TIMESTAMP<\/code><\/td><td><code>TIMESTAMP<\/code> \/ <code>TIMESTAMPTZ<\/code><\/td><td>Prefer <code>TIMESTAMPTZ<\/code> for real-world time<\/td><\/tr><tr><td><code>TIME<\/code><\/td><td><code>TIME<\/code><\/td><td>Same<\/td><\/tr><tr><td><code>BLOB<\/code><\/td><td><code>BYTEA<\/code><\/td><td>Binary data<\/td><\/tr><tr><td><code>JSON<\/code><\/td><td><code>JSON<\/code> \/ <code>JSONB<\/code><\/td><td><code>JSONB<\/code> is usually preferred<\/td><\/tr><tr><td><code>ENUM<\/code><\/td><td><code>ENUM<\/code> \/ lookup table<\/td><td>PostgreSQL supports custom enum types<\/td><\/tr><tr><td><code>SET<\/code><\/td><td>Array \/ join table<\/td><td>No direct same feature<\/td><\/tr><tr><td><code>UUID<\/code><\/td><td><code>UUID<\/code><\/td><td>PostgreSQL has strong native UUID support<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Boolean Difference<\/h2>\n\n\n\n<p>MySQL often uses:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>is_active TINYINT(1)\n<\/code><\/pre>\n\n\n\n<p>PostgreSQL uses:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>is_active BOOLEAN\n<\/code><\/pre>\n\n\n\n<p>Values:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>TRUE\nFALSE\n<\/code><\/pre>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE TABLE users (\n    id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,\n    name TEXT NOT NULL,\n    is_active BOOLEAN NOT NULL DEFAULT TRUE\n);\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Date and Time Difference<\/h2>\n\n\n\n<p>MySQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>created_at DATETIME\n<\/code><\/pre>\n\n\n\n<p>PostgreSQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>created_at TIMESTAMP\n<\/code><\/pre>\n\n\n\n<p>Better for timezone-aware data:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>created_at TIMESTAMPTZ\n<\/code><\/pre>\n\n\n\n<p>Important PostgreSQL note:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>TIMESTAMPTZ does not store the timezone name.\nIt stores an absolute point in time and displays it according to session timezone.\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">JSON Difference<\/h2>\n\n\n\n<p>MySQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>data JSON\n<\/code><\/pre>\n\n\n\n<p>PostgreSQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>data JSONB\n<\/code><\/pre>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE TABLE events (\n    id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,\n    payload JSONB NOT NULL\n);\n<\/code><\/pre>\n\n\n\n<p>Query:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT *\nFROM events\nWHERE payload-&gt;&gt;'type' = 'login';\n<\/code><\/pre>\n\n\n\n<p>JSONB indexing:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE INDEX idx_events_payload\nON events USING GIN (payload);\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">13. SQL Syntax Mapping<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">Selecting Rows<\/h2>\n\n\n\n<p>Same:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT *\nFROM users;\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Limit<\/h2>\n\n\n\n<p>Same:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT *\nFROM users\nLIMIT 10;\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Offset<\/h2>\n\n\n\n<p>Same:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT *\nFROM users\nLIMIT 10 OFFSET 20;\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">String Concatenation<\/h2>\n\n\n\n<p>MySQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT CONCAT(first_name, ' ', last_name) AS full_name\nFROM users;\n<\/code><\/pre>\n\n\n\n<p>PostgreSQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT first_name || ' ' || last_name AS full_name\nFROM users;\n<\/code><\/pre>\n\n\n\n<p>PostgreSQL also supports:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT CONCAT(first_name, ' ', last_name) AS full_name\nFROM users;\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Current Date and Time<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>MySQL<\/th><th>PostgreSQL<\/th><\/tr><\/thead><tbody><tr><td><code>NOW()<\/code><\/td><td><code>NOW()<\/code><\/td><\/tr><tr><td><code>CURRENT_TIMESTAMP<\/code><\/td><td><code>CURRENT_TIMESTAMP<\/code><\/td><\/tr><tr><td><code>CURDATE()<\/code><\/td><td><code>CURRENT_DATE<\/code><\/td><\/tr><tr><td><code>CURTIME()<\/code><\/td><td><code>CURRENT_TIME<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>MySQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT CURDATE();\n<\/code><\/pre>\n\n\n\n<p>PostgreSQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT CURRENT_DATE;\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Case-Insensitive Search<\/h2>\n\n\n\n<p>MySQL often depends on collation:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT *\nFROM users\nWHERE name LIKE '%john%';\n<\/code><\/pre>\n\n\n\n<p>PostgreSQL has <code>ILIKE<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT *\nFROM users\nWHERE name ILIKE '%john%';\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Upsert<\/h2>\n\n\n\n<p>MySQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>INSERT INTO users (email, name)\nVALUES ('alice@example.com', 'Alice')\nON DUPLICATE KEY UPDATE\n    name = VALUES(name);\n<\/code><\/pre>\n\n\n\n<p>PostgreSQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>INSERT INTO users (email, name)\nVALUES ('alice@example.com', 'Alice')\nON CONFLICT (email)\nDO UPDATE SET\n    name = EXCLUDED.name;\n<\/code><\/pre>\n\n\n\n<p>This is a major mapping:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>MySQL ON DUPLICATE KEY UPDATE\n=\nPostgreSQL ON CONFLICT DO UPDATE\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Replace<\/h2>\n\n\n\n<p>MySQL has:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>REPLACE INTO users (id, name)\nVALUES (1, 'Alice');\n<\/code><\/pre>\n\n\n\n<p>PostgreSQL does not have the same <code>REPLACE INTO<\/code>.<\/p>\n\n\n\n<p>Use:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>INSERT INTO users (id, name)\nVALUES (1, 'Alice')\nON CONFLICT (id)\nDO UPDATE SET name = EXCLUDED.name;\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Insert Ignore<\/h2>\n\n\n\n<p>MySQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>INSERT IGNORE INTO users (email, name)\nVALUES ('alice@example.com', 'Alice');\n<\/code><\/pre>\n\n\n\n<p>PostgreSQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>INSERT INTO users (email, name)\nVALUES ('alice@example.com', 'Alice')\nON CONFLICT DO NOTHING;\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Group By Strictness<\/h2>\n\n\n\n<p>MySQL may allow loose grouping depending on SQL mode:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT department, name, COUNT(*)\nFROM employees\nGROUP BY department;\n<\/code><\/pre>\n\n\n\n<p>PostgreSQL rejects this because <code>name<\/code> is neither grouped nor aggregated.<\/p>\n\n\n\n<p>Correct PostgreSQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT department, COUNT(*)\nFROM employees\nGROUP BY department;\n<\/code><\/pre>\n\n\n\n<p>This is important:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>PostgreSQL is stricter and more SQL-standard in many areas.\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">14. Command-Line Tool Mapping<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL Client<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>mysql -u root -p\n<\/code><\/pre>\n\n\n\n<p>Connect to database:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>mysql -u appuser -p shop\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL Client<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>psql -U postgres\n<\/code><\/pre>\n\n\n\n<p>Connect to database:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>psql -U appuser -d shop\n<\/code><\/pre>\n\n\n\n<p>Connect with host and port:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>psql -h localhost -p 5432 -U appuser -d shop\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Useful MySQL vs PostgreSQL Commands<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Task<\/th><th>MySQL<\/th><th>PostgreSQL<\/th><\/tr><\/thead><tbody><tr><td>Show databases<\/td><td><code>SHOW DATABASES;<\/code><\/td><td><code>\\l<\/code><\/td><\/tr><tr><td>Use database<\/td><td><code>USE shop;<\/code><\/td><td><code>\\c shop<\/code><\/td><\/tr><tr><td>Show tables<\/td><td><code>SHOW TABLES;<\/code><\/td><td><code>\\dt<\/code><\/td><\/tr><tr><td>Describe table<\/td><td><code>DESCRIBE users;<\/code><\/td><td><code>\\d users<\/code><\/td><\/tr><tr><td>Show users<\/td><td>Query <code>mysql.user<\/code><\/td><td><code>\\du<\/code><\/td><\/tr><tr><td>Show current database<\/td><td><code>SELECT DATABASE();<\/code><\/td><td><code>SELECT current_database();<\/code><\/td><\/tr><tr><td>Show current user<\/td><td><code>SELECT USER();<\/code><\/td><td><code>SELECT current_user;<\/code><\/td><\/tr><tr><td>Show variables<\/td><td><code>SHOW VARIABLES;<\/code><\/td><td><code>SHOW ALL;<\/code><\/td><\/tr><tr><td>Show process list<\/td><td><code>SHOW PROCESSLIST;<\/code><\/td><td>Query <code>pg_stat_activity<\/code><\/td><\/tr><tr><td>Exit<\/td><td><code>exit<\/code><\/td><td><code>\\q<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL <code>psql<\/code> Commands<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Command<\/th><th>Meaning<\/th><\/tr><\/thead><tbody><tr><td><code>\\l<\/code><\/td><td>List databases<\/td><\/tr><tr><td><code>\\c dbname<\/code><\/td><td>Connect to database<\/td><\/tr><tr><td><code>\\dn<\/code><\/td><td>List schemas<\/td><\/tr><tr><td><code>\\dt<\/code><\/td><td>List tables<\/td><\/tr><tr><td><code>\\dv<\/code><\/td><td>List views<\/td><\/tr><tr><td><code>\\dm<\/code><\/td><td>List materialized views<\/td><\/tr><tr><td><code>\\df<\/code><\/td><td>List functions<\/td><\/tr><tr><td><code>\\ds<\/code><\/td><td>List sequences<\/td><\/tr><tr><td><code>\\du<\/code><\/td><td>List roles<\/td><\/tr><tr><td><code>\\d table_name<\/code><\/td><td>Describe table<\/td><\/tr><tr><td><code>\\x<\/code><\/td><td>Expanded output<\/td><\/tr><tr><td><code>\\q<\/code><\/td><td>Quit<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\\l\n\\c shop\n\\dn\n\\dt\n\\d users\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">15. Configuration Files<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL<\/h2>\n\n\n\n<p>Common MySQL config files:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/etc\/my.cnf\n\/etc\/mysql\/my.cnf\n\/etc\/mysql\/mysql.conf.d\/mysqld.cnf\n<\/code><\/pre>\n\n\n\n<p>Main config sections:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;mysqld]<\/code><\/pre>\n\n\n<p>[client]<\/p>\n\n\n\n<p>[mysql]<\/p>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;mysqld]\nbind-address = 0.0.0.0\nmax_connections = 200\ninnodb_buffer_pool_size = 2G\nslow_query_log = 1\nlong_query_time = 2\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL<\/h2>\n\n\n\n<p>Main PostgreSQL config files:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>postgresql.conf\npg_hba.conf\npg_ident.conf\n<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>File<\/th><th>Meaning<\/th><\/tr><\/thead><tbody><tr><td><code>postgresql.conf<\/code><\/td><td>Main server settings<\/td><\/tr><tr><td><code>pg_hba.conf<\/code><\/td><td>Host-based authentication<\/td><\/tr><tr><td><code>pg_ident.conf<\/code><\/td><td>Maps OS users to DB users<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>Example <code>postgresql.conf<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>listen_addresses = '*'\nmax_connections = 200\nshared_buffers = 2GB\nwork_mem = 16MB\nmaintenance_work_mem = 512MB\nlog_min_duration_statement = 2000\n<\/code><\/pre>\n\n\n\n<p>Example <code>pg_hba.conf<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>host    shop    appuser    192.168.1.0\/24    scram-sha-256\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">16. Port and Service Mapping<\/h1>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Component<\/th><th>MySQL<\/th><th>PostgreSQL<\/th><\/tr><\/thead><tbody><tr><td>Default port<\/td><td><code>3306<\/code><\/td><td><code>5432<\/code><\/td><\/tr><tr><td>Server process<\/td><td><code>mysqld<\/code><\/td><td><code>postgres<\/code><\/td><\/tr><tr><td>CLI client<\/td><td><code>mysql<\/code><\/td><td><code>psql<\/code><\/td><\/tr><tr><td>Service name<\/td><td><code>mysql<\/code> \/ <code>mysqld<\/code><\/td><td><code>postgresql<\/code><\/td><\/tr><tr><td>Admin database<\/td><td><code>mysql<\/code><\/td><td><code>postgres<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>Check service:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>systemctl status mysql\n<\/code><\/pre>\n\n\n\n<p>PostgreSQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>systemctl status postgresql\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">17. Memory Resource Mapping<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL Important Memory Settings<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>MySQL Setting<\/th><th>Meaning<\/th><\/tr><\/thead><tbody><tr><td><code>innodb_buffer_pool_size<\/code><\/td><td>Main InnoDB data\/index cache<\/td><\/tr><tr><td><code>sort_buffer_size<\/code><\/td><td>Per-connection sort buffer<\/td><\/tr><tr><td><code>join_buffer_size<\/code><\/td><td>Per-connection join buffer<\/td><\/tr><tr><td><code>tmp_table_size<\/code><\/td><td>Max internal temporary table size<\/td><\/tr><tr><td><code>max_heap_table_size<\/code><\/td><td>Max MEMORY table size<\/td><\/tr><tr><td><code>key_buffer_size<\/code><\/td><td>MyISAM index cache<\/td><\/tr><tr><td><code>max_connections<\/code><\/td><td>Max client connections<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>Most important MySQL memory setting for InnoDB:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>innodb_buffer_pool_size\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL Important Memory Settings<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>PostgreSQL Setting<\/th><th>Meaning<\/th><\/tr><\/thead><tbody><tr><td><code>shared_buffers<\/code><\/td><td>Main PostgreSQL buffer cache<\/td><\/tr><tr><td><code>work_mem<\/code><\/td><td>Per-operation memory for sort\/hash<\/td><\/tr><tr><td><code>maintenance_work_mem<\/code><\/td><td>Memory for vacuum, create index, alter table<\/td><\/tr><tr><td><code>effective_cache_size<\/code><\/td><td>Planner estimate of OS cache<\/td><\/tr><tr><td><code>wal_buffers<\/code><\/td><td>Memory for WAL<\/td><\/tr><tr><td><code>max_connections<\/code><\/td><td>Max client connections<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>Most important PostgreSQL memory settings:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>shared_buffers\nwork_mem\nmaintenance_work_mem\neffective_cache_size\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Key Difference<\/h2>\n\n\n\n<p>MySQL\/InnoDB heavily relies on:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>InnoDB buffer pool\n<\/code><\/pre>\n\n\n\n<p>PostgreSQL relies on:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>shared_buffers + operating system page cache\n<\/code><\/pre>\n\n\n\n<p>So do not map this too literally:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>innodb_buffer_pool_size = shared_buffers\n<\/code><\/pre>\n\n\n\n<p>They are related, but not identical.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">18. Connection Resource Mapping<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL<\/h2>\n\n\n\n<p>MySQL commonly uses threads for connections.<\/p>\n\n\n\n<p>Command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SHOW PROCESSLIST;\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL<\/h2>\n\n\n\n<p>PostgreSQL traditionally uses a process per connection.<\/p>\n\n\n\n<p>View active sessions:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT *\nFROM pg_stat_activity;\n<\/code><\/pre>\n\n\n\n<p>Useful query:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT\n    pid,\n    usename,\n    datname,\n    state,\n    query\nFROM pg_stat_activity;\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Connection Pooling<\/h2>\n\n\n\n<p>PostgreSQL often benefits from connection pooling because too many direct connections can become expensive.<\/p>\n\n\n\n<p>Common pooler:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>PgBouncer\n<\/code><\/pre>\n\n\n\n<p>Mental mapping:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>MySQL<\/th><th>PostgreSQL<\/th><\/tr><\/thead><tbody><tr><td>Many app connections may be okay depending on setup<\/td><td>Use connection pooling earlier<\/td><\/tr><tr><td><code>SHOW PROCESSLIST<\/code><\/td><td><code>pg_stat_activity<\/code><\/td><\/tr><tr><td>Kill connection with <code>KILL id<\/code><\/td><td><code>SELECT pg_terminate_backend(pid);<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>PostgreSQL terminate session:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT pg_terminate_backend(12345);\n<\/code><\/pre>\n\n\n\n<p>Cancel running query:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT pg_cancel_backend(12345);\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">19. Transaction Mapping<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>START TRANSACTION;\n\nUPDATE accounts\nSET balance = balance - 100\nWHERE id = 1;\n\nUPDATE accounts\nSET balance = balance + 100\nWHERE id = 2;\n\nCOMMIT;\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>BEGIN;\n\nUPDATE accounts\nSET balance = balance - 100\nWHERE id = 1;\n\nUPDATE accounts\nSET balance = balance + 100\nWHERE id = 2;\n\nCOMMIT;\n<\/code><\/pre>\n\n\n\n<p>Same general concept.<\/p>\n\n\n\n<p>Rollback:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ROLLBACK;\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Important PostgreSQL Difference<\/h2>\n\n\n\n<p>In PostgreSQL, if an error happens inside a transaction, the transaction becomes failed.<\/p>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>BEGIN;\n\nSELECT 1 \/ 0;\n\nSELECT * FROM users;\n<\/code><\/pre>\n\n\n\n<p>PostgreSQL will say the transaction is aborted until you run:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ROLLBACK;\n<\/code><\/pre>\n\n\n\n<p>This surprises MySQL users.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Transactional DDL<\/h2>\n\n\n\n<p>PostgreSQL supports transactional DDL for many operations.<\/p>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>BEGIN;\n\nCREATE TABLE test_table (\n    id INT\n);\n\nROLLBACK;\n<\/code><\/pre>\n\n\n\n<p>After rollback, the table is gone.<\/p>\n\n\n\n<p>In MySQL, many DDL operations cause implicit commits.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">20. MVCC Mapping<\/h1>\n\n\n\n<p>Both MySQL\/InnoDB and PostgreSQL use MVCC.<\/p>\n\n\n\n<p>MVCC means:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Multi-Version Concurrency Control\n<\/code><\/pre>\n\n\n\n<p>It allows readers and writers to avoid blocking each other in many cases.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL\/InnoDB MVCC<\/h2>\n\n\n\n<p>InnoDB stores undo information in undo logs.<\/p>\n\n\n\n<p>Old row versions are reconstructed from undo data.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL MVCC<\/h2>\n\n\n\n<p>PostgreSQL stores multiple row versions directly in the table.<\/p>\n\n\n\n<p>When you update a row, PostgreSQL creates a new row version.<\/p>\n\n\n\n<p>Old versions are later cleaned up by:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>VACUUM\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Important PostgreSQL Concept: Dead Tuples<\/h2>\n\n\n\n<p>When PostgreSQL updates or deletes rows, old row versions remain for a while.<\/p>\n\n\n\n<p>These old row versions are called:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>dead tuples\n<\/code><\/pre>\n\n\n\n<p>They are cleaned by:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>autovacuum\n<\/code><\/pre>\n\n\n\n<p>This is one of the biggest PostgreSQL operational topics.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">21. Vacuum vs MySQL Purge<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL\/InnoDB<\/h2>\n\n\n\n<p>InnoDB uses undo logs and purge threads to clean old row versions.<\/p>\n\n\n\n<p>You usually do not manually run something called vacuum.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL<\/h2>\n\n\n\n<p>PostgreSQL needs vacuum.<\/p>\n\n\n\n<p>Manual vacuum:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>VACUUM users;\n<\/code><\/pre>\n\n\n\n<p>Vacuum and update statistics:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>VACUUM ANALYZE users;\n<\/code><\/pre>\n\n\n\n<p>Full table rewrite:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>VACUUM FULL users;\n<\/code><\/pre>\n\n\n\n<p>Important:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>VACUUM FULL is heavier and takes stronger locks.\nDo not treat it as a routine command.\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Autovacuum<\/h2>\n\n\n\n<p>PostgreSQL normally runs autovacuum automatically.<\/p>\n\n\n\n<p>Autovacuum handles:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Cleaning dead tuples\nPreventing transaction ID wraparound\nUpdating table statistics\n<\/code><\/pre>\n\n\n\n<p>For MySQL users, think:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>PostgreSQL autovacuum is not optional housekeeping.\nIt is essential database maintenance.\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">22. Index Mapping<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">Common Index Types<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>MySQL Index<\/th><th>PostgreSQL Equivalent<\/th><\/tr><\/thead><tbody><tr><td>Primary key<\/td><td>Primary key<\/td><\/tr><tr><td>Unique index<\/td><td>Unique index<\/td><\/tr><tr><td>Normal index<\/td><td>B-tree index<\/td><\/tr><tr><td>Composite index<\/td><td>Composite index<\/td><\/tr><tr><td>Full-text index<\/td><td>Full-text search index<\/td><\/tr><tr><td>Spatial index<\/td><td>GiST\/SP-GiST, often with PostGIS<\/td><\/tr><tr><td>Functional index<\/td><td>Expression index<\/td><\/tr><tr><td>Prefix index<\/td><td>Expression or specialized index<\/td><\/tr><tr><td>Invisible index<\/td><td>No exact same common equivalent<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Basic Index<\/h2>\n\n\n\n<p>MySQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE INDEX idx_users_email\nON users(email);\n<\/code><\/pre>\n\n\n\n<p>PostgreSQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE INDEX idx_users_email\nON users(email);\n<\/code><\/pre>\n\n\n\n<p>Same.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Unique Index<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE UNIQUE INDEX idx_users_email_unique\nON users(email);\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Composite Index<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE INDEX idx_orders_customer_status\nON orders(customer_id, status);\n<\/code><\/pre>\n\n\n\n<p>Same concept as MySQL.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Expression Index<\/h2>\n\n\n\n<p>PostgreSQL is excellent here.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE INDEX idx_users_lower_email\nON users (LOWER(email));\n<\/code><\/pre>\n\n\n\n<p>Then query:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT *\nFROM users\nWHERE LOWER(email) = LOWER('Alice@Example.com');\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Partial Index<\/h2>\n\n\n\n<p>This is a powerful PostgreSQL feature.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE INDEX idx_active_users_email\nON users(email)\nWHERE is_active = TRUE;\n<\/code><\/pre>\n\n\n\n<p>This indexes only active users.<\/p>\n\n\n\n<p>MySQL does not have the same general partial-index feature.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL Index Types<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>PostgreSQL Index Type<\/th><th>Use<\/th><\/tr><\/thead><tbody><tr><td>B-tree<\/td><td>Default, equality\/range<\/td><\/tr><tr><td>Hash<\/td><td>Equality<\/td><\/tr><tr><td>GIN<\/td><td>JSONB, arrays, full-text<\/td><\/tr><tr><td>GiST<\/td><td>Geometric, full-text, ranges<\/td><\/tr><tr><td>SP-GiST<\/td><td>Partitioned search structures<\/td><\/tr><tr><td>BRIN<\/td><td>Very large naturally ordered tables<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>Example JSONB GIN index:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE INDEX idx_events_payload\nON events USING GIN (payload);\n<\/code><\/pre>\n\n\n\n<p>Example BRIN index:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE INDEX idx_logs_created_at_brin\nON logs USING BRIN (created_at);\n<\/code><\/pre>\n\n\n\n<p>BRIN is useful for huge append-only tables where data is naturally ordered by time.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">23. Explain Plan Mapping<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>EXPLAIN\nSELECT *\nFROM users\nWHERE email = 'alice@example.com';\n<\/code><\/pre>\n\n\n\n<p>MySQL also supports:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>EXPLAIN ANALYZE\nSELECT *\nFROM users\nWHERE email = 'alice@example.com';\n<\/code><\/pre>\n\n\n\n<p>in newer versions.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>EXPLAIN\nSELECT *\nFROM users\nWHERE email = 'alice@example.com';\n<\/code><\/pre>\n\n\n\n<p>Better:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>EXPLAIN ANALYZE\nSELECT *\nFROM users\nWHERE email = 'alice@example.com';\n<\/code><\/pre>\n\n\n\n<p>Even better for details:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>EXPLAIN (ANALYZE, BUFFERS)\nSELECT *\nFROM users\nWHERE email = 'alice@example.com';\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL Plan Terms<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Term<\/th><th>Meaning<\/th><\/tr><\/thead><tbody><tr><td><code>Seq Scan<\/code><\/td><td>Full table scan<\/td><\/tr><tr><td><code>Index Scan<\/code><\/td><td>Uses index and table<\/td><\/tr><tr><td><code>Index Only Scan<\/code><\/td><td>Uses only index if possible<\/td><\/tr><tr><td><code>Bitmap Index Scan<\/code><\/td><td>Builds bitmap from index<\/td><\/tr><tr><td><code>Nested Loop<\/code><\/td><td>Join by looping<\/td><\/tr><tr><td><code>Hash Join<\/code><\/td><td>Join using hash table<\/td><\/tr><tr><td><code>Merge Join<\/code><\/td><td>Join sorted inputs<\/td><\/tr><tr><td><code>Sort<\/code><\/td><td>Sort operation<\/td><\/tr><tr><td><code>Aggregate<\/code><\/td><td>Aggregation<\/td><\/tr><tr><td><code>HashAggregate<\/code><\/td><td>Aggregation using hash table<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>Important:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>EXPLAIN ANALYZE actually runs the query.\n<\/code><\/pre>\n\n\n\n<p>So be careful with:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>EXPLAIN ANALYZE\nDELETE FROM large_table;\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">24. Logs Mapping<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL Logs<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>MySQL Log<\/th><th>Meaning<\/th><\/tr><\/thead><tbody><tr><td>Error log<\/td><td>Server errors and startup messages<\/td><\/tr><tr><td>General query log<\/td><td>All client queries<\/td><\/tr><tr><td>Slow query log<\/td><td>Slow SQL statements<\/td><\/tr><tr><td>Binary log<\/td><td>Replication and point-in-time recovery<\/td><\/tr><tr><td>Relay log<\/td><td>Replica receives source binary log events<\/td><\/tr><tr><td>Redo log<\/td><td>InnoDB crash recovery<\/td><\/tr><tr><td>Undo log<\/td><td>MVCC rollback data<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL Logs<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>PostgreSQL Log\/Resource<\/th><th>Meaning<\/th><\/tr><\/thead><tbody><tr><td>Server log<\/td><td>Errors, messages, slow queries<\/td><\/tr><tr><td>WAL<\/td><td>Write-Ahead Log for recovery and replication<\/td><\/tr><tr><td>Archive WAL<\/td><td>Saved WAL files for PITR<\/td><\/tr><tr><td>Autovacuum logs<\/td><td>Vacuum activity if enabled<\/td><\/tr><tr><td>Checkpoint logs<\/td><td>Checkpoint activity if enabled<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Slow Query Logging<\/h2>\n\n\n\n<p>MySQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>slow_query_log = 1\nlong_query_time = 2\n<\/code><\/pre>\n\n\n\n<p>PostgreSQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>log_min_duration_statement = 2000\n<\/code><\/pre>\n\n\n\n<p>This logs queries taking longer than 2000 milliseconds.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Statement Logging<\/h2>\n\n\n\n<p>PostgreSQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>log_statement = 'none'\nlog_statement = 'ddl'\nlog_statement = 'mod'\nlog_statement = 'all'\n<\/code><\/pre>\n\n\n\n<p>Common safer setting:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>log_statement = 'ddl'\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">25. WAL vs Binary Log<\/h1>\n\n\n\n<p>This is a major concept.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL Binary Log<\/h2>\n\n\n\n<p>MySQL binary log records changes for:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Replication\nPoint-in-time recovery\nAuditing changes\n<\/code><\/pre>\n\n\n\n<p>Common formats:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>STATEMENT\nROW\nMIXED\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL WAL<\/h2>\n\n\n\n<p>PostgreSQL uses:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>WAL = Write-Ahead Log\n<\/code><\/pre>\n\n\n\n<p>WAL is used for:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Crash recovery\nStreaming replication\nPoint-in-time recovery\nLogical decoding\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Mapping<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>MySQL<\/th><th>PostgreSQL<\/th><\/tr><\/thead><tbody><tr><td>Binary log<\/td><td>WAL for replication\/recovery concepts<\/td><\/tr><tr><td>Redo log<\/td><td>WAL<\/td><\/tr><tr><td>Relay log<\/td><td>Received WAL on standby<\/td><\/tr><tr><td>Binlog position<\/td><td>LSN<\/td><\/tr><tr><td>GTID<\/td><td>Replication slots \/ LSN concepts<\/td><\/tr><tr><td>Row-based replication<\/td><td>Logical replication \/ WAL decoding<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>PostgreSQL uses LSN:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Log Sequence Number\n<\/code><\/pre>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT pg_current_wal_lsn();\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">26. Backup and Restore Mapping<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL Logical Backup<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>mysqldump -u root -p shop &gt; shop.sql\n<\/code><\/pre>\n\n\n\n<p>Restore:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>mysql -u root -p shop &lt; shop.sql\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL Logical Backup<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>pg_dump -U postgres shop &gt; shop.sql\n<\/code><\/pre>\n\n\n\n<p>Restore:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>psql -U postgres -d shop &lt; shop.sql\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL Custom Format Backup<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>pg_dump -U postgres -Fc shop &gt; shop.dump\n<\/code><\/pre>\n\n\n\n<p>Restore:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>pg_restore -U postgres -d shop shop.dump\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Backup All Databases<\/h2>\n\n\n\n<p>MySQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>mysqldump -u root -p --all-databases &gt; all.sql\n<\/code><\/pre>\n\n\n\n<p>PostgreSQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>pg_dumpall -U postgres &gt; all.sql\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Physical Backup<\/h2>\n\n\n\n<p>MySQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Filesystem snapshot\nMySQL Enterprise Backup\nPercona XtraBackup\n<\/code><\/pre>\n\n\n\n<p>PostgreSQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>pg_basebackup\nFilesystem snapshot\nStorage snapshot\n<\/code><\/pre>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>pg_basebackup -U replication_user -D \/backup\/pgbase -Fp -Xs -P\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Point-In-Time Recovery<\/h2>\n\n\n\n<p>MySQL PITR uses:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Full backup + binary logs\n<\/code><\/pre>\n\n\n\n<p>PostgreSQL PITR uses:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Base backup + archived WAL\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">27. Replication Mapping<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL Replication<\/h2>\n\n\n\n<p>Common terms:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Source\nReplica\nBinary log\nRelay log\nGTID\nSemi-sync replication\n<\/code><\/pre>\n\n\n\n<p>Older terms:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Master\nSlave\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL Replication<\/h2>\n\n\n\n<p>Common terms:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Primary\nStandby\nWAL streaming\nReplication slot\nSynchronous replication\nLogical replication\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Mapping Table<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>MySQL<\/th><th>PostgreSQL<\/th><\/tr><\/thead><tbody><tr><td>Source\/master<\/td><td>Primary<\/td><\/tr><tr><td>Replica\/slave<\/td><td>Standby<\/td><\/tr><tr><td>Binary log<\/td><td>WAL<\/td><\/tr><tr><td>Relay log<\/td><td>WAL received by standby<\/td><\/tr><tr><td>Binlog position<\/td><td>LSN<\/td><\/tr><tr><td>GTID<\/td><td>Replication slots \/ LSN tracking<\/td><\/tr><tr><td>Row-based replication<\/td><td>Logical replication<\/td><\/tr><tr><td>Statement-based replication<\/td><td>No exact direct default equivalent<\/td><\/tr><tr><td>Semi-sync replication<\/td><td>Synchronous replication<\/td><\/tr><tr><td>Read replica<\/td><td>Hot standby<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL Physical Streaming Replication<\/h2>\n\n\n\n<p>This replicates the whole PostgreSQL cluster.<\/p>\n\n\n\n<p>Good for:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Read replicas\nFailover\nDisaster recovery\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL Logical Replication<\/h2>\n\n\n\n<p>This replicates selected tables.<\/p>\n\n\n\n<p>Good for:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Selective replication\nData migration\nPartial sync\nVersion upgrades\n<\/code><\/pre>\n\n\n\n<p>Example publication:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE PUBLICATION shop_pub\nFOR TABLE customers, orders;\n<\/code><\/pre>\n\n\n\n<p>Example subscription:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE SUBSCRIPTION shop_sub\nCONNECTION 'host=primary dbname=shop user=repl password=secret'\nPUBLICATION shop_pub;\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">28. High Availability Mapping<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL HA Tools<\/h2>\n\n\n\n<p>Common MySQL HA options:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Group Replication\nInnoDB Cluster\nMHA\nOrchestrator\nProxySQL\nSemi-sync replication\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL HA Tools<\/h2>\n\n\n\n<p>Common PostgreSQL HA tools:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Patroni\nrepmgr\npg_auto_failover\nPacemaker\/Corosync\nHAProxy\nPgBouncer\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Conceptual Mapping<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>MySQL HA Concept<\/th><th>PostgreSQL Equivalent<\/th><\/tr><\/thead><tbody><tr><td>Primary with replicas<\/td><td>Primary with standbys<\/td><\/tr><tr><td>Automatic failover<\/td><td>Patroni\/repmgr\/pg_auto_failover<\/td><\/tr><tr><td>ProxySQL routing<\/td><td>HAProxy\/PgBouncer<\/td><\/tr><tr><td>Group Replication<\/td><td>No exact default equivalent<\/td><\/tr><tr><td>InnoDB Cluster<\/td><td>PostgreSQL HA stack using external tools<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>PostgreSQL core provides strong replication primitives, but automatic HA is commonly built with external tooling.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">29. Partitioning Mapping<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL Partitioning<\/h2>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE TABLE orders (\n    id BIGINT,\n    order_date DATE\n)\nPARTITION BY RANGE (YEAR(order_date)) (\n    PARTITION p2024 VALUES LESS THAN (2025),\n    PARTITION p2025 VALUES LESS THAN (2026)\n);\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL Partitioning<\/h2>\n\n\n\n<p>PostgreSQL uses declarative partitioning.<\/p>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE TABLE orders (\n    id BIGINT GENERATED ALWAYS AS IDENTITY,\n    order_date DATE NOT NULL,\n    amount NUMERIC(10,2)\n) PARTITION BY RANGE (order_date);\n<\/code><\/pre>\n\n\n\n<p>Create partitions:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE TABLE orders_2025\nPARTITION OF orders\nFOR VALUES FROM ('2025-01-01') TO ('2026-01-01');\n\nCREATE TABLE orders_2026\nPARTITION OF orders\nFOR VALUES FROM ('2026-01-01') TO ('2027-01-01');\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Partitioning Types<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>MySQL<\/th><th>PostgreSQL<\/th><\/tr><\/thead><tbody><tr><td>RANGE<\/td><td>RANGE<\/td><\/tr><tr><td>LIST<\/td><td>LIST<\/td><\/tr><tr><td>HASH<\/td><td>HASH<\/td><\/tr><tr><td>KEY<\/td><td>No exact same style<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>PostgreSQL partitioning is commonly used for:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Large time-series tables\nLog tables\nMulti-tenant data\nData retention\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">30. Temporary Tables<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE TEMPORARY TABLE temp_users (\n    id INT,\n    name VARCHAR(100)\n);\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE TEMP TABLE temp_users (\n    id INT,\n    name TEXT\n);\n<\/code><\/pre>\n\n\n\n<p>PostgreSQL temporary tables are session-local.<\/p>\n\n\n\n<p>You can also use:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE TEMPORARY TABLE temp_users (\n    id INT,\n    name TEXT\n);\n<\/code><\/pre>\n\n\n\n<p>Same meaning.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">31. Views and Materialized Views<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">Views<\/h2>\n\n\n\n<p>MySQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE VIEW active_users AS\nSELECT *\nFROM users\nWHERE is_active = 1;\n<\/code><\/pre>\n\n\n\n<p>PostgreSQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE VIEW active_users AS\nSELECT *\nFROM users\nWHERE is_active = TRUE;\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Materialized Views<\/h2>\n\n\n\n<p>PostgreSQL supports materialized views natively:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE MATERIALIZED VIEW monthly_sales AS\nSELECT\n    date_trunc('month', order_date) AS month,\n    SUM(amount) AS total_sales\nFROM orders\nGROUP BY date_trunc('month', order_date);\n<\/code><\/pre>\n\n\n\n<p>Refresh:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>REFRESH MATERIALIZED VIEW monthly_sales;\n<\/code><\/pre>\n\n\n\n<p>MySQL does not have true built-in materialized views in the same way. You usually simulate them with tables, scheduled jobs, or triggers.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">32. Functions, Procedures, and Triggers<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL<\/h2>\n\n\n\n<p>MySQL supports:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Stored procedures\nFunctions\nTriggers\nEvents\n<\/code><\/pre>\n\n\n\n<p>Example procedure:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>DELIMITER \/\/\n\nCREATE PROCEDURE GetUsers()\nBEGIN\n    SELECT * FROM users;\nEND \/\/\n\nDELIMITER ;\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL<\/h2>\n\n\n\n<p>PostgreSQL supports:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Functions\nProcedures\nTriggers\nRules\nExtensions\n<\/code><\/pre>\n\n\n\n<p>PostgreSQL function example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE OR REPLACE FUNCTION get_user_count()\nRETURNS INTEGER AS $$\nDECLARE\n    total INTEGER;\nBEGIN\n    SELECT COUNT(*) INTO total FROM users;\n    RETURN total;\nEND;\n$$ LANGUAGE plpgsql;\n<\/code><\/pre>\n\n\n\n<p>Call:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT get_user_count();\n<\/code><\/pre>\n\n\n\n<p>Procedure example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE PROCEDURE deactivate_user(p_user_id BIGINT)\nLANGUAGE plpgsql\nAS $$\nBEGIN\n    UPDATE users\n    SET is_active = FALSE\n    WHERE id = p_user_id;\nEND;\n$$;\n<\/code><\/pre>\n\n\n\n<p>Call:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CALL deactivate_user(10);\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Trigger Example in PostgreSQL<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE OR REPLACE FUNCTION update_modified_time()\nRETURNS TRIGGER AS $$\nBEGIN\n    NEW.updated_at = NOW();\n    RETURN NEW;\nEND;\n$$ LANGUAGE plpgsql;\n<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE TRIGGER trg_users_updated_at\nBEFORE UPDATE ON users\nFOR EACH ROW\nEXECUTE FUNCTION update_modified_time();\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">33. Events vs Scheduled Jobs<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL Event Scheduler<\/h2>\n\n\n\n<p>MySQL has an event scheduler:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE EVENT cleanup_old_logs\nON SCHEDULE EVERY 1 DAY\nDO\nDELETE FROM logs\nWHERE created_at &lt; NOW() - INTERVAL 30 DAY;\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL<\/h2>\n\n\n\n<p>PostgreSQL core does not have exactly the same built-in event scheduler.<\/p>\n\n\n\n<p>Common options:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cron\npg_cron extension\napplication scheduler\nexternal job scheduler\n<\/code><\/pre>\n\n\n\n<p>With <code>pg_cron<\/code>, PostgreSQL can schedule jobs from inside the database.<\/p>\n\n\n\n<p>Conceptual mapping:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>MySQL<\/th><th>PostgreSQL<\/th><\/tr><\/thead><tbody><tr><td>Event Scheduler<\/td><td><code>pg_cron<\/code>, cron, external scheduler<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">34. Extensions vs Plugins<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL Plugins<\/h2>\n\n\n\n<p>MySQL supports plugins for:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Authentication\nStorage engines\nFull-text parsers\nAudit\nReplication\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL Extensions<\/h2>\n\n\n\n<p>PostgreSQL has a powerful extension system.<\/p>\n\n\n\n<p>List available extensions:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT *\nFROM pg_available_extensions;\n<\/code><\/pre>\n\n\n\n<p>Install extension:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE EXTENSION IF NOT EXISTS pg_stat_statements;\n<\/code><\/pre>\n\n\n\n<p>Popular PostgreSQL extensions:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Extension<\/th><th>Purpose<\/th><\/tr><\/thead><tbody><tr><td><code>pg_stat_statements<\/code><\/td><td>Query performance tracking<\/td><\/tr><tr><td><code>uuid-ossp<\/code><\/td><td>UUID generation<\/td><\/tr><tr><td><code>pgcrypto<\/code><\/td><td>Cryptographic functions<\/td><\/tr><tr><td><code>citext<\/code><\/td><td>Case-insensitive text<\/td><\/tr><tr><td><code>postgis<\/code><\/td><td>Spatial\/geographic data<\/td><\/tr><tr><td><code>pg_trgm<\/code><\/td><td>Trigram search<\/td><\/tr><tr><td><code>btree_gin<\/code><\/td><td>Extra GIN support<\/td><\/tr><tr><td><code>pg_cron<\/code><\/td><td>Scheduled jobs<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE EXTENSION IF NOT EXISTS pg_trgm;\n<\/code><\/pre>\n\n\n\n<p>Then create trigram index:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE INDEX idx_users_name_trgm\nON users USING GIN (name gin_trgm_ops);\n<\/code><\/pre>\n\n\n\n<p>Useful for fuzzy search.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">35. Monitoring Mapping<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL Monitoring<\/h2>\n\n\n\n<p>Common commands:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SHOW STATUS;\nSHOW GLOBAL STATUS;\nSHOW VARIABLES;\nSHOW PROCESSLIST;\n<\/code><\/pre>\n\n\n\n<p>Common schemas:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>performance_schema\nsys\ninformation_schema\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL Monitoring<\/h2>\n\n\n\n<p>PostgreSQL uses catalog and statistics views.<\/p>\n\n\n\n<p>Common views:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>PostgreSQL View<\/th><th>Meaning<\/th><\/tr><\/thead><tbody><tr><td><code>pg_stat_activity<\/code><\/td><td>Active sessions and queries<\/td><\/tr><tr><td><code>pg_stat_database<\/code><\/td><td>Database-level stats<\/td><\/tr><tr><td><code>pg_stat_user_tables<\/code><\/td><td>Table-level stats<\/td><\/tr><tr><td><code>pg_stat_user_indexes<\/code><\/td><td>Index-level stats<\/td><\/tr><tr><td><code>pg_stat_bgwriter<\/code><\/td><td>Background writer stats<\/td><\/tr><tr><td><code>pg_stat_wal<\/code><\/td><td>WAL stats<\/td><\/tr><tr><td><code>pg_locks<\/code><\/td><td>Lock information<\/td><\/tr><tr><td><code>pg_stat_replication<\/code><\/td><td>Replication status<\/td><\/tr><tr><td><code>pg_stat_statements<\/code><\/td><td>Query stats extension<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Active Queries<\/h2>\n\n\n\n<p>MySQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SHOW PROCESSLIST;\n<\/code><\/pre>\n\n\n\n<p>PostgreSQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT\n    pid,\n    usename,\n    datname,\n    state,\n    query,\n    query_start\nFROM pg_stat_activity\nORDER BY query_start;\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Locks<\/h2>\n\n\n\n<p>PostgreSQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT *\nFROM pg_locks;\n<\/code><\/pre>\n\n\n\n<p>Useful lock query:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT\n    a.pid,\n    a.usename,\n    a.query,\n    l.locktype,\n    l.mode,\n    l.granted\nFROM pg_locks l\nJOIN pg_stat_activity a ON l.pid = a.pid;\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">36. Query Performance Mapping<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL<\/h2>\n\n\n\n<p>You may use:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>EXPLAIN\nslow query log\nperformance_schema\nsys schema\nSHOW STATUS\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL<\/h2>\n\n\n\n<p>You commonly use:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>EXPLAIN ANALYZE\npg_stat_statements\npg_stat_activity\npg_stat_user_tables\npg_stat_user_indexes\nauto_explain\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><code>pg_stat_statements<\/code><\/h2>\n\n\n\n<p>Enable extension:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE EXTENSION IF NOT EXISTS pg_stat_statements;\n<\/code><\/pre>\n\n\n\n<p>Query slow\/expensive SQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT\n    query,\n    calls,\n    total_exec_time,\n    mean_exec_time,\n    rows\nFROM pg_stat_statements\nORDER BY total_exec_time DESC\nLIMIT 10;\n<\/code><\/pre>\n\n\n\n<p>This is somewhat like using MySQL Performance Schema or slow query analysis.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">37. Locking Mapping<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL\/InnoDB<\/h2>\n\n\n\n<p>Common lock concepts:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Row locks\nGap locks\nNext-key locks\nMetadata locks\nTable locks\nDeadlocks\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL<\/h2>\n\n\n\n<p>Common lock concepts:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Row locks\nTable locks\nAdvisory locks\nPredicate locks\nDeadlocks\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Row Lock<\/h2>\n\n\n\n<p>Same style:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT *\nFROM orders\nWHERE id = 100\nFOR UPDATE;\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL Lock Modes<\/h2>\n\n\n\n<p>Common PostgreSQL table lock modes include:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ACCESS SHARE\nROW SHARE\nROW EXCLUSIVE\nSHARE UPDATE EXCLUSIVE\nSHARE\nSHARE ROW EXCLUSIVE\nEXCLUSIVE\nACCESS EXCLUSIVE\n<\/code><\/pre>\n\n\n\n<p>The strongest common lock is:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ACCESS EXCLUSIVE\n<\/code><\/pre>\n\n\n\n<p>It blocks almost everything.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Advisory Locks<\/h2>\n\n\n\n<p>PostgreSQL has advisory locks:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT pg_advisory_lock(12345);\n<\/code><\/pre>\n\n\n\n<p>Release:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT pg_advisory_unlock(12345);\n<\/code><\/pre>\n\n\n\n<p>These are application-controlled locks.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">38. Isolation Levels<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL<\/h2>\n\n\n\n<p>Common isolation levels:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>READ UNCOMMITTED\nREAD COMMITTED\nREPEATABLE READ\nSERIALIZABLE\n<\/code><\/pre>\n\n\n\n<p>MySQL\/InnoDB default is commonly:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>REPEATABLE READ\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL<\/h2>\n\n\n\n<p>PostgreSQL supports:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>READ COMMITTED\nREPEATABLE READ\nSERIALIZABLE\n<\/code><\/pre>\n\n\n\n<p>PostgreSQL default:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>READ COMMITTED\n<\/code><\/pre>\n\n\n\n<p>Set isolation level:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;\n<\/code><\/pre>\n\n\n\n<p>or:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;\n<\/code><\/pre>\n\n\n\n<p>Important mapping:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>MySQL<\/th><th>PostgreSQL<\/th><\/tr><\/thead><tbody><tr><td>Common default: <code>REPEATABLE READ<\/code><\/td><td>Default: <code>READ COMMITTED<\/code><\/td><\/tr><tr><td>Gap locks under InnoDB<\/td><td>Different MVCC\/locking behavior<\/td><\/tr><tr><td>Serializable<\/td><td>Serializable Snapshot Isolation<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">39. Character Set and Collation<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL<\/h2>\n\n\n\n<p>MySQL often uses:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CHARACTER SET utf8mb4\nCOLLATE utf8mb4_0900_ai_ci\n<\/code><\/pre>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE DATABASE shop\nCHARACTER SET utf8mb4\nCOLLATE utf8mb4_0900_ai_ci;\n<\/code><\/pre>\n\n\n\n<p>Many MySQL collations are case-insensitive by default.<\/p>\n\n\n\n<p>That means:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>WHERE name = 'alice'\n<\/code><\/pre>\n\n\n\n<p>may match:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Alice\nALICE\nalice\n<\/code><\/pre>\n\n\n\n<p>depending on collation.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL<\/h2>\n\n\n\n<p>PostgreSQL supports collations too, but case-sensitive behavior is more common by default.<\/p>\n\n\n\n<p>Case-insensitive search:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT *\nFROM users\nWHERE name ILIKE 'alice';\n<\/code><\/pre>\n\n\n\n<p>Or use <code>LOWER()<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT *\nFROM users\nWHERE LOWER(name) = LOWER('Alice');\n<\/code><\/pre>\n\n\n\n<p>Or use <code>citext<\/code> extension:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE EXTENSION IF NOT EXISTS citext;\n<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE TABLE users (\n    email CITEXT UNIQUE\n);\n<\/code><\/pre>\n\n\n\n<p><code>CITEXT<\/code> means case-insensitive text.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">40. Full-Text Search<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL<\/h2>\n\n\n\n<p>MySQL full-text index:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE FULLTEXT INDEX idx_articles_body\nON articles(body);\n<\/code><\/pre>\n\n\n\n<p>Query:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT *\nFROM articles\nWHERE MATCH(title, body) AGAINST ('database tutorial');\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL<\/h2>\n\n\n\n<p>PostgreSQL full-text search uses <code>tsvector<\/code> and <code>tsquery<\/code>.<\/p>\n\n\n\n<p>Simple query:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT *\nFROM articles\nWHERE to_tsvector('english', title || ' ' || body)\n      @@ plainto_tsquery('english', 'database tutorial');\n<\/code><\/pre>\n\n\n\n<p>Better with generated column:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ALTER TABLE articles\nADD COLUMN search_vector tsvector\nGENERATED ALWAYS AS (\n    to_tsvector('english', coalesce(title, '') || ' ' || coalesce(body, ''))\n) STORED;\n<\/code><\/pre>\n\n\n\n<p>Index:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE INDEX idx_articles_search\nON articles USING GIN (search_vector);\n<\/code><\/pre>\n\n\n\n<p>Query:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT *\nFROM articles\nWHERE search_vector @@ plainto_tsquery('english', 'database tutorial');\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">41. JSON Mapping<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL JSON<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE TABLE events (\n    id BIGINT AUTO_INCREMENT PRIMARY KEY,\n    payload JSON\n);\n<\/code><\/pre>\n\n\n\n<p>Query:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT JSON_EXTRACT(payload, '$.type')\nFROM events;\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL JSONB<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE TABLE events (\n    id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,\n    payload JSONB\n);\n<\/code><\/pre>\n\n\n\n<p>Get field as JSON:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT payload-&gt;'type'\nFROM events;\n<\/code><\/pre>\n\n\n\n<p>Get field as text:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT payload-&gt;&gt;'type'\nFROM events;\n<\/code><\/pre>\n\n\n\n<p>Filter:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT *\nFROM events\nWHERE payload-&gt;&gt;'type' = 'login';\n<\/code><\/pre>\n\n\n\n<p>Containment:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT *\nFROM events\nWHERE payload @&gt; '{\"type\": \"login\"}';\n<\/code><\/pre>\n\n\n\n<p>Index:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE INDEX idx_events_payload\nON events USING GIN (payload);\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">42. Constraints Mapping<\/h1>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Constraint<\/th><th>MySQL<\/th><th>PostgreSQL<\/th><\/tr><\/thead><tbody><tr><td>Primary key<\/td><td>Yes<\/td><td>Yes<\/td><\/tr><tr><td>Foreign key<\/td><td>Yes with InnoDB<\/td><td>Yes<\/td><\/tr><tr><td>Unique<\/td><td>Yes<\/td><td>Yes<\/td><\/tr><tr><td>Not null<\/td><td>Yes<\/td><td>Yes<\/td><\/tr><tr><td>Check<\/td><td>Supported in modern MySQL<\/td><td>Strong support<\/td><\/tr><tr><td>Exclusion constraint<\/td><td>No common equivalent<\/td><td>Yes<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL Check Constraint<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE TABLE products (\n    id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,\n    price NUMERIC(10,2) NOT NULL,\n    CONSTRAINT chk_price_positive CHECK (price &gt;= 0)\n);\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Foreign Key<\/h2>\n\n\n\n<p>Same idea:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE TABLE orders (\n    id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,\n    customer_id BIGINT NOT NULL,\n    CONSTRAINT fk_orders_customer\n        FOREIGN KEY (customer_id)\n        REFERENCES customers(id)\n);\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Deferrable Constraints<\/h2>\n\n\n\n<p>PostgreSQL supports deferrable constraints.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE TABLE child (\n    id BIGINT PRIMARY KEY,\n    parent_id BIGINT,\n    CONSTRAINT fk_parent\n        FOREIGN KEY (parent_id)\n        REFERENCES parent(id)\n        DEFERRABLE INITIALLY DEFERRED\n);\n<\/code><\/pre>\n\n\n\n<p>This lets constraint checking happen at transaction commit.<\/p>\n\n\n\n<p>Very powerful. Not something most MySQL users commonly use.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">43. Generated Columns<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE TABLE users (\n    first_name VARCHAR(100),\n    last_name VARCHAR(100),\n    full_name VARCHAR(201)\n        GENERATED ALWAYS AS (CONCAT(first_name, ' ', last_name)) STORED\n);\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE TABLE users (\n    first_name TEXT,\n    last_name TEXT,\n    full_name TEXT\n        GENERATED ALWAYS AS (first_name || ' ' || last_name) STORED\n);\n<\/code><\/pre>\n\n\n\n<p>Both support generated columns, but syntax and function support differ.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">44. Error Handling and Strictness<\/h1>\n\n\n\n<p>PostgreSQL is stricter than MySQL in many situations.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Example: Invalid Integer<\/h2>\n\n\n\n<p>MySQL may allow or coerce depending on SQL mode:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>INSERT INTO users(id) VALUES ('abc');\n<\/code><\/pre>\n\n\n\n<p>PostgreSQL will reject invalid type conversion.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Example: Group By<\/h2>\n\n\n\n<p>PostgreSQL requires correct grouping.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Example: Transaction Error<\/h2>\n\n\n\n<p>PostgreSQL aborts the whole transaction after an error until rollback.<\/p>\n\n\n\n<p>This strictness is actually one reason many developers like PostgreSQL: fewer silent surprises.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">45. DDL Differences<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">Create Database<\/h2>\n\n\n\n<p>MySQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE DATABASE shop;\n<\/code><\/pre>\n\n\n\n<p>PostgreSQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE DATABASE shop;\n<\/code><\/pre>\n\n\n\n<p>Same.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Use Database<\/h2>\n\n\n\n<p>MySQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>USE shop;\n<\/code><\/pre>\n\n\n\n<p>PostgreSQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\\c shop\n<\/code><\/pre>\n\n\n\n<p>In PostgreSQL, <code>\\c<\/code> is a <code>psql<\/code> command, not SQL.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Create Table<\/h2>\n\n\n\n<p>MySQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE TABLE users (\n    id BIGINT AUTO_INCREMENT PRIMARY KEY,\n    name VARCHAR(100) NOT NULL\n);\n<\/code><\/pre>\n\n\n\n<p>PostgreSQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE TABLE users (\n    id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,\n    name VARCHAR(100) NOT NULL\n);\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Drop Table<\/h2>\n\n\n\n<p>Same:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>DROP TABLE users;\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Truncate<\/h2>\n\n\n\n<p>Same:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>TRUNCATE TABLE users;\n<\/code><\/pre>\n\n\n\n<p>PostgreSQL can restart identity:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>TRUNCATE TABLE users RESTART IDENTITY;\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">46. Admin Commands Mapping<\/h1>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Task<\/th><th>MySQL<\/th><th>PostgreSQL<\/th><\/tr><\/thead><tbody><tr><td>Start server<\/td><td><code>systemctl start mysql<\/code><\/td><td><code>systemctl start postgresql<\/code><\/td><\/tr><tr><td>Stop server<\/td><td><code>systemctl stop mysql<\/code><\/td><td><code>systemctl stop postgresql<\/code><\/td><\/tr><tr><td>Restart server<\/td><td><code>systemctl restart mysql<\/code><\/td><td><code>systemctl restart postgresql<\/code><\/td><\/tr><tr><td>Reload config<\/td><td><code>systemctl reload mysql<\/code><\/td><td><code>SELECT pg_reload_conf();<\/code> or service reload<\/td><\/tr><tr><td>Check variables<\/td><td><code>SHOW VARIABLES;<\/code><\/td><td><code>SHOW ALL;<\/code><\/td><\/tr><tr><td>Check status<\/td><td><code>SHOW STATUS;<\/code><\/td><td><code>pg_stat_*<\/code> views<\/td><\/tr><tr><td>List sessions<\/td><td><code>SHOW PROCESSLIST;<\/code><\/td><td><code>pg_stat_activity<\/code><\/td><\/tr><tr><td>Kill session<\/td><td><code>KILL id;<\/code><\/td><td><code>pg_terminate_backend(pid)<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>PostgreSQL reload config:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT pg_reload_conf();\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">47. Information Schema and Catalogs<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT *\nFROM information_schema.tables\nWHERE table_schema = 'shop';\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT *\nFROM information_schema.tables\nWHERE table_schema = 'public';\n<\/code><\/pre>\n\n\n\n<p>PostgreSQL also has richer internal catalogs:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT *\nFROM pg_catalog.pg_class;\n<\/code><\/pre>\n\n\n\n<p>List user tables:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT schemaname, tablename\nFROM pg_tables\nWHERE schemaname = 'public';\n<\/code><\/pre>\n\n\n\n<p>List columns:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT\n    column_name,\n    data_type,\n    is_nullable\nFROM information_schema.columns\nWHERE table_schema = 'public'\n  AND table_name = 'users';\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">48. Security Model Mapping<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL Security Model<\/h2>\n\n\n\n<p>MySQL commonly combines:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>User\nHost\nPassword\nPrivileges\n<\/code><\/pre>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE USER 'reporter'@'10.%' IDENTIFIED BY 'secret';\nGRANT SELECT ON shop.* TO 'reporter'@'10.%';\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL Security Model<\/h2>\n\n\n\n<p>PostgreSQL separates:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Role\nDatabase permission\nSchema permission\nObject permission\nAuthentication rule\n<\/code><\/pre>\n\n\n\n<p>Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE ROLE reporter WITH LOGIN PASSWORD 'secret';\n\nGRANT CONNECT ON DATABASE shop TO reporter;\nGRANT USAGE ON SCHEMA public TO reporter;\nGRANT SELECT ON ALL TABLES IN SCHEMA public TO reporter;\n<\/code><\/pre>\n\n\n\n<p>And in <code>pg_hba.conf<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>host    shop    reporter    10.0.0.0\/8    scram-sha-256\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL Row-Level Security<\/h2>\n\n\n\n<p>PostgreSQL has strong row-level security.<\/p>\n\n\n\n<p>Enable:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ALTER TABLE orders ENABLE ROW LEVEL SECURITY;\n<\/code><\/pre>\n\n\n\n<p>Create policy:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE POLICY user_orders_policy\nON orders\nFOR SELECT\nUSING (customer_id = current_setting('app.customer_id')::BIGINT);\n<\/code><\/pre>\n\n\n\n<p>This is more advanced, but powerful for multi-tenant systems.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">49. Schema Design Difference<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL Style<\/h2>\n\n\n\n<p>You may create multiple databases:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>shop\nshop_audit\nshop_reporting\n<\/code><\/pre>\n\n\n\n<p>And query between them:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT *\nFROM shop.orders o\nJOIN shop_reporting.order_summary s ON o.id = s.order_id;\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL Style<\/h2>\n\n\n\n<p>You may create one database with multiple schemas:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>shop database\n \u251c\u2500\u2500 app\n \u251c\u2500\u2500 audit\n \u251c\u2500\u2500 reporting\n \u2514\u2500\u2500 staging\n<\/code><\/pre>\n\n\n\n<p>Query:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT *\nFROM app.orders o\nJOIN reporting.order_summary s ON o.id = s.order_id;\n<\/code><\/pre>\n\n\n\n<p>This is one of the best PostgreSQL habits to learn early.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">50. Practical Side-by-Side Example<\/h1>\n\n\n\n<p>Let us build the same small application schema in both databases.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL Version<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE DATABASE shop;\nUSE shop;\n\nCREATE TABLE customers (\n    id BIGINT AUTO_INCREMENT PRIMARY KEY,\n    email VARCHAR(255) NOT NULL UNIQUE,\n    name VARCHAR(100) NOT NULL,\n    is_active TINYINT(1) NOT NULL DEFAULT 1,\n    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP\n);\n\nCREATE TABLE orders (\n    id BIGINT AUTO_INCREMENT PRIMARY KEY,\n    customer_id BIGINT NOT NULL,\n    order_total DECIMAL(10,2) NOT NULL,\n    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n    CONSTRAINT fk_orders_customer\n        FOREIGN KEY (customer_id)\n        REFERENCES customers(id)\n);\n\nCREATE INDEX idx_orders_customer_id\nON orders(customer_id);\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL Version<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE DATABASE shop;\n<\/code><\/pre>\n\n\n\n<p>Connect:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\\c shop\n<\/code><\/pre>\n\n\n\n<p>Create schema:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE SCHEMA app;\n<\/code><\/pre>\n\n\n\n<p>Create tables:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE TABLE app.customers (\n    id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,\n    email VARCHAR(255) NOT NULL UNIQUE,\n    name VARCHAR(100) NOT NULL,\n    is_active BOOLEAN NOT NULL DEFAULT TRUE,\n    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\n);\n\nCREATE TABLE app.orders (\n    id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,\n    customer_id BIGINT NOT NULL,\n    order_total NUMERIC(10,2) NOT NULL,\n    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n    CONSTRAINT fk_orders_customer\n        FOREIGN KEY (customer_id)\n        REFERENCES app.customers(id)\n);\n\nCREATE INDEX idx_orders_customer_id\nON app.orders(customer_id);\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Key Differences in This Example<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>MySQL<\/th><th>PostgreSQL<\/th><\/tr><\/thead><tbody><tr><td><code>AUTO_INCREMENT<\/code><\/td><td><code>GENERATED ALWAYS AS IDENTITY<\/code><\/td><\/tr><tr><td><code>TINYINT(1)<\/code><\/td><td><code>BOOLEAN<\/code><\/td><\/tr><tr><td><code>DATETIME<\/code><\/td><td><code>TIMESTAMPTZ<\/code><\/td><\/tr><tr><td><code>DECIMAL<\/code><\/td><td><code>NUMERIC<\/code><\/td><\/tr><tr><td>Database used as namespace<\/td><td>Schema used as namespace<\/td><\/tr><tr><td><code>USE shop<\/code><\/td><td><code>\\c shop<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">51. Common Migration Mapping<\/h1>\n\n\n\n<p>When converting MySQL schema to PostgreSQL:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>MySQL<\/th><th>Convert To PostgreSQL<\/th><\/tr><\/thead><tbody><tr><td><code>AUTO_INCREMENT<\/code><\/td><td><code>GENERATED BY DEFAULT AS IDENTITY<\/code><\/td><\/tr><tr><td><code>TINYINT(1)<\/code><\/td><td><code>BOOLEAN<\/code><\/td><\/tr><tr><td><code>DATETIME<\/code><\/td><td><code>TIMESTAMP<\/code> or <code>TIMESTAMPTZ<\/code><\/td><\/tr><tr><td><code>LONGTEXT<\/code><\/td><td><code>TEXT<\/code><\/td><\/tr><tr><td><code>BLOB<\/code><\/td><td><code>BYTEA<\/code><\/td><\/tr><tr><td><code>JSON<\/code><\/td><td><code>JSONB<\/code><\/td><\/tr><tr><td><code>ENUM<\/code><\/td><td>PostgreSQL enum or lookup table<\/td><\/tr><tr><td><code>UNSIGNED INT<\/code><\/td><td>Larger signed type or check constraint<\/td><\/tr><tr><td><code>ON DUPLICATE KEY UPDATE<\/code><\/td><td><code>ON CONFLICT DO UPDATE<\/code><\/td><\/tr><tr><td><code>INSERT IGNORE<\/code><\/td><td><code>ON CONFLICT DO NOTHING<\/code><\/td><\/tr><tr><td>Backticks<\/td><td>Double quotes or no quotes<\/td><\/tr><tr><td><code>IFNULL()<\/code><\/td><td><code>COALESCE()<\/code><\/td><\/tr><tr><td><code>DATE_FORMAT()<\/code><\/td><td><code>to_char()<\/code><\/td><\/tr><tr><td><code>STR_TO_DATE()<\/code><\/td><td><code>to_date()<\/code> \/ <code>to_timestamp()<\/code><\/td><\/tr><tr><td><code>GROUP_CONCAT()<\/code><\/td><td><code>string_agg()<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Function Mapping<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>MySQL Function<\/th><th>PostgreSQL Function<\/th><\/tr><\/thead><tbody><tr><td><code>IFNULL(a,b)<\/code><\/td><td><code>COALESCE(a,b)<\/code><\/td><\/tr><tr><td><code>NOW()<\/code><\/td><td><code>NOW()<\/code><\/td><\/tr><tr><td><code>CURDATE()<\/code><\/td><td><code>CURRENT_DATE<\/code><\/td><\/tr><tr><td><code>CONCAT()<\/code><\/td><td><code>CONCAT()<\/code> or `<\/td><\/tr><tr><td><code>GROUP_CONCAT()<\/code><\/td><td><code>string_agg()<\/code><\/td><\/tr><tr><td><code>DATE_FORMAT()<\/code><\/td><td><code>to_char()<\/code><\/td><\/tr><tr><td><code>JSON_EXTRACT()<\/code><\/td><td><code>-&gt;<\/code>, <code>-&gt;&gt;<\/code>, <code>jsonb_path_query<\/code><\/td><\/tr><tr><td><code>RAND()<\/code><\/td><td><code>random()<\/code><\/td><\/tr><tr><td><code>UUID()<\/code><\/td><td><code>gen_random_uuid()<\/code> with <code>pgcrypto<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>Example MySQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT GROUP_CONCAT(name)\nFROM users;\n<\/code><\/pre>\n\n\n\n<p>PostgreSQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT string_agg(name, ',')\nFROM users;\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">52. Quoting Difference<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">MySQL<\/h2>\n\n\n\n<p>MySQL often uses backticks:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT `user`, `order`\nFROM `my_table`;\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">PostgreSQL<\/h2>\n\n\n\n<p>PostgreSQL uses double quotes for identifiers:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT \"user\", \"order\"\nFROM \"my_table\";\n<\/code><\/pre>\n\n\n\n<p>But best practice:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Avoid reserved words and mixed-case identifiers.\nUse lowercase snake_case.\n<\/code><\/pre>\n\n\n\n<p>Good PostgreSQL style:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE TABLE customer_orders (\n    id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,\n    order_status TEXT NOT NULL\n);\n<\/code><\/pre>\n\n\n\n<p>Avoid:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE TABLE \"CustomerOrders\" (\n    \"ID\" BIGINT\n);\n<\/code><\/pre>\n\n\n\n<p>Because then you must quote it forever.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">53. Naming Conventions<\/h1>\n\n\n\n<p>Recommended PostgreSQL style:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>lowercase\nsnake_case\nplural or singular consistently\navoid reserved words\navoid quoted identifiers\n<\/code><\/pre>\n\n\n\n<p>Good:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>customer_orders\ncreated_at\nis_active\norder_total\n<\/code><\/pre>\n\n\n\n<p>Avoid:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CustomerOrders\nOrder\nUser\nCreatedAt\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">54. PostgreSQL Features MySQL Users Should Appreciate<\/h1>\n\n\n\n<p>PostgreSQL has several powerful features that MySQL users may not expect.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Strong JSONB<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>payload JSONB\n<\/code><\/pre>\n\n\n\n<p>with GIN indexes.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Partial Indexes<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE INDEX idx_active_users\nON users(email)\nWHERE is_active = TRUE;\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Expression Indexes<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE INDEX idx_lower_email\nON users(LOWER(email));\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Rich Data Types<\/h2>\n\n\n\n<p>PostgreSQL supports:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>UUID\nJSONB\nARRAY\nRANGE\nINET\nCIDR\nMACADDR\nTSVECTOR\nGEOMETRY through PostGIS\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Extensions<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE EXTENSION postgis;\nCREATE EXTENSION pg_stat_statements;\nCREATE EXTENSION pgcrypto;\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Transactional DDL<\/h2>\n\n\n\n<p>Rollback many schema changes safely.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Powerful CTEs<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>WITH active_customers AS (\n    SELECT *\n    FROM customers\n    WHERE is_active = TRUE\n)\nSELECT *\nFROM active_customers;\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Returning Clause<\/h2>\n\n\n\n<p>PostgreSQL can return inserted\/updated\/deleted rows:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>INSERT INTO users (name)\nVALUES ('Alice')\nRETURNING id, name;\n<\/code><\/pre>\n\n\n\n<p>This is extremely useful.<\/p>\n\n\n\n<p>MySQL has some newer returning support in limited areas, but PostgreSQL\u2019s <code>RETURNING<\/code> is widely used and very natural.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">55. Common PostgreSQL Surprises for MySQL Users<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">Surprise 1: Users Are Roles<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>User = role with LOGIN\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Surprise 2: Schema Is Not Database<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>database \u2192 schema \u2192 table\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Surprise 3: Permissions Need Schema Grants<\/h2>\n\n\n\n<p>This is not enough:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>GRANT SELECT ON ALL TABLES IN SCHEMA public TO appuser;\n<\/code><\/pre>\n\n\n\n<p>You also often need:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>GRANT USAGE ON SCHEMA public TO appuser;\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Surprise 4: Sequences Need Permissions<\/h2>\n\n\n\n<p>For identity\/serial inserts, you may need:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>GRANT USAGE ON ALL SEQUENCES IN SCHEMA public TO appuser;\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Surprise 5: Failed Transaction Requires Rollback<\/h2>\n\n\n\n<p>After an error:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ROLLBACK;\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Surprise 6: Case Sensitivity<\/h2>\n\n\n\n<p>PostgreSQL does not behave like case-insensitive MySQL collations by default.<\/p>\n\n\n\n<p>Use:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ILIKE\nLOWER()\ncitext\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Surprise 7: Vacuum Matters<\/h2>\n\n\n\n<p>Autovacuum is essential.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Surprise 8: No <code>USE database<\/code><\/h2>\n\n\n\n<p>Use:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\\c database_name\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Surprise 9: No Backticks<\/h2>\n\n\n\n<p>Use double quotes only when necessary.<\/p>\n\n\n\n<p>Better: avoid quoting by using lowercase names.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Surprise 10: <code>EXPLAIN ANALYZE<\/code> Runs the Query<\/h2>\n\n\n\n<p>Do not use carelessly with destructive SQL.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">56. Quick Reference Cheat Sheet<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">Object Hierarchy<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>MySQL:\nserver \u2192 database\/schema \u2192 table\n\nPostgreSQL:\ncluster\/server \u2192 database \u2192 schema \u2192 table\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">User System<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>MySQL:\n'user'@'host'\n\nPostgreSQL:\nrole + pg_hba.conf\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Auto Increment<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>MySQL:\nAUTO_INCREMENT\n\nPostgreSQL:\nGENERATED ALWAYS AS IDENTITY\nsequence\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Logs<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>MySQL:\nbinary log, redo log, slow query log\n\nPostgreSQL:\nWAL, server log, slow query via log_min_duration_statement\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Monitoring<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>MySQL:\nSHOW PROCESSLIST\nSHOW STATUS\nperformance_schema\n\nPostgreSQL:\npg_stat_activity\npg_stat_database\npg_stat_user_tables\npg_stat_statements\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Backup<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>MySQL:\nmysqldump\n\nPostgreSQL:\npg_dump\npg_restore\npg_basebackup\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Replication<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>MySQL:\nbinlog replication\n\nPostgreSQL:\nWAL streaming replication\nlogical replication\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">57. Recommended Learning Order for a MySQL Expert<\/h1>\n\n\n\n<p>Study PostgreSQL in this order:<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 1: Basic Navigation<\/h2>\n\n\n\n<p>Learn:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\\l\n\\c\n\\dn\n\\dt\n\\d\n\\du\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Step 2: Database vs Schema<\/h2>\n\n\n\n<p>Understand:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>database \u2192 schema \u2192 table\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Step 3: Roles and Permissions<\/h2>\n\n\n\n<p>Understand:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>role with LOGIN = user\nrole without LOGIN = group-like role\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Step 4: Data Type Differences<\/h2>\n\n\n\n<p>Focus on:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>BOOLEAN\nTEXT\nTIMESTAMPTZ\nNUMERIC\nJSONB\nUUID\nARRAY\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Step 5: Auto Increment<\/h2>\n\n\n\n<p>Learn:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>GENERATED ALWAYS AS IDENTITY\n<\/code><\/pre>\n\n\n\n<p>and sequences.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 6: Upsert<\/h2>\n\n\n\n<p>Learn:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ON CONFLICT DO UPDATE\nON CONFLICT DO NOTHING\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Step 7: Monitoring<\/h2>\n\n\n\n<p>Learn:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>pg_stat_activity\npg_stat_user_tables\npg_stat_statements\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Step 8: Performance<\/h2>\n\n\n\n<p>Learn:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>EXPLAIN ANALYZE\nEXPLAIN (ANALYZE, BUFFERS)\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Step 9: Vacuum<\/h2>\n\n\n\n<p>Understand:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>MVCC\ndead tuples\nautovacuum\nVACUUM ANALYZE\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Step 10: Backup and Replication<\/h2>\n\n\n\n<p>Learn:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>pg_dump\npg_restore\npg_basebackup\nWAL\nstreaming replication\nlogical replication\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">58. Final Mental Translation<\/h1>\n\n\n\n<p>When you see this in MySQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Create user appuser@localhost and grant access to database shop.\n<\/code><\/pre>\n\n\n\n<p>Think PostgreSQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Create a role with LOGIN.\nAllow connection through pg_hba.conf.\nGrant CONNECT on database.\nGrant USAGE on schema.\nGrant privileges on tables.\nGrant privileges on sequences if needed.\n<\/code><\/pre>\n\n\n\n<p>When you see this in MySQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Create database shop and tables inside it.\n<\/code><\/pre>\n\n\n\n<p>Think PostgreSQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Create database shop.\nCreate schema app or use public.\nCreate tables inside that schema.\n<\/code><\/pre>\n\n\n\n<p>When you see this in MySQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>AUTO_INCREMENT\n<\/code><\/pre>\n\n\n\n<p>Think PostgreSQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>IDENTITY column backed by sequence.\n<\/code><\/pre>\n\n\n\n<p>When you see this in MySQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Binary log replication\n<\/code><\/pre>\n\n\n\n<p>Think PostgreSQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>WAL streaming or logical replication.\n<\/code><\/pre>\n\n\n\n<p>When you see this in MySQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Performance Schema\n<\/code><\/pre>\n\n\n\n<p>Think PostgreSQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>pg_stat_* views and pg_stat_statements.\n<\/code><\/pre>\n\n\n\n<p>When you see this in MySQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>InnoDB purge cleans old versions.\n<\/code><\/pre>\n\n\n\n<p>Think PostgreSQL:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Autovacuum cleans dead tuples.\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Conclusion<\/h1>\n\n\n\n<p>PostgreSQL is not \u201cMySQL with different syntax.\u201d It has a different internal model.<\/p>\n\n\n\n<p>But once you map the major ideas, it becomes much easier:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>MySQL database\/schema        \u2192 PostgreSQL database + schema\nMySQL user@host              \u2192 PostgreSQL role + pg_hba.conf\nAUTO_INCREMENT               \u2192 identity\/sequence\nBinary log                   \u2192 WAL\nSHOW PROCESSLIST             \u2192 pg_stat_activity\nPerformance Schema           \u2192 pg_stat_* views\nmysqldump                    \u2192 pg_dump\nON DUPLICATE KEY UPDATE      \u2192 ON CONFLICT DO UPDATE\nInnoDB purge                 \u2192 VACUUM\/autovacuum\nStorage engines              \u2192 PostgreSQL native storage + table types\n<\/code><\/pre>\n\n\n\n<p>The most important PostgreSQL concepts for a MySQL user are:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Roles\nSchemas\nSequences\nWAL\nMVCC\nAutovacuum\npg_hba.conf\nJSONB\nExtensions\nEXPLAIN ANALYZE\npg_stat_* views\n<\/code><\/pre>\n\n\n\n<p>Once these are clear, PostgreSQL starts to feel very logical \u2014 just stricter, more layered, and more explicit than MySQL.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Introduction If you already know MySQL well, learning PostgreSQL becomes much easier when you stop trying to memorize PostgreSQL from [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-2836","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.5 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>MySQL vs PostgreSQL: Comprehensive Guide for MySQL Users Learning PostgreSQL - SRE School<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/sreschool.com\/blog\/mysql-vs-postgresql-comprehensive-guide-for-mysql-users-learning-postgresql\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"MySQL vs PostgreSQL: Comprehensive Guide for MySQL Users Learning PostgreSQL - SRE School\" \/>\n<meta property=\"og:description\" content=\"Introduction If you already know MySQL well, learning PostgreSQL becomes much easier when you stop trying to memorize PostgreSQL from [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/sreschool.com\/blog\/mysql-vs-postgresql-comprehensive-guide-for-mysql-users-learning-postgresql\/\" \/>\n<meta property=\"og:site_name\" content=\"SRE School\" \/>\n<meta property=\"article:published_time\" content=\"2026-05-06T01:18:23+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-05-06T01:18:24+00:00\" \/>\n<meta name=\"author\" content=\"Rajesh Kumar\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Rajesh Kumar\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"16 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/sreschool.com\/blog\/mysql-vs-postgresql-comprehensive-guide-for-mysql-users-learning-postgresql\/\",\"url\":\"https:\/\/sreschool.com\/blog\/mysql-vs-postgresql-comprehensive-guide-for-mysql-users-learning-postgresql\/\",\"name\":\"MySQL vs PostgreSQL: Comprehensive Guide for MySQL Users Learning PostgreSQL - SRE School\",\"isPartOf\":{\"@id\":\"https:\/\/sreschool.com\/blog\/#website\"},\"datePublished\":\"2026-05-06T01:18:23+00:00\",\"dateModified\":\"2026-05-06T01:18:24+00:00\",\"author\":{\"@id\":\"https:\/\/sreschool.com\/blog\/#\/schema\/person\/0ffe446f77bb2589992dbe3a7f417201\"},\"breadcrumb\":{\"@id\":\"https:\/\/sreschool.com\/blog\/mysql-vs-postgresql-comprehensive-guide-for-mysql-users-learning-postgresql\/#breadcrumb\"},\"inLanguage\":\"en\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/sreschool.com\/blog\/mysql-vs-postgresql-comprehensive-guide-for-mysql-users-learning-postgresql\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/sreschool.com\/blog\/mysql-vs-postgresql-comprehensive-guide-for-mysql-users-learning-postgresql\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/sreschool.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"MySQL vs PostgreSQL: Comprehensive Guide for MySQL Users Learning PostgreSQL\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/sreschool.com\/blog\/#website\",\"url\":\"https:\/\/sreschool.com\/blog\/\",\"name\":\"SRESchool\",\"description\":\"Master SRE. Build Resilient Systems. Lead the Future of Reliability\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/sreschool.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/sreschool.com\/blog\/#\/schema\/person\/0ffe446f77bb2589992dbe3a7f417201\",\"name\":\"Rajesh Kumar\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en\",\"@id\":\"https:\/\/sreschool.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/f901a4f2929fa034a291a8363d589791d5a3c1f6a051c22e744acb8bfc8e022a?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/f901a4f2929fa034a291a8363d589791d5a3c1f6a051c22e744acb8bfc8e022a?s=96&d=mm&r=g\",\"caption\":\"Rajesh Kumar\"},\"sameAs\":[\"http:\/\/sreschool.com\/blog\"],\"url\":\"https:\/\/sreschool.com\/blog\/author\/admin\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"MySQL vs PostgreSQL: Comprehensive Guide for MySQL Users Learning PostgreSQL - SRE School","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/sreschool.com\/blog\/mysql-vs-postgresql-comprehensive-guide-for-mysql-users-learning-postgresql\/","og_locale":"en_US","og_type":"article","og_title":"MySQL vs PostgreSQL: Comprehensive Guide for MySQL Users Learning PostgreSQL - SRE School","og_description":"Introduction If you already know MySQL well, learning PostgreSQL becomes much easier when you stop trying to memorize PostgreSQL from [&hellip;]","og_url":"https:\/\/sreschool.com\/blog\/mysql-vs-postgresql-comprehensive-guide-for-mysql-users-learning-postgresql\/","og_site_name":"SRE School","article_published_time":"2026-05-06T01:18:23+00:00","article_modified_time":"2026-05-06T01:18:24+00:00","author":"Rajesh Kumar","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Rajesh Kumar","Est. reading time":"16 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/sreschool.com\/blog\/mysql-vs-postgresql-comprehensive-guide-for-mysql-users-learning-postgresql\/","url":"https:\/\/sreschool.com\/blog\/mysql-vs-postgresql-comprehensive-guide-for-mysql-users-learning-postgresql\/","name":"MySQL vs PostgreSQL: Comprehensive Guide for MySQL Users Learning PostgreSQL - SRE School","isPartOf":{"@id":"https:\/\/sreschool.com\/blog\/#website"},"datePublished":"2026-05-06T01:18:23+00:00","dateModified":"2026-05-06T01:18:24+00:00","author":{"@id":"https:\/\/sreschool.com\/blog\/#\/schema\/person\/0ffe446f77bb2589992dbe3a7f417201"},"breadcrumb":{"@id":"https:\/\/sreschool.com\/blog\/mysql-vs-postgresql-comprehensive-guide-for-mysql-users-learning-postgresql\/#breadcrumb"},"inLanguage":"en","potentialAction":[{"@type":"ReadAction","target":["https:\/\/sreschool.com\/blog\/mysql-vs-postgresql-comprehensive-guide-for-mysql-users-learning-postgresql\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/sreschool.com\/blog\/mysql-vs-postgresql-comprehensive-guide-for-mysql-users-learning-postgresql\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/sreschool.com\/blog\/"},{"@type":"ListItem","position":2,"name":"MySQL vs PostgreSQL: Comprehensive Guide for MySQL Users Learning PostgreSQL"}]},{"@type":"WebSite","@id":"https:\/\/sreschool.com\/blog\/#website","url":"https:\/\/sreschool.com\/blog\/","name":"SRESchool","description":"Master SRE. Build Resilient Systems. Lead the Future of Reliability","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/sreschool.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en"},{"@type":"Person","@id":"https:\/\/sreschool.com\/blog\/#\/schema\/person\/0ffe446f77bb2589992dbe3a7f417201","name":"Rajesh Kumar","image":{"@type":"ImageObject","inLanguage":"en","@id":"https:\/\/sreschool.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/f901a4f2929fa034a291a8363d589791d5a3c1f6a051c22e744acb8bfc8e022a?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/f901a4f2929fa034a291a8363d589791d5a3c1f6a051c22e744acb8bfc8e022a?s=96&d=mm&r=g","caption":"Rajesh Kumar"},"sameAs":["http:\/\/sreschool.com\/blog"],"url":"https:\/\/sreschool.com\/blog\/author\/admin\/"}]}},"_links":{"self":[{"href":"https:\/\/sreschool.com\/blog\/wp-json\/wp\/v2\/posts\/2836","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/sreschool.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sreschool.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sreschool.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/sreschool.com\/blog\/wp-json\/wp\/v2\/comments?post=2836"}],"version-history":[{"count":1,"href":"https:\/\/sreschool.com\/blog\/wp-json\/wp\/v2\/posts\/2836\/revisions"}],"predecessor-version":[{"id":2837,"href":"https:\/\/sreschool.com\/blog\/wp-json\/wp\/v2\/posts\/2836\/revisions\/2837"}],"wp:attachment":[{"href":"https:\/\/sreschool.com\/blog\/wp-json\/wp\/v2\/media?parent=2836"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sreschool.com\/blog\/wp-json\/wp\/v2\/categories?post=2836"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sreschool.com\/blog\/wp-json\/wp\/v2\/tags?post=2836"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}