<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Postgresql从入门到精通 on 老张开工了</title>
    <link>/postgresql%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E7%B2%BE%E9%80%9A/</link>
    <description>Recent content in Postgresql从入门到精通 on 老张开工了</description>
    <generator>Hugo</generator>
    <language>zh-cn</language>
    <lastBuildDate>Wed, 05 Mar 2025 10:00:00 +0800</lastBuildDate>
    <atom:link href="/postgresql%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E7%B2%BE%E9%80%9A/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>实战项目：电商数据库设计</title>
      <link>/posts/2025/03/ecommerce-database-project/</link>
      <pubDate>Wed, 05 Mar 2025 10:00:00 +0800</pubDate>
      <guid>/posts/2025/03/ecommerce-database-project/</guid>
      <description>本章将综合运用本系列所学的全部知识，设计一个完整的电商数据库系统——从需求分析、表结构设计、到高级查询和性能优化。&#xA;项目需求概述 构建一个支持多用户、多商品的电商平台后台数据库，主要功能包括：&#xA;用户注册与登录 商品浏览与搜索 购物车与下单 订单管理与支付 商品评价 后台数据分析 E-R 图 ┌──────────┐ ┌──────────────┐ ┌────────────┐ │ 用户 │──────→│ 购物车 │──────→│ 商品 │ └──────────┘ └──────────────┘ └────────────┘ ↓ ↑ ┌──────────┐ ┌──────────────┐ ┌──────────────┐ │ 地址 │ │ 订单 │──────→│ 订单明细 │ └──────────┘ └──────────────┘ └──────────────┘ ↓ ┌──────────────┐ ┌──────────────┐ │ 支付记录 │ │ 商品评价 │ └──────────────┘ └──────────────┘ 数据库设计与建表 -- 创建数据库 CREATE DATABASE ecommerce; \c ecommerce -- ============================ -- 1. 用户模块 -- ============================ CREATE TABLE es_users ( user_id SERIAL PRIMARY KEY, username VARCHAR(50) UNIQUE NOT NULL, email VARCHAR(200) UNIQUE NOT NULL, password_hash VARCHAR(255) NOT NULL, phone VARCHAR(20), avatar_url TEXT, member_level VARCHAR(10) DEFAULT &amp;#39;普通&amp;#39; CHECK (member_level IN (&amp;#39;普通&amp;#39;, &amp;#39;银卡&amp;#39;, &amp;#39;金卡&amp;#39;, &amp;#39;钻石&amp;#39;)), status VARCHAR(10) DEFAULT &amp;#39;active&amp;#39; CHECK (status IN (&amp;#39;active&amp;#39;, &amp;#39;locked&amp;#39;, &amp;#39;deleted&amp;#39;)), created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); -- 用户地址表 CREATE TABLE es_user_addresses ( address_id SERIAL PRIMARY KEY, user_id INTEGER NOT NULL REFERENCES es_users(user_id), receiver_name VARCHAR(50) NOT NULL, receiver_phone VARCHAR(20) NOT NULL, province VARCHAR(50), city VARCHAR(50), district VARCHAR(50), detail_address TEXT NOT NULL, is_default BOOLEAN DEFAULT false, created_at TIMESTAMPTZ DEFAULT NOW() ); -- ============================ -- 2.</description>
    </item>
    <item>
      <title>高级特性（JSON/全文搜索/扩展）</title>
      <link>/posts/2025/03/advanced-features/</link>
      <pubDate>Sat, 01 Mar 2025 10:00:00 +0800</pubDate>
      <guid>/posts/2025/03/advanced-features/</guid>
      <description>PostgreSQL 远不止是一个关系型数据库。它的高级特性让它在很多场景下可以替代专门的 MongoDB（JSON）、Elasticsearch（全文搜索）、甚至 Redis（键值存储）。&#xA;JSON 与 JSONB PostgreSQL 从 9.2 开始支持 JSON，9.4 引入了性能优异的 JSONB。&#xA;JSONB 的核心优势 JSONB 是二进制格式，去掉了空格和键的顺序 支持索引（GIN 索引），查询速度显著快于 JSON 支持高效的操作符（-&amp;gt;、 -&amp;gt;&amp;gt;、 @&amp;gt;、 ?、 ?|、 ?&amp;amp;） -- 创建 JSONB 列 CREATE TABLE products_json ( id SERIAL PRIMARY KEY, name VARCHAR(100), attributes JSONB -- 可变属性的最佳容器 ); INSERT INTO products_json (name, attributes) VALUES (&amp;#39;笔记本电脑&amp;#39;, &amp;#39;{&amp;#34;brand&amp;#34;: &amp;#34;联想&amp;#34;, &amp;#34;ram&amp;#34;: &amp;#34;16GB&amp;#34;, &amp;#34;storage&amp;#34;: &amp;#34;512GB SSD&amp;#34;, &amp;#34;color&amp;#34;: &amp;#34;银灰&amp;#34;, &amp;#34;weight&amp;#34;: 1.4}&amp;#39;), (&amp;#39;手机&amp;#39;, &amp;#39;{&amp;#34;brand&amp;#34;: &amp;#34;华为&amp;#34;, &amp;#34;ram&amp;#34;: &amp;#34;8GB&amp;#34;, &amp;#34;storage&amp;#34;: &amp;#34;256GB&amp;#34;, &amp;#34;color&amp;#34;: &amp;#34;黑色&amp;#34;, &amp;#34;screen&amp;#34;: &amp;#34;6.</description>
    </item>
    <item>
      <title>用户与权限管理</title>
      <link>/posts/2025/02/user-and-permission/</link>
      <pubDate>Thu, 27 Feb 2025 10:00:00 +0800</pubDate>
      <guid>/posts/2025/02/user-and-permission/</guid>
      <description>数据库安全的第一道防线是完善的用户和权限管理体系。PostgreSQL 提供了精细的权限控制模型，从数据库级别到列级别都能控制。&#xA;用户/角色管理 PostgreSQL 中用户和角色概念相通——区别在于用户默认有 LOGIN 权限，角色没有。&#xA;-- 创建用户 CREATE USER alice WITH PASSWORD &amp;#39;alice123&amp;#39;; -- 等价于 CREATE ROLE alice WITH LOGIN PASSWORD &amp;#39;alice123&amp;#39;; -- 创建角色（不用于登录，用于权限分组） CREATE ROLE sales_team; CREATE ROLE dev_team; CREATE USER bob WITH PASSWORD &amp;#39;bob456&amp;#39;; CREATE USER charlie WITH PASSWORD &amp;#39;charlie789&amp;#39;; 修改用户 -- 修改密码 ALTER USER alice WITH PASSWORD &amp;#39;newpassword&amp;#39;; -- 修改属性 ALTER USER alice WITH SUPERUSER; -- 设为超级用户 ALTER USER alice WITH NOSUPERUSER; -- 取消超级用户 ALTER USER alice WITH CREATEDB; -- 允许创建数据库 ALTER USER alice WITH CREATEROLE; -- 允许创建角色 -- 禁用用户 ALTER USER alice WITH NOLOGIN; -- 重命名 ALTER USER alice RENAME TO alice_new; 删除用户 -- 删除前确认没有依赖的数据库对象 REASSIGN OWNED BY alice TO postgres; -- 转移对象所有权 DROP OWNED BY alice; -- 删除用户所有的对象 DROP USER alice; 角色继承 角色的核心价值在于层级管理——把权限授予角色，然后把角色授予用户：</description>
    </item>
    <item>
      <title>备份与恢复</title>
      <link>/posts/2025/02/backup-and-restore/</link>
      <pubDate>Tue, 25 Feb 2025 10:00:00 +0800</pubDate>
      <guid>/posts/2025/02/backup-and-restore/</guid>
      <description>备份是数据库管理的生命线。PostgreSQL 提供了多种备份策略，从简单的 SQL 导出到基于 WAL 的持续归档（PITR），能满足从开发到企业级的各种需求。&#xA;逻辑备份 使用 pg_dump 导出 SQL 格式备份，是简单场景的首选。&#xA;备份单个数据库 # 导出纯 SQL 格式（最通用，可读可编辑） pg_dump -U postgres -d mydb &amp;gt; mydb_backup.sql # 自定义格式（压缩，支持并行恢复） pg_dump -U postgres -d mydb -Fc &amp;gt; mydb_backup.dump # 目录格式（并行备份，默认每个表一个文件） pg_dump -U postgres -d mydb -Fd -j 4 &amp;gt; mydb_backup_dir/ # 只备份 schema（不备份数据） pg_dump -U postgres -d mydb --schema-only &amp;gt; mydb_schema.sql # 只备份数据（不备份 schema） pg_dump -U postgres -d mydb --data-only &amp;gt; mydb_data.sql 恢复备份 # 方式 1：SQL 格式直接导入 psql -U postgres -d mydb &amp;lt; mydb_backup.</description>
    </item>
    <item>
      <title>触发器与事件</title>
      <link>/posts/2025/02/triggers-and-events/</link>
      <pubDate>Sun, 23 Feb 2025 10:00:00 +0800</pubDate>
      <guid>/posts/2025/02/triggers-and-events/</guid>
      <description>触发器让数据库在特定事件（INSERT、UPDATE、DELETE）发生时自动执行函数，是维护数据完整性和实现自动化逻辑的强大工具。&#xA;触发器基础 创建触发器的流程 -- 1. 先创建一个触发器函数（返回 TRIGGER 类型） CREATE OR REPLACE FUNCTION trigger_function() RETURNS TRIGGER LANGUAGE plpgsql AS $$ BEGIN -- 逻辑代码 RETURN NEW; -- 或 RETURN OLD，或 RETURN NULL END; $$; -- 2. 将函数绑定到表上 CREATE TRIGGER trigger_name {BEFORE | AFTER} {INSERT | UPDATE | DELETE | TRUNCATE} ON table_name [FOR EACH {ROW | STATEMENT}] EXECUTE FUNCTION trigger_function(); 新旧数据的含义 场景 OLD NEW INSERT NULL 新插入的行 UPDATE 更新前的行 更新后的行 DELETE 被删除的行 NULL 实战：自动更新时间戳 这是最常用的触发器——自动维护 updated_at 列：</description>
    </item>
    <item>
      <title>存储过程与函数</title>
      <link>/posts/2025/02/stored-procedures-and-functions/</link>
      <pubDate>Fri, 21 Feb 2025 10:00:00 +0800</pubDate>
      <guid>/posts/2025/02/stored-procedures-and-functions/</guid>
      <description>存储过程和函数允许你在数据库内部执行业务逻辑——数据不离开数据库，减少网络往返，对特定场景有显著性能优势。&#xA;函数 vs 存储过程 PostgreSQL 11 之前只有函数（必须返回值），11 之后引入了真正的存储过程（可以不返回值）。&#xA;特性 函数（FUNCTION） 存储过程（PROCEDURE） 返回值 必须返回（可 VOID） 可不返回 事务控制 不支持 BEGIN/COMMIT 内部 支持 SELECT 调用 ✅ SELECT func() ❌ CALL 调用 ❌ ✅ CALL proc() 参数模式 IN / OUT / INOUT IN / INOUT 创建函数 CREATE OR REPLACE FUNCTION func_name(param1 TYPE, param2 TYPE DEFAULT default_value) RETURNS return_type LANGUAGE plpgsql AS $$ BEGIN -- 函数体 RETURN result; END; $$; 标量函数 -- 最简单的函数：计算折扣价格 CREATE OR REPLACE FUNCTION calc_discount_price( p_price NUMERIC, p_discount NUMERIC DEFAULT 0.</description>
    </item>
    <item>
      <title>视图与物化视图</title>
      <link>/posts/2025/02/views-and-materialized-views/</link>
      <pubDate>Wed, 19 Feb 2025 10:00:00 +0800</pubDate>
      <guid>/posts/2025/02/views-and-materialized-views/</guid>
      <description>视图（View）和物化视图（Materialized View）是 PostgreSQL 中封装复杂查询、提供数据和访问控制层的重要工具。&#xA;视图：虚拟表 视图本质上是一个命名的查询，不存储数据，每次查询时执行底层查询。&#xA;-- 准备数据 CREATE TABLE products ( id SERIAL PRIMARY KEY, name VARCHAR(100) NOT NULL, category VARCHAR(50), price NUMERIC(10,2), stock INTEGER, is_active BOOLEAN DEFAULT true ); INSERT INTO products (name, category, price, stock, is_active) VALUES (&amp;#39;笔记本电脑&amp;#39;, &amp;#39;电子产品&amp;#39;, 5999, 50, true), (&amp;#39;手机&amp;#39;, &amp;#39;电子产品&amp;#39;, 3999, 200, true), (&amp;#39;耳机&amp;#39;, &amp;#39;电子产品&amp;#39;, 299, 500, true), (&amp;#39;运动鞋&amp;#39;, &amp;#39;服饰&amp;#39;, 599, 300, true), (&amp;#39;T恤&amp;#39;, &amp;#39;服饰&amp;#39;, 99, 100, false), (&amp;#39;咖啡机&amp;#39;, &amp;#39;家居&amp;#39;, 1299, 50, true); 创建视图 -- 创建一个活跃产品视图 CREATE VIEW active_products AS SELECT id, name, category, price, stock FROM products WHERE is_active = true; -- 使用视图（就像使用表一样） SELECT * FROM active_products WHERE category = &amp;#39;电子产品&amp;#39; ORDER BY price DESC; 视图的作用 简化复杂查询 -- 创建常用分析视图 CREATE VIEW customer_order_summary AS SELECT c.</description>
    </item>
    <item>
      <title>CTE 与递归查询</title>
      <link>/posts/2025/02/cte-and-recursive/</link>
      <pubDate>Mon, 17 Feb 2025 10:00:00 +0800</pubDate>
      <guid>/posts/2025/02/cte-and-recursive/</guid>
      <description>WITH（CTE，Common Table Expression）可以让你在 SQL 中像写程序一样组织逻辑——分步骤、命名中间结果、复用。递归 CTE 更是处理层级数据的利器。&#xA;普通 CTE CTE 把复杂的查询拆成多个可读的步骤：&#xA;-- 统计每个城市的客户消费情况 WITH city_stats AS ( SELECT c.city, COUNT(DISTINCT o.customer_id) AS 客户数, SUM(o.total_amount) AS 总消费 FROM customers c LEFT JOIN orders o ON c.customer_id = o.customer_id AND o.status = &amp;#39;已完成&amp;#39; GROUP BY c.city ), avg_city AS ( SELECT AVG(总消费) AS 城市平均消费 FROM city_stats ) SELECT cs.city, cs.客户数, cs.总消费, ROUND(cs.总消费 / ac.城市平均消费 * 100, 2) AS 相对占比 FROM city_stats cs CROSS JOIN avg_city ac ORDER BY cs.</description>
    </item>
    <item>
      <title>事务与并发控制</title>
      <link>/posts/2025/02/transaction-and-concurrency/</link>
      <pubDate>Sat, 15 Feb 2025 10:00:00 +0800</pubDate>
      <guid>/posts/2025/02/transaction-and-concurrency/</guid>
      <description>数据库的并发控制决定了多个用户同时读写时，数据一致性和性能的平衡。PostgreSQL 使用 MVCC（多版本并发控制）来优雅地解决这个问题。&#xA;什么是事务？ 事务是一组 SQL 操作的逻辑单位，满足 ACID 特性：&#xA;原子性（A）：要么全部成功，要么全部回滚 一致性（C）：事务前后数据状态一致 隔离性（I）：并发事务互不干扰（程度可调） 持久性（D）：提交后数据不会丢失 基本事务控制 -- 准备测试数据 CREATE TABLE bank_accounts ( id SERIAL PRIMARY KEY, name VARCHAR(50) NOT NULL, balance NUMERIC(10,2) DEFAULT 0 CHECK (balance &amp;gt;= 0) ); INSERT INTO bank_accounts (name, balance) VALUES (&amp;#39;张三&amp;#39;, 10000), (&amp;#39;李四&amp;#39;, 5000), (&amp;#39;王五&amp;#39;, 20000); BEGIN / COMMIT / ROLLBACK -- 转账操作：张三转 1000 给李四 BEGIN; UPDATE bank_accounts SET balance = balance - 1000 WHERE name = &amp;#39;张三&amp;#39;; UPDATE bank_accounts SET balance = balance + 1000 WHERE name = &amp;#39;李四&amp;#39;; -- 检查余额是否正确（此时其他会话还看不到变化） SELECT * FROM bank_accounts ORDER BY id; -- 确认无误后提交 COMMIT; 回滚事务 BEGIN; UPDATE bank_accounts SET balance = balance - 50000 WHERE name = &amp;#39;张三&amp;#39;; -- ❌ 余额不足 -- 发现错误，回滚全部操作 ROLLBACK; -- 查看数据，发现数据变回事务开始前的状态 SELECT * FROM bank_accounts; SAVEPOINT 保存点 BEGIN; INSERT INTO bank_accounts (name, balance) VALUES (&amp;#39;赵六&amp;#39;, 3000); SAVEPOINT after_insert; UPDATE bank_accounts SET balance = balance + 5000 WHERE name = &amp;#39;张三&amp;#39;; -- 决定撤销这笔更新 ROLLBACK TO after_insert; -- 张三的余额不受影响，但赵六已插入 COMMIT; 隔离级别 SQL 标准定义了四个隔离级别，从低到高依次限制不同的并发问题：</description>
    </item>
    <item>
      <title>索引与性能优化</title>
      <link>/posts/2025/02/index-and-performance/</link>
      <pubDate>Thu, 13 Feb 2025 10:00:00 +0800</pubDate>
      <guid>/posts/2025/02/index-and-performance/</guid>
      <description>数据量达到百万级后，不合适的索引会让查询慢如蜗牛。本章将系统学习 PostgreSQL 的索引原理和性能优化方法。&#xA;为什么需要索引？ 没有索引的查询需要全表扫描——逐行检查数据。就像一本没有目录的书，要找一句话就得从头翻到尾。&#xA;索引的本质是一个排序过的数据结构（B-Tree），让数据库能在 O(log n) 时间内找到数据，而不是 O(n)。&#xA;创建练习数据 -- 创建一张大表来演示索引效果 CREATE TABLE big_orders ( id SERIAL PRIMARY KEY, user_id INTEGER NOT NULL, product_name VARCHAR(100), amount NUMERIC(10,2), status VARCHAR(10), order_date DATE, created_at TIMESTAMP DEFAULT NOW() ); -- 插入 100 万条数据 INSERT INTO big_orders (user_id, product_name, amount, status, order_date) SELECT (random() * 100000)::INT + 1, CASE (random() * 5)::INT WHEN 0 THEN &amp;#39;笔记本电脑&amp;#39; WHEN 1 THEN &amp;#39;手机&amp;#39; WHEN 2 THEN &amp;#39;耳机&amp;#39; WHEN 3 THEN &amp;#39;运动鞋&amp;#39; ELSE &amp;#39;T恤&amp;#39; END, (random() * 5000 + 100)::NUMERIC(10,2), CASE (random() * 4)::INT WHEN 0 THEN &amp;#39;已完成&amp;#39; WHEN 1 THEN &amp;#39;已取消&amp;#39; WHEN 2 THEN &amp;#39;退款中&amp;#39; ELSE &amp;#39;已完成&amp;#39; END, DATE &amp;#39;2024-01-01&amp;#39; + (random() * 730)::INT FROM generate_series(1, 1000000); -- 确认数据已插入 SELECT count(*) FROM big_orders; EXPLAIN 读懂查询计划 -- 查看查询计划 EXPLAIN SELECT * FROM big_orders WHERE user_id = 42; EXPLAIN ANALYZE SELECT * FROM big_orders WHERE user_id = 42; EXPLAIN 输出解读：</description>
    </item>
    <item>
      <title>窗口函数</title>
      <link>/posts/2025/02/window-functions/</link>
      <pubDate>Tue, 11 Feb 2025 10:00:00 +0800</pubDate>
      <guid>/posts/2025/02/window-functions/</guid>
      <description>窗口函数是 PostgreSQL 最强大的分析工具之一。它在不改变行数的前提下，为每一行计算一个&amp;quot;窗口&amp;quot;内的聚合值——让你同时看到&amp;quot;行级&amp;quot;和&amp;quot;聚合级&amp;quot;的数据。&#xA;窗口函数 vs 普通聚合 普通聚合用 GROUP BY 将多行压缩为一行：&#xA;SELECT product, SUM(amount) FROM sales GROUP BY product; -- 结果：每种商品一行 窗口函数保留原始行数，额外返回聚合值：&#xA;SELECT product, amount, SUM(amount) OVER () AS 总计 FROM sales; -- 结果：每行不变，多一列&amp;#34;总计&amp;#34; 创建练习数据集 -- 月度销售数据 CREATE TABLE monthly_sales ( id SERIAL PRIMARY KEY, product VARCHAR(50), category VARCHAR(50), month DATE NOT NULL, sales_amount NUMERIC(10,2), sales_qty INTEGER ); INSERT INTO monthly_sales (product, category, month, sales_amount, sales_qty) VALUES (&amp;#39;笔记本电脑&amp;#39;, &amp;#39;电子产品&amp;#39;, &amp;#39;2025-01-01&amp;#39;, 300000, 50), (&amp;#39;笔记本电脑&amp;#39;, &amp;#39;电子产品&amp;#39;, &amp;#39;2025-02-01&amp;#39;, 350000, 58), (&amp;#39;笔记本电脑&amp;#39;, &amp;#39;电子产品&amp;#39;, &amp;#39;2025-03-01&amp;#39;, 280000, 46), (&amp;#39;手机&amp;#39;, &amp;#39;电子产品&amp;#39;, &amp;#39;2025-01-01&amp;#39;, 450000, 112), (&amp;#39;手机&amp;#39;, &amp;#39;电子产品&amp;#39;, &amp;#39;2025-02-01&amp;#39;, 520000, 130), (&amp;#39;手机&amp;#39;, &amp;#39;电子产品&amp;#39;, &amp;#39;2025-03-01&amp;#39;, 480000, 120), (&amp;#39;耳机&amp;#39;, &amp;#39;电子产品&amp;#39;, &amp;#39;2025-01-01&amp;#39;, 60000, 200), (&amp;#39;耳机&amp;#39;, &amp;#39;电子产品&amp;#39;, &amp;#39;2025-02-01&amp;#39;, 55000, 180), (&amp;#39;耳机&amp;#39;, &amp;#39;电子产品&amp;#39;, &amp;#39;2025-03-01&amp;#39;, 70000, 240), (&amp;#39;运动鞋&amp;#39;, &amp;#39;服饰&amp;#39;, &amp;#39;2025-01-01&amp;#39;, 80000, 135), (&amp;#39;运动鞋&amp;#39;, &amp;#39;服饰&amp;#39;, &amp;#39;2025-02-01&amp;#39;, 75000, 125), (&amp;#39;运动鞋&amp;#39;, &amp;#39;服饰&amp;#39;, &amp;#39;2025-03-01&amp;#39;, 65000, 108), (&amp;#39;T恤&amp;#39;, &amp;#39;服饰&amp;#39;, &amp;#39;2025-01-01&amp;#39;, 30000, 300), (&amp;#39;T恤&amp;#39;, &amp;#39;服饰&amp;#39;, &amp;#39;2025-02-01&amp;#39;, 35000, 350), (&amp;#39;T恤&amp;#39;, &amp;#39;服饰&amp;#39;, &amp;#39;2025-03-01&amp;#39;, 28000, 280), (&amp;#39;咖啡机&amp;#39;, &amp;#39;家居&amp;#39;, &amp;#39;2025-01-01&amp;#39;, 65000, 50), (&amp;#39;咖啡机&amp;#39;, &amp;#39;家居&amp;#39;, &amp;#39;2025-02-01&amp;#39;, 55000, 42), (&amp;#39;咖啡机&amp;#39;, &amp;#39;家居&amp;#39;, &amp;#39;2025-03-01&amp;#39;, 40000, 30); 排名函数 -- ROW_NUMBER：连续的排名（不并列） SELECT product, month, sales_amount, ROW_NUMBER() OVER (ORDER BY sales_amount DESC) AS 总排名 FROM monthly_sales; -- RANK：并列排名（跳过后续序号） SELECT product, month, sales_amount, RANK() OVER (ORDER BY sales_amount DESC) AS 排名_可并列 FROM monthly_sales; -- DENSE_RANK：并列排名（不跳过后续序号） SELECT product, month, sales_amount, DENSE_RANK() OVER (ORDER BY sales_amount DESC) AS 排名_密集 FROM monthly_sales; 三者的区别：</description>
    </item>
    <item>
      <title>JOIN 与子查询</title>
      <link>/posts/2025/02/join-and-subquery/</link>
      <pubDate>Sun, 09 Feb 2025 10:00:00 +0800</pubDate>
      <guid>/posts/2025/02/join-and-subquery/</guid>
      <description>真实世界的数据很少只存在于一张表中。JOIN 和多表关联是 SQL 最核心的能力之一。&#xA;创建练习数据集 -- 客户表 CREATE TABLE customers ( customer_id SERIAL PRIMARY KEY, name VARCHAR(100) NOT NULL, city VARCHAR(50), member_level VARCHAR(10) DEFAULT &amp;#39;普通&amp;#39;, created_at TIMESTAMP DEFAULT NOW() ); -- 订单表 CREATE TABLE orders ( order_id SERIAL PRIMARY KEY, customer_id INTEGER REFERENCES customers(customer_id), order_date DATE NOT NULL, total_amount NUMERIC(10,2) NOT NULL, status VARCHAR(10) DEFAULT &amp;#39;已完成&amp;#39; CHECK (status IN (&amp;#39;已完成&amp;#39;, &amp;#39;已取消&amp;#39;, &amp;#39;退款中&amp;#39;)) ); -- 订单明细表 CREATE TABLE order_items ( item_id SERIAL PRIMARY KEY, order_id INTEGER NOT NULL REFERENCES orders(order_id), product_name VARCHAR(100) NOT NULL, price NUMERIC(10,2) NOT NULL, quantity INTEGER NOT NULL ); -- 插入客户数据 INSERT INTO customers (name, city, member_level) VALUES (&amp;#39;张三&amp;#39;, &amp;#39;北京&amp;#39;, &amp;#39;金卡&amp;#39;), (&amp;#39;李四&amp;#39;, &amp;#39;上海&amp;#39;, &amp;#39;银卡&amp;#39;), (&amp;#39;王五&amp;#39;, &amp;#39;广州&amp;#39;, &amp;#39;金卡&amp;#39;), (&amp;#39;赵六&amp;#39;, &amp;#39;深圳&amp;#39;, &amp;#39;普通&amp;#39;), (&amp;#39;陈七&amp;#39;, &amp;#39;杭州&amp;#39;, &amp;#39;银卡&amp;#39;), (&amp;#39;周八&amp;#39;, &amp;#39;成都&amp;#39;, &amp;#39;普通&amp;#39;); -- 从未下单的客户 -- 插入订单数据（陈七从未下单） INSERT INTO orders (customer_id, order_date, total_amount, status) VALUES (1, &amp;#39;2025-01-05&amp;#39;, 8997, &amp;#39;已完成&amp;#39;), (2, &amp;#39;2025-01-08&amp;#39;, 5295, &amp;#39;已完成&amp;#39;), (3, &amp;#39;2025-01-12&amp;#39;, 3500, &amp;#39;已完成&amp;#39;), (1, &amp;#39;2025-01-15&amp;#39;, 399, &amp;#39;已完成&amp;#39;), (4, &amp;#39;2025-01-18&amp;#39;, 1500, &amp;#39;已完成&amp;#39;), (5, &amp;#39;2025-01-20&amp;#39;, 2500, &amp;#39;已完成&amp;#39;), (2, &amp;#39;2025-01-22&amp;#39;, 880, &amp;#39;已取消&amp;#39;), (1, &amp;#39;2025-01-25&amp;#39;, 12000, &amp;#39;已完成&amp;#39;), (3, &amp;#39;2025-01-28&amp;#39;, 599, &amp;#39;退款中&amp;#39;), (5, &amp;#39;2025-02-01&amp;#39;, 1800, &amp;#39;已完成&amp;#39;); -- 插入订单明细 INSERT INTO order_items (order_id, product_name, price, quantity) VALUES (1, &amp;#39;笔记本电脑&amp;#39;, 5999, 1), (1, &amp;#39;手机&amp;#39;, 3999, 1), -- 订单1：5999+3999 = 9998-.</description>
    </item>
    <item>
      <title>聚合与分组</title>
      <link>/posts/2025/02/aggregation-and-grouping/</link>
      <pubDate>Fri, 07 Feb 2025 10:00:00 +0800</pubDate>
      <guid>/posts/2025/02/aggregation-and-grouping/</guid>
      <description>聚合函数 聚合函数将多行数据汇总为单个结果，是数据分析的核心工具。&#xA;-- 创建销售数据表 CREATE TABLE sales ( id SERIAL PRIMARY KEY, product VARCHAR(100), category VARCHAR(50), amount NUMERIC(10,2), quantity INTEGER, sale_date DATE, salesperson VARCHAR(50) ); INSERT INTO sales (product, category, amount, quantity, sale_date, salesperson) VALUES (&amp;#39;笔记本电脑&amp;#39;, &amp;#39;电子产品&amp;#39;, 5999, 2, &amp;#39;2025-01-01&amp;#39;, &amp;#39;小陈&amp;#39;), (&amp;#39;手机&amp;#39;, &amp;#39;电子产品&amp;#39;, 3999, 5, &amp;#39;2025-01-02&amp;#39;, &amp;#39;小陈&amp;#39;), (&amp;#39;耳机&amp;#39;, &amp;#39;电子产品&amp;#39;, 299, 10, &amp;#39;2025-01-03&amp;#39;, &amp;#39;小李&amp;#39;), (&amp;#39;运动鞋&amp;#39;, &amp;#39;服饰&amp;#39;, 599, 3, &amp;#39;2025-01-05&amp;#39;, &amp;#39;小王&amp;#39;), (&amp;#39;T恤&amp;#39;, &amp;#39;服饰&amp;#39;, 99, 20, &amp;#39;2025-01-06&amp;#39;, &amp;#39;小陈&amp;#39;), (&amp;#39;咖啡机&amp;#39;, &amp;#39;家居&amp;#39;, 1299, 2, &amp;#39;2025-01-07&amp;#39;, &amp;#39;小李&amp;#39;), (&amp;#39;台灯&amp;#39;, &amp;#39;家居&amp;#39;, 199, 8, &amp;#39;2025-01-08&amp;#39;, &amp;#39;小王&amp;#39;), (&amp;#39;手机&amp;#39;, &amp;#39;电子产品&amp;#39;, 3999, 3, &amp;#39;2025-01-10&amp;#39;, &amp;#39;小李&amp;#39;), (&amp;#39;笔记本电脑&amp;#39;, &amp;#39;电子产品&amp;#39;, 5999, 1, &amp;#39;2025-01-12&amp;#39;, &amp;#39;小王&amp;#39;), (&amp;#39;运动鞋&amp;#39;, &amp;#39;服饰&amp;#39;, 599, 5, &amp;#39;2025-01-15&amp;#39;, &amp;#39;小陈&amp;#39;), (&amp;#39;耳机&amp;#39;, &amp;#39;电子产品&amp;#39;, 299, 15, &amp;#39;2025-01-18&amp;#39;, &amp;#39;小李&amp;#39;), (&amp;#39;台灯&amp;#39;, &amp;#39;家居&amp;#39;, 199, 12, &amp;#39;2025-01-20&amp;#39;, &amp;#39;小陈&amp;#39;), (&amp;#39;咖啡机&amp;#39;, &amp;#39;家居&amp;#39;, 1299, 1, &amp;#39;2025-01-22&amp;#39;, &amp;#39;小王&amp;#39;), (&amp;#39;T恤&amp;#39;, &amp;#39;服饰&amp;#39;, 99, 30, &amp;#39;2025-01-25&amp;#39;, &amp;#39;小李&amp;#39;), (&amp;#39;手机&amp;#39;, &amp;#39;电子产品&amp;#39;, 3999, 4, &amp;#39;2025-01-28&amp;#39;, &amp;#39;小陈&amp;#39;); 五种基础聚合 SELECT COUNT(*) AS 总记录数, COUNT(DISTINCT product) AS 不同商品数, SUM(amount) AS 总金额, AVG(amount) AS 平均金额, MAX(amount) AS 最大金额, MIN(amount) AS 最小金额 FROM sales; COUNT 的细节差异 -- COUNT(*) 统计行数（包括 NULL） SELECT COUNT(*) FROM sales; -- COUNT(column) 统计非 NULL 值的数量 SELECT COUNT(product) FROM sales; -- COUNT(DISTINCT column) 统计不重复的非 NULL 值数量 SELECT COUNT(DISTINCT product) FROM sales; -- 三者的区别 SELECT COUNT(*) AS 总行数, COUNT(salesperson) AS 非空姓名数, COUNT(DISTINCT salesperson) AS 不同姓名数 FROM sales; GROUP BY 分组聚合 -- 按产品分组 SELECT product, COUNT(*) AS 订单数, SUM(amount) AS 总销售额, SUM(quantity) AS 总销量, ROUND(AVG(amount), 2) AS 平均金额 FROM sales GROUP BY product ORDER BY 总销售额 DESC; 多维度分组 -- 按类别和产品分组（SQL 中，SELECT 的非聚合列必须出现在 GROUP BY 中） SELECT category, product, SUM(amount) AS 销售额, COUNT(*) AS 订单数 FROM sales GROUP BY category, product ORDER BY category, 销售额 DESC; PostgreSQL 允许按 SELECT 中列的序号简化：</description>
    </item>
    <item>
      <title>查询与过滤</title>
      <link>/posts/2025/02/query-and-filter/</link>
      <pubDate>Wed, 05 Feb 2025 10:00:00 +0800</pubDate>
      <guid>/posts/2025/02/query-and-filter/</guid>
      <description>SELECT 基础 创建练习表并插入数据 -- 创建练习数据库 CREATE DATABASE practice; \c practice -- 创建员工表 CREATE TABLE employees ( id SERIAL PRIMARY KEY, name VARCHAR(50) NOT NULL, department VARCHAR(30), salary NUMERIC(10,2), hire_date DATE, is_active BOOLEAN DEFAULT true ); -- 插入测试数据 INSERT INTO employees (name, department, salary, hire_date, is_active) VALUES (&amp;#39;张三&amp;#39;, &amp;#39;技术部&amp;#39;, 15000, &amp;#39;2023-01-15&amp;#39;, true), (&amp;#39;李四&amp;#39;, &amp;#39;技术部&amp;#39;, 18000, &amp;#39;2022-06-01&amp;#39;, true), (&amp;#39;王五&amp;#39;, &amp;#39;市场部&amp;#39;, 12000, &amp;#39;2023-03-10&amp;#39;, true), (&amp;#39;赵六&amp;#39;, &amp;#39;市场部&amp;#39;, 11000, &amp;#39;2024-01-20&amp;#39;, true), (&amp;#39;钱七&amp;#39;, &amp;#39;技术部&amp;#39;, 22000, &amp;#39;2021-09-01&amp;#39;, true), (&amp;#39;孙八&amp;#39;, &amp;#39;人事部&amp;#39;, 9000, &amp;#39;2024-07-15&amp;#39;, true), (&amp;#39;周九&amp;#39;, &amp;#39;人事部&amp;#39;, 9500, &amp;#39;2023-11-01&amp;#39;, true), (&amp;#39;吴十&amp;#39;, &amp;#39;技术部&amp;#39;, 16000, &amp;#39;2023-06-20&amp;#39;, false), (&amp;#39;郑一&amp;#39;, &amp;#39;市场部&amp;#39;, 10000, &amp;#39;2024-09-01&amp;#39;, true), (&amp;#39;陈二&amp;#39;, &amp;#39;财务部&amp;#39;, 13000, &amp;#39;2022-12-01&amp;#39;, true); 基本 SELECT -- 查询所有列 SELECT * FROM employees; -- 查询指定列 SELECT name, salary FROM employees; -- 列别名 SELECT name AS 姓名, salary AS 工资 FROM employees; -- 列计算 SELECT name, salary, salary * 1.</description>
    </item>
    <item>
      <title>建表与数据类型</title>
      <link>/posts/2025/02/table-and-datatypes/</link>
      <pubDate>Mon, 03 Feb 2025 00:00:00 +0000</pubDate>
      <guid>/posts/2025/02/table-and-datatypes/</guid>
      <description>数据类型概览 PostgreSQL 支持丰富的数据类型，是 SQL 标准实现最全面的数据库之一。&#xA;数值类型 -- 整数类型 SMALLINT -- 2字节，范围 -32768 ~ 32767 INTEGER -- 4字节，范围 -21亿 ~ 21亿 BIGINT -- 8字节，范围超大 -- 精确小数 NUMERIC(10,2) -- 总共10位，小数点后2位，精确计算 DECIMAL(10,2) -- NUMERIC 的别名 -- 浮点类型 REAL -- 4字节，6位十进制精度 DOUBLE PRECISION -- 8字节，15位十进制精度 -- 自增类型 SMALLSERIAL -- 2字节自增 SERIAL -- 4字节自增（常用） BIGSERIAL -- 8字节自增 字符串类型 CHAR(n) -- 定长字符串，不足补空格 VARCHAR(n) -- 变长字符串，有长度限制（最常用） TEXT -- 不限长度字符串（无性能损失） 经验：不要被 CHAR 的&amp;quot;性能优势&amp;quot;迷惑。在 PostgreSQL 中，VARCHAR 和 TEXT 几乎没有性能差异。大多数场景直接使用 TEXT 最省心。如果需要限制长度，用 VARCHAR(n)。</description>
    </item>
    <item>
      <title>PostgreSQL 简介与安装</title>
      <link>/posts/2025/02/postgresql-intro/</link>
      <pubDate>Sun, 02 Feb 2025 00:00:00 +0000</pubDate>
      <guid>/posts/2025/02/postgresql-intro/</guid>
      <description>什么是 PostgreSQL？ PostgreSQL 是一个功能强大的开源关系型数据库管理系统，拥有超过 35 年的活跃开发历史。它的名字源于 POSTGRES（加州大学伯克利分校的项目）和 Ingres（其前身），后更名为 PostgreSQL 以体现 SQL 支持。&#xA;为什么选择 PostgreSQL？ 开源免费：PostgreSQL 采用类似 MIT 的开源许可，可以自由使用、修改和分发 ACID 合规：完整支持事务的原子性、一致性、隔离性和持久性 扩展性强：支持 JSON/JSONB、全文搜索、地理空间（PostGIS）、键值存储等 活跃社区：全球范围内拥有数百万用户和数千贡献者 企业级特性：支持热备、流复制、分区表、并行查询等 PostgreSQL vs MySQL vs SQLite 特性 PostgreSQL MySQL SQLite 开源许可 MIT/类BSD GPL Public Domain ACID 完整支持 取决于存储引擎 有限 JSON 支持 JSONB（二进制，可索引） JSON 文本 无 并发控制 MVCC InnoDB MVCC 串行化 扩展性 非常强 中等 有限 适用场景 企业级应用、数据分析 Web 应用 移动/嵌入式 安装 PostgreSQL Windows 安装 # 方案一：官方安装包 # 访问 https://www.</description>
    </item>
    <item>
      <title>PostgreSQL 从入门到精通</title>
      <link>/posts/2025/02/postgresql-course/</link>
      <pubDate>Sat, 01 Feb 2025 00:00:00 +0000</pubDate>
      <guid>/posts/2025/02/postgresql-course/</guid>
      <description>PostgreSQL 被誉为&amp;quot;世界上最先进的开源关系型数据库&amp;quot;，凭借其强大的功能、出色的扩展性和活跃的社区生态，已成为越来越多企业和开发者的首选数据库。&#xA;本系列教程将从零开始，系统地带你掌握 PostgreSQL 的核心知识和实战技能。无论你是刚接触数据库的初学者，还是有一定经验的开发者，都能在这个系列中找到有价值的内容。&#xA;课程大纲 基础篇 PostgreSQL 简介与安装 建表与数据类型 查询与过滤 聚合与分组 JOIN 与子查询 进阶篇 窗口函数 索引与性能优化 事务与并发控制 CTE 与递归查询 视图与物化视图 高级篇 存储过程与函数 触发器与事件 备份与恢复 用户与权限管理 高级特性（JSON/全文搜索/扩展） 实战篇 实战项目：电商数据库设计 本系列文章使用 PostgreSQL 17 版本，示例代码可直接在 psql 或任何 PostgreSQL 客户端中运行。</description>
    </item>
  </channel>
</rss>
