CTE 不检索任何行用于左连接和 where 表达式
CTE retrieving no rows is used in left join and where expression
我在 Amazon Redshift 上写了一个 SQL 查询,我遇到了一个 CTE 问题,return 没有行。
在比较 CTE No1 的时间戳字段与 CTE No2 的时间戳字段之后,查询正在从 CTE No1 和 returns 字段中检索 ID 和时间戳,并从 CTE No 2,3,4,5,6 中检索字段, 3、4、5。更准确地说,下面是我的 SQL 查询的伪代码,我希望它是可以理解的。请不要过多关注 LEAD() 和 LAG() 函数查询问题本质上比它可能出现的要简单得多,并且是关于 CTE 何时有 0 行,但它们用于最终的 where 条件查询,如何解释这些情况。
with CTE1 AS
(SELECT timestamp1, id
from table1
where ...
...
...)
, CTE2 as
(select timestamp2,
id,
field1,
field2,
LAG(timestamp2,1) over partition by id order by timestamp2 as timestamp2_previous,
LEAD(timestamp2,1) over partition by id order by timestamp2 as timestamp2_next
from table2)
, CTE3 as
(select timestamp3,
id,
field1,
field2,
LAG(timestamp3,1) over partition by id order by timestamp3 as timestamp3_previous,
LEAD(timestamp3,1) over partition by id order by timestamp3 as timestamp3_next
from table3)
, CTE4 as
(select timestamp4,
id,
field1,
field2,
LAG(timestamp4,1) over partition by id order by timestamp4 as timestamp4_previous,
LEAD(timestamp4,1) over partition by id order by timestamp4 as timestamp4_next
from table4)
, CTE5 as
(select timestamp5,
id,
field1,
field2,
LAG(timestamp5,1) over partition by id order by timestamp5 as timestamp5_previous,
LEAD(timestamp5,1) over partition by id order by timestamp5 as timestamp5_next
from table5)
, CTE6 as
(select timestamp6,
id,
field1,
field2,
LAG(timestamp6,1) over partition by id order by timestamp6 as timestamp6_previous,
LEAD(timestamp6,1) over partition by id order by timestamp6 as timestamp6_next
from table6)
select cte1.id,
cte1.timestamp1,
case when cte1.timestamp1<cte2.timestamp2 and cte2.timestamp2_previous is null then cte2.field1 else cte2.field2,
case when cte1.timestamp1<cte3.timestamp3 and cte3.timestamp3_previous is null then cte3.field1 else cte3.field2,
case when cte1.timestamp1<cte4.timestamp4 and cte4.timestamp4_previous is null then cte4.field1 else cte4.field2,
case when cte1.timestamp1<cte5.timestamp5 and cte5.timestamp5_previous is null then cte5.field1 else cte5.field2,
case when cte1.timestamp1<cte6.timestamp6 and cte6.timestamp6_previous is null then cte6.field1 else cte6.field2
from cte1
left join cte2 on cte1.id=cte2.id
left join cte3 on cte1.id=cte3.id
left join cte4 on cte1.id=cte4.id
left join cte5 on cte1.id=cte5.id
left join cte6 on cte1.id=cte6.id
where 1
and
-- conditions for CTE2
( case when cte1.timestamp1 < cte2.timestamp2 and cte2.timestamp2_previous is null then 1=1
else cte1.timestamp1 > cte2.timestamp2 end
and
CASE
WHEN cte2.timestamp2_next is not null THEN cte1.timestamp1 < cte2.timestamp2_next ELSE 1=1
END
)
-- conditions for CTE3
( case when cte1.timestamp1 < cte3.timestamp3 and cte3.timestamp3_previous is null then 1=1
else cte1.timestamp1 > cte3.timestamp3 end
and
CASE
WHEN cte3.timestamp3_next is not null THEN cte1.timestamp1 < cte3.timestamp3_next ELSE 1=1
END
)
-- conditions for CTE4
( case when cte1.timestamp1 < cte4.timestamp4 and cte4.timestamp4_previous is null then 1=1
else cte1.timestamp1 > cte4.timestamp4 end
and
CASE
WHEN cte4.timestamp4_next is not null THEN cte1.timestamp1 < cte4.timestamp4_next ELSE 1=1
END
)
-- conditions for CTE5
( case when cte1.timestamp1 < cte5.timestamp5 and cte5.timestamp5_previous is null then 1=1
else cte1.timestamp1 > cte5.timestamp5 end
and
CASE
WHEN cte5.timestamp5_next is not null THEN cte1.timestamp1 < cte5.timestamp5_next ELSE 1=1
END
)
-- conditions for CTE6
( case when cte1.timestamp1 < cte6.timestamp6 and cte6.timestamp6_previous is null then 1=1
else cte1.timestamp1 > cte6.timestamp6 end
and
CASE
WHEN cte6.timestamp6_next is not null THEN cte1.timestamp1 < cte6.timestamp6_next ELSE 1=1
END
)
问题是第一个之后的一些 CTE,return 没有特定 ID 的记录(我针对特定 ID 测试了我的代码,而 CTE3 return 没有该 ID 的行).
在这些情况下,最后的 where 条件,它们都与 AND 连接,将评估其中没有记录的 CTE,并且 return 0 行(因为操作 TRUE AND NULL),即使所有其他 CTE 有该 ID 的数据,查询结果应该是为它们检索数据。
如何检索此类情况的结果,并在引用具有当前行的 CTE 的结果字段中获取 NULL?
正如@JNevill 所说,答案是将 where 条件移动到连接中。为了让您了解这是如何工作的,您在 cte1 和 cte2 之间的连接变为:
ON cte1.id = cte2.id AND ((cte1.timestamp < cte2.timestamp and cte2.timestamp_previous IS NULL) OR cte1.timestamp > cte2.timestamp) AND (cte2.timestamp_next IS NULL OR cte1.timestamp < cte2.timestamp_next)
我在 Amazon Redshift 上写了一个 SQL 查询,我遇到了一个 CTE 问题,return 没有行。
在比较 CTE No1 的时间戳字段与 CTE No2 的时间戳字段之后,查询正在从 CTE No1 和 returns 字段中检索 ID 和时间戳,并从 CTE No 2,3,4,5,6 中检索字段, 3、4、5。更准确地说,下面是我的 SQL 查询的伪代码,我希望它是可以理解的。请不要过多关注 LEAD() 和 LAG() 函数查询问题本质上比它可能出现的要简单得多,并且是关于 CTE 何时有 0 行,但它们用于最终的 where 条件查询,如何解释这些情况。
with CTE1 AS
(SELECT timestamp1, id
from table1
where ...
...
...)
, CTE2 as
(select timestamp2,
id,
field1,
field2,
LAG(timestamp2,1) over partition by id order by timestamp2 as timestamp2_previous,
LEAD(timestamp2,1) over partition by id order by timestamp2 as timestamp2_next
from table2)
, CTE3 as
(select timestamp3,
id,
field1,
field2,
LAG(timestamp3,1) over partition by id order by timestamp3 as timestamp3_previous,
LEAD(timestamp3,1) over partition by id order by timestamp3 as timestamp3_next
from table3)
, CTE4 as
(select timestamp4,
id,
field1,
field2,
LAG(timestamp4,1) over partition by id order by timestamp4 as timestamp4_previous,
LEAD(timestamp4,1) over partition by id order by timestamp4 as timestamp4_next
from table4)
, CTE5 as
(select timestamp5,
id,
field1,
field2,
LAG(timestamp5,1) over partition by id order by timestamp5 as timestamp5_previous,
LEAD(timestamp5,1) over partition by id order by timestamp5 as timestamp5_next
from table5)
, CTE6 as
(select timestamp6,
id,
field1,
field2,
LAG(timestamp6,1) over partition by id order by timestamp6 as timestamp6_previous,
LEAD(timestamp6,1) over partition by id order by timestamp6 as timestamp6_next
from table6)
select cte1.id,
cte1.timestamp1,
case when cte1.timestamp1<cte2.timestamp2 and cte2.timestamp2_previous is null then cte2.field1 else cte2.field2,
case when cte1.timestamp1<cte3.timestamp3 and cte3.timestamp3_previous is null then cte3.field1 else cte3.field2,
case when cte1.timestamp1<cte4.timestamp4 and cte4.timestamp4_previous is null then cte4.field1 else cte4.field2,
case when cte1.timestamp1<cte5.timestamp5 and cte5.timestamp5_previous is null then cte5.field1 else cte5.field2,
case when cte1.timestamp1<cte6.timestamp6 and cte6.timestamp6_previous is null then cte6.field1 else cte6.field2
from cte1
left join cte2 on cte1.id=cte2.id
left join cte3 on cte1.id=cte3.id
left join cte4 on cte1.id=cte4.id
left join cte5 on cte1.id=cte5.id
left join cte6 on cte1.id=cte6.id
where 1
and
-- conditions for CTE2
( case when cte1.timestamp1 < cte2.timestamp2 and cte2.timestamp2_previous is null then 1=1
else cte1.timestamp1 > cte2.timestamp2 end
and
CASE
WHEN cte2.timestamp2_next is not null THEN cte1.timestamp1 < cte2.timestamp2_next ELSE 1=1
END
)
-- conditions for CTE3
( case when cte1.timestamp1 < cte3.timestamp3 and cte3.timestamp3_previous is null then 1=1
else cte1.timestamp1 > cte3.timestamp3 end
and
CASE
WHEN cte3.timestamp3_next is not null THEN cte1.timestamp1 < cte3.timestamp3_next ELSE 1=1
END
)
-- conditions for CTE4
( case when cte1.timestamp1 < cte4.timestamp4 and cte4.timestamp4_previous is null then 1=1
else cte1.timestamp1 > cte4.timestamp4 end
and
CASE
WHEN cte4.timestamp4_next is not null THEN cte1.timestamp1 < cte4.timestamp4_next ELSE 1=1
END
)
-- conditions for CTE5
( case when cte1.timestamp1 < cte5.timestamp5 and cte5.timestamp5_previous is null then 1=1
else cte1.timestamp1 > cte5.timestamp5 end
and
CASE
WHEN cte5.timestamp5_next is not null THEN cte1.timestamp1 < cte5.timestamp5_next ELSE 1=1
END
)
-- conditions for CTE6
( case when cte1.timestamp1 < cte6.timestamp6 and cte6.timestamp6_previous is null then 1=1
else cte1.timestamp1 > cte6.timestamp6 end
and
CASE
WHEN cte6.timestamp6_next is not null THEN cte1.timestamp1 < cte6.timestamp6_next ELSE 1=1
END
)
问题是第一个之后的一些 CTE,return 没有特定 ID 的记录(我针对特定 ID 测试了我的代码,而 CTE3 return 没有该 ID 的行). 在这些情况下,最后的 where 条件,它们都与 AND 连接,将评估其中没有记录的 CTE,并且 return 0 行(因为操作 TRUE AND NULL),即使所有其他 CTE 有该 ID 的数据,查询结果应该是为它们检索数据。
如何检索此类情况的结果,并在引用具有当前行的 CTE 的结果字段中获取 NULL?
正如@JNevill 所说,答案是将 where 条件移动到连接中。为了让您了解这是如何工作的,您在 cte1 和 cte2 之间的连接变为:
ON cte1.id = cte2.id AND ((cte1.timestamp < cte2.timestamp and cte2.timestamp_previous IS NULL) OR cte1.timestamp > cte2.timestamp) AND (cte2.timestamp_next IS NULL OR cte1.timestamp < cte2.timestamp_next)