DROPTABLEIFEXISTSemployees_example;CREATETABLEemployees_example(idintegerPRIMARYKEY,nametextNOTNULL,manager_idintegerREFERENCESemployees_example(id));INSERTINTOemployees_exampleVALUES(1,'Alice',NULL),-- top of the hierarchy
(2,'Bob',1),-- reports to Alice
(3,'Carol',1),-- reports to Alice
(4,'Dave',2),-- reports to Bob
(5,'Eve',2);-- reports to Bob
-- One level: employee and their direct manager
SELECTe.nameASemployee,m.nameASmanagerFROMemployees_exampleeLEFTJOINemployees_examplemONe.manager_id=m.idORDERBYe.id;-- Count direct reports per manager
SELECTm.nameASmanager,count(e.id)ASdirect_reportsFROMemployees_examplemLEFTJOINemployees_exampleeONe.manager_id=m.idGROUPBYm.id,m.nameORDERBYm.id;-- A taste of recursion: full chain to the top (covered fully in the CTE lesson)
WITHRECURSIVEchainAS(SELECTid,name,manager_id,1ASdepthFROMemployees_exampleWHEREid=4-- start at Dave
UNIONALLSELECTe.id,e.name,e.manager_id,c.depth+1FROMemployees_exampleeJOINchaincONe.id=c.manager_id)SELECT*FROMchain;
在连接 FROM employees_example e LEFT JOIN employees_example m ON e.manager_id = m.id 中,e 是员工行,m 是经理行。两者都指向同一张物理表——PostgreSQL 把这些别名当作彼此独立的副本。使用 LEFT JOIN 意味着没有经理的员工(Alice)仍会出现,经理列为 NULL。
$ psql -f source/self-join.sql postgres
DROP TABLE
CREATE TABLE
INSERT 0 5
employee | manager
----------+---------
Alice |
Bob | Alice
Carol | Alice
Dave | Bob
Eve | Bob
(5 rows)
...
常见陷阱
用 INNER JOIN 而不是 LEFT JOIN,会悄悄丢掉层级顶端的行。顶层行没有经理,INNER JOIN 会将其丢弃。
忘记 e 和 m 是同一张表。若条件可能允许同一行匹配,可以使用 WHERE e.id <> m.id 排除这种情况。