在数据分析和管理中,掌握复杂查询逻辑是关键技能。即使在人工智能应用中,80%以上的时间都用于数据处理。
所谓 “garbage in, garbage out” 说明了数据质量的重要性。
而写好查询是数据处理的第一步。一个错误的查询可能毁掉整个模型。
今天,我将通过一个有趣的案例,向你展示如何从自然语言到SQL查询的转化。这不仅是一次技术之旅,更是一场逻辑的魔法秀。
关系模式 (Relational Schema)
学生表:Student(Sid, Name, Major, Birthdate)
课程表:Course(Cid, CName, Dept)
课程选修表:Enroll(Sid, Cid, Quarter, Grade)
查询的自然语言描述 (Queries)
Q1: 查找那些选修了Sid 为 928521 的同学选修的课程,并且在相同学期内选修这些课程的学生。
Q2: 查找那些选修了Sid 为 928521 的同学选修的所有课程,并且在相同学期内选修这些课程的学生。
Q3: 查找那些仅选修了Sid 为 928521 的同学选修的课程,并且在相同学期内选修这些课程的学生。
Q4: 查找那些仅选修了Sid 为 928521 的同学选修的所有课程,并且在相同学期内选修这些课程的学生。
查询中的逻辑解读
韦恩图:学生 s s s 选修的课程 与 928521 选修的课程 的关系。 U U U 表示所有学生:
Q1: s s s 的课程要与 928521 的课程有交集。
Q2: 928521 的课程是 s s s 的课程的子集,928521 的任何课程都被 s s s 选修了。
Q3: s s s 的课程是928521 的课程的子集,928521 的任何没有选修的课程, s s s 不可能选修。
Q4: s s s 的课程和 928521 的课程是同一个集合。
解决问题步骤
1.将自然语言查询翻译成一阶逻辑 (First Order Language) 语言表达 (一阶逻辑表达式不包含 全称量词 和 逻辑蕴含);
2.将一阶逻辑语言翻译成关系代数 (Relational Algebra) 语言表达;
3.将关系代数语言翻译成 SQL 查询。
Step1. 自然语言 --> 一阶逻辑
这里我先规定一些变量和谓词:
- 变量: s s s 来表示某个学生, c c c 表示某个课程, q q q 表示某个学期。
- 谓词: e n r o l l ( s , c , q ) enroll(s,c,q) enroll(s,c,q) 表达某个学生 s s s 在某个学期 q q q 选修了课程 c c c。为了表达式的简单,我们不单独为学生和课程定义谓词了。
Q1:描述一个学生集合,这些学生要选修 928521 这个同学选修了的课程,并且还是在同一学期选修的:
{ s ∣ ( ∃ c ) ( ∃ q ) [ e n r o l l ( 928251 , c , q ) ∧ e n r o l l ( s , c , q ) ] ∧ s ≠ 928251 } \{s|(\exists c)(\exists q)[enroll(928251,c,q)\land enroll(s,c,q)]\land s\not=928251\} {
s∣(∃c)(∃q)[enroll(928251,c,q)∧enroll(s,c,q)]∧s=928251}
Q2:根据 s s s 选修了 928521 的所有课程,如果 928521 选修了某门课, s s s 必定也选修了 (因为 s s s 上了 928521 所有的课):
{ s ∣ ( ∀ c ) ( ∀ q ) [ e n r o l l ( 928251 , c , q ) ⇒ e n r o l l ( s , c , q ) ] ∧ s ≠ 928251 } \{s|(\forall c)(\forall q)[enroll(928251,c,q)\Rightarrow enroll(s,c,q)]\land s\not=928251\} {
s∣(∀c)(∀q)[enroll(928251,c,q)⇒enroll(s,c,q)]∧s=928251}
Q3:根据 s s s 仅 选修 928521 的课,如果 s s s 选修了某门课,928521 必定也选修了:
{ s ∣ ( ∀ c ) ( ∀ q ) [ e n r o l l ( s , c , q ) ⇒ e n r o l l ( 928251 , c , q ) ] ∧ s ≠ 928251 } \{s|(\forall c)(\forall q)[enroll(s,c,q)\Rightarrow enroll(928251,c,q)]\land s\not=928251\} {
s∣(∀c)(∀q)[enroll(s,c,q)⇒enroll(928251,c,q)]∧s=928251}
Q4:Q2 和 Q3 条件的并:
{ s ∣ ( ∀ c ) ( ∀ q ) [ e n r o l l ( s , c , q ) ⇔ e n r o l l ( 928251 , c , q ) ] ∧ s ≠ 928251 } \{s|(\forall c)(\forall q)[enroll(s,c,q)\Leftrightarrow enroll(928251,c,q)]\land s\not=928251\} {
s∣(∀c)(∀q)[enroll(s,c,q)⇔enroll(928251,c,q)]∧s=928251}
⇔ \Leftrightarrow ⇔ 表示逻辑等价 (也可以用 ≡ \equiv ≡ 表示): q ⇒ p ∧ p ⇒ q ≡ q ⇔ q q\Rightarrow p\land p\Rightarrow q \equiv q\Leftrightarrow q q⇒p∧p⇒q≡q⇔q
Step2. 一阶逻辑 --> 关系代数
学生表:Student(Sid, Name, Major, Birthdate)
课程表:Course(Cid, CName, Dept)
课程选修表:Enroll(Sid, Cid, Quarter, Grade)
Q1: { s ∣ ( ∃ c ) ( ∃ q ) [ e n r o l l ( 928251 , c , q ) ∧ e n r o l l ( s , c , q ) ] ∧ s ≠ 928251 } \{s|(\exists c)(\exists q)[enroll(928251,c,q)\land enroll(s,c,q)]\land s\not=928251\} { s∣(∃c)(∃q)[enroll(928251,c,q)∧enroll(s,c,q)]∧s=928251} 这个一阶逻辑简化了关系代数的每个部分,我们可以把原查询拆分为多个关系代数表达式:
- T 11 : = π C i d , Q u a r t e r ( σ S i d = 928251 ( E n r o l l ) ) ) T_{11}:=\pi_{Cid,Quarter}(\sigma_{Sid=928251}(Enroll))) T11:=πCid,Quarter(σSid=928251(Enroll))) 对应一阶逻辑表达式 e n r o l l ( 928251 , c , q ) enroll(928251,c,q) enroll(928251,c,q) 。其中 π \pi π 表示选择哪些列, σ \sigma σ 表示关系选择条件。
- T 12 = π S i d , C i d , Q u a r t e r ( E n r o l l ) T_{12}=\pi_{Sid,Cid,Quarter}(Enroll) T12=πSid,Cid,Quarter(Enroll) 对应一阶逻辑表达式 e n r o l l ( s , c , q ) enroll(s,c,q) enroll(s,c,q)。
- T 13 = σ S i d ≠ 928521 ( T 12 ) T_{13}=\sigma_{Sid\not= 928521}(T_{12}) T13=σ