让我们从一个简单的例子开始。假设我们有个员工表,想找出工资高于平均工资的员工:
1 | WITH avg_salary AS ( |
看到没?我们把计算平均工资的逻辑单独拎出来了。你可能会问,这和子查询有什么区别?区别就在于CTE可以复用,而且可读性更好。想象一下,如果你要在一个复杂查询里多次用到这个平均值,CTE其实就是救星。
不过CTE最酷的地方在于递归查询。来,我们画个简单的流程图:
flowchart LR A[开始] --> B{是否满足条件?} B -- 是 --> C[处理当前行] C --> D[生成下一行] D --> B B -- 否 --> E[结束]
这个流程图描述了一个典型的递归CTE结构。举个实际例子,假设我们要查询一个员工的所有下属:
1 | WITH RECURSIVE subordinates AS ( |
说实话,我第一次看到递归CTE的时候,脑子是懵的。但一旦理解了它的工作原理,就会发现它简直是个神器。不过要小心,递归查询很容易写出死循环,我就干过这种蠢事。
说到性能,CTE有时会被过度神话。很多人以为CTE会自动优化查询,其实不然。PostgreSQL会把CTE当作一个临时视图来处理,这意味着它可能会被多次执行。如果你发现查询变慢了,不妨试试把CTE改成子查询或者临时表。
最后说点个人偏见:我觉得CTE最大的价值不在于性能优化,而在于让SQL代码更易读、更易维护。毕竟,三个月后回头看代码的很可能就是你自己。与其写一堆让人头大的嵌套查询,不如用CTE把逻辑清晰地表达出来。