使用时间戳列在 postgresql 上联合加入

Posted

技术标签:

【中文标题】使用时间戳列在 postgresql 上联合加入【英文标题】:Union Join on postgresql with timestamp column 【发布时间】:2019-11-05 08:02:37 【问题描述】:

这是一个获取学生在特定日期的出勤行的简单查询。

条件 1. 如果学生在该日期有出勤,请获取 2. 如果不只是检索学生列

我的查询看起来像

Select 0 as attendanceId, 0 as attendanceVersion, '' as inTime,'' as 
outTime,'' as summary,Student.id As 
StudentId,Student.firstName,Student.lastName 
From  Student where student.id not in 
    ( Select student.id From Student join Attendance on ( student.id = 
      Attendance.studentId )
      where  attendance.inactiveDate is null and student.inactivedate is 
      null and 
      date(attendance.intime) = '2019-06-23' )
and student.inactivedate is null  

UNION

Select Attendance.id As AttendanceId,Attendance.version,Attendance.inTime,Attendance.outTime,Attendance.summary,
Student.id As StudentId,Student.firstName,Student.lastName 
From Student join Attendance on ( student.id = Attendance.studentId ) where  
attendance.inactiveDate is null and student.inactivedate is null and 
date(attendance.intime) = '2019-06-23' 
order by student.firstname, student.id 

我正在尝试做的事情: 选择没有出勤的学生行并将其与有出勤的学生行联合起来。

问题: 出现以下错误

错误:带有时区的时间戳类型的输入语法无效:“”

SQL 状态:22007

字符:51

我希望 postgres 能优雅地用空文字代替时区。如何将空字符串替换为时区或执行此查询的更好方法

更新:

Select 
Attendance.id As AttendanceId,Attendance.version, Attendance.inTime,Attendance.outTime,Attendance.summary,
Student.id As StudentId,Student.firstName,Student.lastName 
From Student left join Attendance on ( student.id = Attendance.studentId ) 
where student.inactivedate is null and date(attendance.intime) = '2019-06-23' 
order by student.firstname, student.id

像内部连接一样生成单行。猜猜是因为我参加了 Studentid 的出勤?!

【问题讨论】:

这听起来更像是你想要一个左连接。请发布一些示例数据和预期输出 左连接的行为类似于内连接。不知道我做错了什么。查看左连接结果的更新 关于错误:'' 不是时间戳值的有效常量。您需要在第一个查询中使用 null::timestamptz as intime 而不是 '' as intime 来选择“空”时间戳值。 【参考方案1】:

只要您在 where 子句中使用左连接列,查询就会转换为内连接:

SELECT * FROM
  lef 
  LEFT JOIN
  ri 
  ON lef.id = ri.id
WHERE ri.column = 'some value'

这个查询将像内部一样工作;左连接将为 ri 中没有匹配项的任何行添加空值,但随后 where 子句将取出空值,因为它们中的任何一个都不能等于“某个值”

Null 永远不等于任何东西

要解决这个问题,请将谓词放在连接条件中:

SELECT * FROM
  lef 
  LEFT JOIN
  ri 
  ON lef.id = ri.id AND ri.column = 'some value'

因此您的查询:

Select 
  a.id As AttendanceId,
  a.version,     
  a.inTime,
  a.outTime,
  a.summary,
  s.id As StudentId,
  s.firstName,
  s.lastName 
From 
  Student s 
  left join 
  Attendance a
  on 
    s.id = a.studentId AND 
    date(a.intime) = '2019-06-23')
WHERE
  s.inactivedate is null 
ORDER BY s.firstname, s.id

提示:

为您的表命名。它会清理您的代码并允许您多次加入同一个表 缩进你的代码,而不是把它全部塞进一个大块;帮助查看查询的哪些部分去哪里 - 我缩进以便查询中处于同一处理级别的所有内容都处于同一缩进级别。诸如 select、from、on 和 where 等关键字形成块标题,然后它们下面的所有内容都缩进 请记住,始终将引用左连接中的右表(或右连接中的左表)的谓词放在 ON 而不是 where

【讨论】:

谢谢你的作品。也感谢您的提示。我注意到我的错误并在哪里使用和( date(attendance.intime) = '2019-06-23' or date(attendance.intime) is null )。如果有人感兴趣,这也会产生相同的结果。 确实可以,但如果由于其他原因,出席时间列自然为空,而不是因为没有匹配而人为地为空,可能会有细微的差别 - 列为空的行 (但其他值已填充)也会显示,而在 ON 版本中则不会。如果一个例子能帮助我知道,我会发布一个 这是我的建议的一个陷阱。如果在查询日期以外的日期有学生的出勤记录,但不是在当前日期: (date(attendance.intime) = '2019-06-23' or date(attendance.intime) is null) in where 子句忽略它。而你的答案是正确的【参考方案2】:

我想你只是想要一个left join:

Select a.id As AttendanceId, a.version, a.inTime, a.outTime, a.summary,
       a.id As StudentId, s.firstName, s.lastName 
From Student s join
     Attendance a
    on s.id = a.studentId and
       date(a.intime) = '2019-06-23' and
       a.inactiveDate is null
where s.inactivedate is null and        
order by s.firstname, s.id ;

【讨论】:

左连接的行为类似于内连接。不知道我做错了什么。查看左连接结果的更新 @Cyber​​monk 。 . .如果您将过滤条件放在WHERE 子句而不是ON 子句中,就会发生这种情况。

以上是关于使用时间戳列在 postgresql 上联合加入的主要内容,如果未能解决你的问题,请参考以下文章

PostgreSQL 9.6 在对时间戳列进行聚合期间选择了错误的计划

使用表中的列在 PostgreSQL 中创建视图

如果列在 PostgreSQL 中同时包含 int 和 NULL 值,如何从文本转换为 int

Postgresql 12:重叠运算符的性能问题并在同一张表上加入

按日期范围分区 PostgreSQL 扫描所有分区

如何在 PostgreSQL 9.2.13 中执行 *此特定 * 更新 + 加入?