PostgreSQL vs MySQL vs SQL Server: The Enterprise Database Decision, Made on Technical Merit
Every enterprise database evaluation lands on the same shortlist: PostgreSQL, MySQL, and SQL Server. The problem is that the decision is usually made based on organizational inertia -- what a consultant recommended in 2015, or which database the senior architect used at their previous company -- rather than current technical merit.
The three databases have diverged significantly in recent years. PostgreSQL has added parallel query execution and a mature extension ecosystem, SQL Server has moved to Linux, and MySQL has closed long-standing feature gaps with version 8. The landscape is materially different from even three years ago.
This is the technical comparison I wish someone had handed me before my last procurement cycle. Concrete differences, real cost numbers, and clear guidance on when each database is the right choice.
Licensing and Cost: The Elephant in the Room
Before discussing any technical feature, let us talk about the number that dominates every enterprise database decision: licensing cost.
SQL Server licensing is complex and expensive. Enterprise Edition runs approximately $15,123 per two-core pack (retail). A typical 16-core production server requires 8 core packs: $120,984 in licensing alone. Standard Edition is cheaper at roughly $3,945 per two-core pack ($31,560 for 16 cores), but it caps memory at 128GB and limits many Enterprise features. Add Software Assurance (roughly 25% annually for updates and support), and you are paying $30,000-$40,000 per year just to keep the lights on for a single server. A primary plus two replicas? Triple those numbers.
PostgreSQL is free. Completely, irrevocably, no-strings-attached free under the PostgreSQL License. There is no Enterprise Edition with features held back. Every feature -- logical replication, parallel query, partitioning, full-text search -- is included. Your costs are hardware, hosting, and operational expertise. For that same 16-core server: $0 in licensing.
MySQL is also free under the GPL, but with an asterisk. Oracle owns MySQL and offers a commercial Enterprise Edition ($5,000-$10,000 per server per year) with additional monitoring tools, backup utilities, and security features. The Community Edition is fully functional, but Oracle's ownership creates strategic uncertainty that makes some enterprise procurement teams nervous. For that 16-core server: $0 for Community, $5,000-$10,000 for Enterprise.
Here is the concrete math for a modest production deployment (primary + 2 read replicas, 16 cores each):
| Cost Component | SQL Server Enterprise | PostgreSQL | MySQL Community |
|---|---|---|---|
| License (3 servers) | $362,952 | $0 | $0 |
| Annual maintenance/SA | $90,738 | $0 | $0 |
| Support contract | Included in SA | ~$15,000 (third-party) | ~$10,000 (third-party) |
| Year 1 total | $453,690 | ~$15,000 | ~$10,000 |
| Year 2+ annual | $90,738 | ~$15,000 | ~$10,000 |
SQL Server's total cost of ownership over five years approaches $800,000 for this configuration. The same hardware running PostgreSQL costs roughly $75,000 in support contracts. That delta funds a full-time DBA with budget left over.
SQL Standard Compliance
If you care about writing portable, standards-compliant SQL -- and you should, because it reduces vendor lock-in and makes hiring easier -- the three databases differ meaningfully.
PostgreSQL is the most SQL-standard-compliant relational database available. It implements the majority of SQL:2023, including advanced features like MERGE, lateral joins, GROUPING SETS, CUBE, and ROLLUP. When PostgreSQL deviates from the standard, it is typically to add functionality, not to omit it.
SQL Server implements T-SQL, Microsoft's proprietary extension of standard SQL. T-SQL adds useful features (variables, control flow, error handling via TRY...CATCH), but it also introduces non-standard behavior that creates lock-in. TOP instead of LIMIT, ISNULL() instead of COALESCE() (though it supports both), += assignment operators, and proprietary date functions are endemic in T-SQL codebases. The code works, but it only works on SQL Server.
MySQL has the most idiosyncratic SQL behavior of the three. Historically, its GROUP BY behavior was non-standard -- you could SELECT columns not in the GROUP BY clause without an aggregate function, and MySQL would silently return an arbitrary value from the group. MySQL 8 defaults to ONLY_FULL_GROUP_BY mode, but legacy applications may depend on the old behavior. Other surprises:
- String comparisons are case-insensitive by default (collation
utf8mb4_0900_ai_ci), which catches every developer migrating from PostgreSQL or SQL Server -
TRUNCATE TABLEdoes an implicit commit and cannot be rolled back, even inside a transaction - Division by zero returns
NULLinstead of raising an error (unless strict mode is enabled) -
TIMESTAMPcolumns auto-update on row modification unless explicitly told not to
-- MySQL's default case-insensitive comparison
SELECT * FROM users WHERE username = 'Admin';
-- Returns rows where username is 'admin', 'ADMIN', 'Admin', etc.
-- PostgreSQL: case-sensitive by default
SELECT * FROM users WHERE username = 'Admin';
-- Returns ONLY rows where username is exactly 'Admin'
-- SQL Server: depends on collation (default is case-insensitive)
SELECT * FROM users WHERE username = 'Admin';
-- Default collation: returns 'admin', 'ADMIN', 'Admin', etc.
Advanced SQL Features
This is where the three databases diverge most sharply. The following table summarizes support for features that matter in real applications:
| Feature | PostgreSQL | SQL Server | MySQL |
|---|---|---|---|
| CTEs (WITH) | Full optimization, materialized/not materialized hints | Full support | Supported (8.0+), less optimized |
| Recursive CTEs | Yes, with cycle detection | Yes | Yes (8.0+) |
| Window functions | Complete (GROUPS, EXCLUDE, FILTER) | Complete (most features) | Basic (no GROUPS, no EXCLUDE) |
| JSONB / JSON | Binary JSONB with GIN indexing | JSON with computed column indexing | JSON with generated column indexing |
| Full-text search | Built-in (tsvector, ranking, stemming) | Built-in (excellent, with thesaurus) | Basic (InnoDB FTS, no stemming) |
| Geospatial | PostGIS (industry standard) | Spatial types (basic) | Spatial types (basic) |
| Array types | Native | No | No |
| Range types | Native with exclusion constraints | No | No |
| Lateral joins | Yes | CROSS APPLY / OUTER APPLY | Yes (8.0.14+) |
| MERGE statement | Yes (15+) | Yes (mature) | No (use INSERT...ON DUPLICATE KEY) |
| Partial indexes | Yes | Filtered indexes | No |
| Generated columns | Stored and virtual | Computed columns (persisted/virtual) | Stored and virtual (8.0+) |
| Table partitioning | Declarative (range, list, hash) | Partition functions/schemes | Declarative (range, list, hash, key) |
| Parallel queries | Full (scans, joins, aggregates) | Full (extensive parallelism) | Limited (COUNT, some reads) |
PostgreSQL and SQL Server are roughly comparable in feature richness. Both handle complex analytical workloads well. MySQL lags behind in areas that matter for enterprise applications -- limited window functions, no array or range types, weaker JSON indexing, and minimal parallel query support.
Stored Procedures and Programmability
All three databases support stored procedures, but the languages differ meaningfully.
SQL Server (T-SQL) has the most mature procedural language. Variables, control flow, structured error handling (TRY...CATCH), and CLR integration for .NET code running inside the database. T-SQL is a complete programming environment:
-- T-SQL: Structured error handling
CREATE PROCEDURE CalculateAccountBalance @AccountId INT, @AsOfDate DATE
AS
BEGIN
BEGIN TRY
SELECT account_id, SUM(CASE WHEN transaction_type = 'credit'
THEN amount ELSE -amount END) AS balance
FROM transactions
WHERE account_id = @AccountId AND transaction_date <= @AsOfDate
GROUP BY account_id;
END TRY
BEGIN CATCH
THROW;
END CATCH
END;
PostgreSQL (PL/pgSQL) is similarly capable, with the unique advantage of supporting multiple procedural languages -- PL/Python, PL/Perl, PL/V8 (JavaScript):
-- PL/pgSQL: Function returning a table
CREATE OR REPLACE FUNCTION calculate_account_balance(
target_account_id INT, as_of_date DATE
) RETURNS TABLE(account_id INT, balance NUMERIC)
LANGUAGE plpgsql AS $$
BEGIN
RETURN QUERY
SELECT t.account_id, COALESCE(SUM(CASE WHEN t.transaction_type = 'credit'
THEN t.amount ELSE -t.amount END), 0.00)
FROM transactions t
WHERE t.account_id = target_account_id AND t.transaction_date <= as_of_date
GROUP BY t.account_id;
END; $$;
MySQL stored procedures (since 5.0) are functional but less refined. Error handling is verbose, and there is no equivalent to PostgreSQL's multi-language support or SQL Server's CLR integration.
Replication and High Availability
High availability is where SQL Server's premium licensing starts to justify its cost -- for organizations that value turnkey HA over operational flexibility.
SQL Server Always On AG is the gold standard for turnkey HA. Synchronous or asynchronous replication, automatic failover (under 30 seconds), readable secondaries, and GUI-driven configuration through Management Studio. The catch: it requires Enterprise Edition. A three-node AG cluster on 16-core servers exceeds $360,000 in licensing.
PostgreSQL offers streaming replication (WAL-level, near-real-time) and logical replication (row-level, enabling selective table replication and zero-downtime major version upgrades). Failover is managed by external tools -- Patroni is the industry standard. The result is highly flexible and free, but requires more operational expertise than SQL Server's integrated approach.
MySQL Group Replication provides multi-primary or single-primary clustering with automatic failover. Combined with MySQL Router and MySQL Shell, InnoDB Cluster is relatively turnkey. The limitation: all tables must have primary keys, and only InnoDB is supported. Group Replication is newer and less battle-tested than the other two options.
Platform Support
SQL Server was Windows-only until 2017. SQL Server on Linux is now generally available and production-ready, but with gaps: SQL Server Agent (job scheduling), SSIS (ETL), SSRS (reporting), and SSAS (analysis services) do not run on Linux. If your organization depends on the BI stack, you still need Windows servers. SQL Server on containers (Docker/Kubernetes) works, but it is not yet the norm for production deployments.
PostgreSQL runs everywhere -- Linux, Windows, macOS, FreeBSD, and Solaris. It is the default relational database on every major cloud platform (AWS RDS/Aurora, GCP Cloud SQL/AlloyDB, Azure Database for PostgreSQL). The overwhelming majority of production deployments run on Linux. Container support is excellent and widely adopted.
MySQL also runs everywhere and is well-supported across all cloud platforms. Like PostgreSQL, most production deployments target Linux.
Extensions and Ecosystem
PostgreSQL Extensions
PostgreSQL's extension system is its strongest differentiator. Extensions run inside the database engine with full access to the query planner, index system, and storage layer:
- PostGIS: The industry-standard spatial database. Used by government agencies, logistics companies, and mapping platforms. Supports 2D/3D geometries, raster data, topology, and geocoding
- TimescaleDB: Hypertables, columnar compression, continuous aggregates, and retention policies for time-series data
- pgvector: Vector similarity search for AI/ML embeddings alongside relational data
- pg_cron: In-database job scheduling
- pg_stat_statements: Normalized query performance tracking
- pg_trgm: Fuzzy string matching with index support
- Citus: Distributed PostgreSQL for horizontal scaling
These extensions are not wrappers or plugins. They extend the core engine. PostGIS adds new data types, index types, and hundreds of spatial functions that the query planner understands natively.
SQL Server Built-In Features
SQL Server takes a different approach: instead of extensions, Microsoft builds features directly into the product. SSIS (ETL), SSRS (reporting), SSAS (OLAP/data mining), Query Store, In-Memory OLTP, and built-in spatial types all ship with the product or as companion tools. The approach is "batteries included" -- everything comes from one vendor, integrates tightly, and is supported under one contract. The tradeoff is that you are locked into Microsoft's implementation and roadmap.
MySQL Extensibility
MySQL's plugin architecture is more limited. There are storage engine plugins (InnoDB, NDB for clustering), authentication plugins, and audit plugins, but nothing comparable to PostGIS or TimescaleDB. If you need spatial analytics, vector search, or time-series capabilities, MySQL requires a separate specialized database.
Monitoring and Observability
Monitoring is an area where SQL Server has a genuine, meaningful advantage that PostgreSQL's ecosystem is still catching up to.
SQL Server Query Store
Query Store is SQL Server's standout monitoring feature. It automatically captures query plans, tracks plan changes over time, identifies regressions, and can force a specific plan for a query that has regressed. This is built into the engine, enabled with a single toggle, and integrated with Management Studio. Combined with Dynamic Management Views (DMVs) for wait statistics, index usage, buffer pool contents, and lock analysis, SQL Server's observability comes out of the box with every edition.
PostgreSQL Monitoring
PostgreSQL exposes its internals through system catalog views -- pg_stat_statements for query performance, pg_stat_activity for live sessions, pg_stat_user_tables for table I/O and vacuum statistics, pg_locks for lock analysis. The auto_explain extension captures execution plans for slow queries automatically.
The raw data is excellent, but PostgreSQL lacks SQL Server's integrated tooling for plan regression detection and automated remediation. This is where ecosystem tools fill the gap. For PostgreSQL, tools like myDBA.dev provide the automated health checks, query plan analysis, and tuning recommendations that SQL Server's Query Store offers natively -- bridging the monitoring gap with EXPLAIN plan capture, plan regression detection, and index advisory across your PostgreSQL fleet.
MySQL Monitoring
MySQL's Performance Schema provides query digests, wait event analysis, and memory instrumentation. It is functional but carries a measurable overhead (typically 5-10%) and the schema is more complex to query directly than either SQL Server's DMVs or PostgreSQL's pg_stat views.
Migration Considerations
The most common enterprise migration path today is SQL Server to PostgreSQL, driven by licensing cost reduction. Key tools:
- pgLoader: Open-source, migrates from MySQL or SQL Server to PostgreSQL with schema translation and data type mapping
- AWS DMS: Continuous replication from SQL Server or MySQL to PostgreSQL, including change data capture
- AWS Babelfish for Aurora PostgreSQL: T-SQL compatibility layer that lets SQL Server applications connect to Aurora PostgreSQL and execute T-SQL natively. Not 100% compatible, but it covers a large T-SQL subset and can dramatically reduce migration effort
- SQL Server Migration Assistant (SSMA): Microsoft's free tool for migrating from MySQL or Oracle to SQL Server
Babelfish is the most significant development here -- it allows organizations to migrate their database to PostgreSQL while deferring the T-SQL stored procedure rewrite, which is typically the most expensive part of the project.
When to Choose Each Database
Choose SQL Server When:
- You are a Microsoft/.NET shop. The integration with Visual Studio, Azure, and Entity Framework is genuinely excellent
- You need the BI suite. SSIS + SSRS + SSAS is a mature, integrated business intelligence stack with no PostgreSQL equivalent
- Turnkey HA matters more than cost. Always On AG is the easiest enterprise HA to configure and operate
- You have existing licenses. Software Assurance makes the marginal cost of additional instances lower than sticker price
Choose PostgreSQL When:
- Cost matters. Deploying multiple production servers saves hundreds of thousands in licensing
- You are cloud-native or Linux-first. PostgreSQL is the default on every major cloud platform
- You need extensions. PostGIS, TimescaleDB, pgvector -- no other relational database handles geospatial, time-series, and vector search natively
- Data integrity is non-negotiable. CHECK, EXCLUDE, partial indexes, deferrable foreign keys, and range types provide guardrails neither SQL Server nor MySQL can match
- You want one database, not five. Extensions replace separate spatial, time-series, vector, and document databases
Choose MySQL When:
- WordPress, Drupal, or legacy PHP. The ecosystem assumes MySQL
- Simple CRUD at high concurrency. The thread model handles thousands of connections natively
- Horizontal scaling with Vitess or PlanetScale. MySQL-compatible distributed databases are more mature for certain sharding patterns
- Team expertise. A team that knows MySQL deeply will outperform during the learning curve
Comprehensive Comparison Table
| Category | PostgreSQL | SQL Server | MySQL |
|---|---|---|---|
| License | PostgreSQL (free, permissive) | Commercial ($15K+ per 2-core pack) | GPL (free) / Commercial |
| Owner | Community (PGDG) | Microsoft | Oracle |
| SQL compliance | Most compliant | T-SQL extensions | Idiosyncratic defaults |
| Connection model | Process-per-connection | Thread-per-connection | Thread-per-connection |
| Parallel queries | Full (scans, joins, aggregates) | Full (extensive) | Limited |
| CTEs / recursive | Full, optimized | Full | Supported (8.0+) |
| Window functions | Complete | Complete | Basic |
| JSON support | JSONB (binary, GIN indexed) | JSON (computed columns) | JSON (generated columns) |
| Geospatial | PostGIS (industry standard) | Built-in (basic) | Built-in (basic) |
| Full-text search | Built-in (stemming, ranking) | Built-in (excellent) | Basic |
| Vector search | pgvector | Preview (2025+) | No |
| Time-series | TimescaleDB | Temporal tables | No |
| Array / range types | Native | No | No |
| Partial indexes | Yes | Filtered indexes | No |
| Stored procedures | PL/pgSQL + multi-language | T-SQL + CLR | MySQL procedures |
| Replication | Streaming + logical | Always On AG | Group Replication |
| HA complexity | External tools (Patroni) | Integrated (WSFC) | MySQL Router/Shell |
| Built-in monitoring | pg_stat_* views | DMVs + Query Store | Performance Schema |
| Plan regression detection | Ecosystem tools | Query Store (built-in) | No |
| Platform support | All major OS | Windows + Linux (gaps) | All major OS |
| Extension ecosystem | Rich (PostGIS, TimescaleDB, etc.) | SSIS/SSRS/SSAS suite | Limited plugins |
| Cloud managed options | All major clouds | Azure, AWS RDS | All major clouds |
| Container support | Excellent | Supported (less common) | Excellent |
The Bottom Line
The enterprise database decision should not be difficult if you separate technical merit from organizational inertia.
SQL Server is the right choice when you are already invested in the Microsoft ecosystem, need the BI suite, and the licensing cost is acceptable. Its integrated tooling -- Query Store, Always On, SSRS -- is genuinely excellent and saves operational effort. Do not underestimate the value of a single vendor supporting your entire data stack.
PostgreSQL is the right choice when cost matters, when you need capabilities beyond basic relational storage, and when you want to avoid vendor lock-in. The extension ecosystem transforms it from a relational database into a platform -- spatial, time-series, vector search, and document storage in a single engine. The monitoring gap that existed five years ago has been largely closed by ecosystem tools.
MySQL is the right choice for simple web workloads, PHP ecosystems, and teams with deep MySQL expertise. It is not the right choice for new enterprise applications that will grow in complexity.
If you are starting a new enterprise project today with no existing constraints, PostgreSQL gives you the broadest capability at the lowest cost. If you choose it, invest the licensing savings in operational expertise and monitoring -- the database is free, but running it well requires the same rigor as any production system.


Top comments (0)