SQL 如何关联两张表,并按 Term 将日期展开为每个 ID 一行?
可以使用 LEFT JOIN 和条件聚合:先按 ID 关联两张表,再用 MAX(CASE WHEN ...) 将各个 Term 的起止日期放入对应列。示例中的 Term 1、2、3 是固定的,无需逐行循环。
查询的前提是 Customer.ID 唯一,且 Dates 中每个 (ID, Term) 最多有一条记录。
为什么 JOIN 和 DISTINCT 没有得到预期结果?
Bob 在 Dates 中有两条记录,John 有三条。普通 JOIN 对每条匹配记录都会输出一行,因此同一个客户会出现多行,这是一对多关联的正常结果。
DISTINCT 只能删除所选列完全相同的重复行。如果记录的 Term 或日期不同,这些行就不会合并;不选择这些字段,又无法保留需要的日期信息。
要得到目标表,需要按客户分组,把不同 Term 的日期放入对应的列。
使用条件聚合完成行转列
下面的查询使用标准 SQL 的双引号引用带空格的字段名和列别名。实际使用时,需要按照数据库的标识符引用规则调整。
SELECT
c.ID,
c."First Name",
c."Family Name",
MAX(CASE WHEN d.Term = 1 THEN d.DateStart END) AS "Term 1 Start",
MAX(CASE WHEN d.Term = 1 THEN d.DateEnd END) AS "Term 1 End",
MAX(CASE WHEN d.Term = 2 THEN d.DateStart END) AS "Term 2 Start",
MAX(CASE WHEN d.Term = 2 THEN d.DateEnd END) AS "Term 2 End",
MAX(CASE WHEN d.Term = 3 THEN d.DateStart END) AS "Term 3 Start",
MAX(CASE WHEN d.Term = 3 THEN d.DateEnd END) AS "Term 3 End"
FROM Customer AS c
LEFT JOIN Dates AS d
ON d.ID = c.ID
GROUP BY
c.ID,
c."First Name",
c."Family Name"
ORDER BY c.ID;
这里假设 Term 是数值类型。如果是字符类型,条件需要按实际类型改成 d.Term = '1' 等形式。
查询中各部分的作用:
LEFT JOIN保留所有客户,即使某个客户没有日期记录。CASE WHEN d.Term = 1 THEN d.DateStart END只保留Term = 1的开始日期,其余行返回NULL。MAX忽略NULL,在每组最多有一个匹配值的前提下取出该日期。GROUP BY将同一个客户的匹配记录汇总到一行。
某个 Term 没有对应记录时,该列参与聚合的值全为 NULL,结果也是 NULL。因此,Bob 的 Term 3 Start 和 Term 3 End 会保持为空。
先检查同一个 Term 是否存在多条记录
上述查询适用于示例数据。如果同一个客户在同一个 Term 下有多条记录,需要先确定选择规则。可以用下面的查询检查:
SELECT
ID,
Term,
COUNT(*) AS record_count
FROM Dates
GROUP BY ID, Term
HAVING COUNT(*) > 1;
如果存在多条记录,分别对 DateStart 和 DateEnd 取 MAX,可能会把不同记录的日期拼成一对。
具体是选取某条记录,还是计算最早开始日期与最晚结束日期,需要根据业务要求确定。原问题没有提供这样的规则,不能假定应保留哪一条。如果业务规定每个客户的每个 Term 只能出现一次,可以通过 (ID, Term) 的唯一约束来保证。
如何将结果保存为新表?
确认上面的查询结果正确后,可以使用数据库支持的建表查询语法保存结果。
- 在支持
CREATE TABLE ... AS SELECT的数据库中,可以在上述查询的SELECT前添加CREATE TABLE CustomerTerms AS。 - 在 SQL Server 中,可以使用
SELECT ... INTO CustomerTerms FROM ...,将INTO CustomerTerms放在选择列表之后、FROM之前。带空格的字段名和别名可用方括号引用,例如[First Name]。
原问题没有说明数据库类型,因此建表语句需要按实际数据库调整。SELECT INTO 用于保存查询结果,日期行转列仍由条件聚合完成。
Term 数量不固定时怎么办?
上面的查询固定输出三个 Term。如果后续出现 Term = 4,需要增加对应的两列。
如果要根据数据自动生成全部 Term 列,通常需要动态 SQL,或在应用层完成透视。具体写法取决于数据库。当前已知的三个 Term 可以用条件聚合处理,也可以选择 PIVOT。
备注:内容仅供参考。
