有条件地使用 set-retuning 函数作为连接子句
Conditionally use set-retuning function as join clause
上下文
我正在实施一个“职位发布”功能,该功能将列出可供用户使用的职位。可以为间隔 start_date
和 end_date
或数组 service_dates
中的稀疏日期设置作业。可用性检查应使用 jobs
模型中存在的先前描述的日期信息针对 availabilities
模型进行。
它们的结构如下
职位
Column | Type
-------------------------+------------------------------
id | bigint
service_dates | timestamp without time zone[]
start_date | timestamp without time zone
end_date | timestamp without time zone
可用性
Column | Type
-------------------------+------------------------------
id | bigint
user_id | integer
date | date
spaces_left | integer
然后加入时,我想将工作日期与 availabilities
table 中存在的匹配日期交叉。问题是工作的日期有两种不同的方式。
对于稀疏日期,如果我执行以下左连接,我可以让它工作:
SELECT j.*
FROM jobs j
LEFT JOIN
availabilities a
ON a.user_id = 1234
AND a.date in (
--this will compose a set of the sparse dates
SELECT UNNEST(j.service_dates)
)
对于 start_date
和 end_date
选项,这将起到作用:
SELECT j.*
FROM jobs j
LEFT JOIN
availabilities a
ON a.user_id = 1234
AND a.date in (
--this will compose a set with the date range between the two dates
SELECT GENERATE_SERIES(j.start_date::date, j.end_date::date, '1 day'::interval)
)
我的问题是我需要根据 jobs.service_dates
存在的条件为特定的工作记录申请一个或另一个。本来我以为我可以做到以下几点:
SELECT j.*
FROM jobs j
LEFT JOIN
availabilities a
ON a.user_id = 1234
AND a.date in (
-- use one or the other based on whether or not there's something on j.service_dates
CASE cardinality(j.service_dates) > 0
WHEN TRUE THEN
(SELECT UNNEST(j.services))
ELSE
(SELECT GENERATE_SERIES(j.start_date::date, j.end_date::date, '1) day'::interval)
END
)
但是我收到以下错误:
ERROR: more than one row returned by a subquery used as an expression
如果我尝试从 CASE
语句返回的任何内容中 select 我得到以下结果:
ERROR: set-returning functions are not allowed in CASE
我想我已经掌握了如何在可以呈现的场景中逻辑关联 table 的东西。我需要的是一种有条件地应用任一逻辑的方法。
更新 1
正如@Kadet 指出的那样,availabilitites
table 中缺少 user_id
所以我添加了它。
他的回答也让我完成了我想要的,我最终没有采用完全相同的解决方案,但我在加入他建议的方式而不是我尝试的方式时应用了 CASE
。他的回答没有任何问题,我只是没有使用它,因为这个查询还有其他特殊性,我没有考虑(为简单起见),这些特殊性会更适合我选择的方式。这是我的做法:
LEFT JOIN
availabilities a
ON a.user_id = 1234
AND case when cardinality(j.service_dates) > 0
THEN a.date IN ( SELECT unnest(j.service_dates) )
ELSE a.date IN ( SELECT GENERATE_SERIES(j.start_date::date, j.end_date::date, '1 day'::interval) )
END
更新 2
值得一提的是@Ely,他的回答在概念上是完全可以接受的table,我相信这是正确的。这对我不起作用,因为我未能在问题的初稿中公开细节,即 service_dates
和 start_date
以及 end_date
并不相互排斥。前 2 个始终存在,并填充了 service_date
的最小值和最大值。但他的想法可能适用于任何有类似问题但没有特定限制的人。所以,如果你是他的话,请给他投票:)
(A && !B) || (!A && B)
怎么样 - 这基本上就是您要实现的目标。非此即彼。
我会尝试将其翻译成以下内容:
SELECT j.*
FROM jobs j
LEFT JOIN
availabilities a
ON a.user_id = 1234
AND
(
(
a.date in (SELECT UNNEST(j.service_dates)
AND (cardinality(j.service_dates) = 0)
)
OR
(
a.date in (SELECT GENERATE_SERIES(j.start_date::date, j.end_date::date, '1 day'::interval)
AND NOT (cardinality(j.service_dates) = 0)
)
)
可能存在语法错误。我不是很精通,也没有从你写的东西中得到启发,但我希望你能理解。
此 table 结构无效或不完整(可用性中没有 user_id 字段 table)但只是为了展示一个想法
SELECT j.*
FROM jobs j
LEFT JOIN
availabilities a
ON a.user_id = 1234
WHERE
case when cardinality(j.service_dates) > 0
then a.date = any(j.service_dates)
else a.date between start_date and end_date
end
或者,如果需要生成系列
SELECT j.*
FROM jobs j
LEFT JOIN
availabilities a
ON a.user_id = 1234
WHERE
case when cardinality(j.service_dates) > 0
then a.date = any(j.service_dates)
else a.date = any ( select GENERATE_SERIES(j.start_date::date, j.end_date::date, '1 day'::interval))
end
上下文
我正在实施一个“职位发布”功能,该功能将列出可供用户使用的职位。可以为间隔 start_date
和 end_date
或数组 service_dates
中的稀疏日期设置作业。可用性检查应使用 jobs
模型中存在的先前描述的日期信息针对 availabilities
模型进行。
它们的结构如下
职位
Column | Type
-------------------------+------------------------------
id | bigint
service_dates | timestamp without time zone[]
start_date | timestamp without time zone
end_date | timestamp without time zone
可用性
Column | Type
-------------------------+------------------------------
id | bigint
user_id | integer
date | date
spaces_left | integer
然后加入时,我想将工作日期与 availabilities
table 中存在的匹配日期交叉。问题是工作的日期有两种不同的方式。
对于稀疏日期,如果我执行以下左连接,我可以让它工作:
SELECT j.*
FROM jobs j
LEFT JOIN
availabilities a
ON a.user_id = 1234
AND a.date in (
--this will compose a set of the sparse dates
SELECT UNNEST(j.service_dates)
)
对于 start_date
和 end_date
选项,这将起到作用:
SELECT j.*
FROM jobs j
LEFT JOIN
availabilities a
ON a.user_id = 1234
AND a.date in (
--this will compose a set with the date range between the two dates
SELECT GENERATE_SERIES(j.start_date::date, j.end_date::date, '1 day'::interval)
)
我的问题是我需要根据 jobs.service_dates
存在的条件为特定的工作记录申请一个或另一个。本来我以为我可以做到以下几点:
SELECT j.*
FROM jobs j
LEFT JOIN
availabilities a
ON a.user_id = 1234
AND a.date in (
-- use one or the other based on whether or not there's something on j.service_dates
CASE cardinality(j.service_dates) > 0
WHEN TRUE THEN
(SELECT UNNEST(j.services))
ELSE
(SELECT GENERATE_SERIES(j.start_date::date, j.end_date::date, '1) day'::interval)
END
)
但是我收到以下错误:
ERROR: more than one row returned by a subquery used as an expression
如果我尝试从 CASE
语句返回的任何内容中 select 我得到以下结果:
ERROR: set-returning functions are not allowed in CASE
我想我已经掌握了如何在可以呈现的场景中逻辑关联 table 的东西。我需要的是一种有条件地应用任一逻辑的方法。
更新 1
正如@Kadet 指出的那样,availabilitites
table 中缺少 user_id
所以我添加了它。
他的回答也让我完成了我想要的,我最终没有采用完全相同的解决方案,但我在加入他建议的方式而不是我尝试的方式时应用了 CASE
。他的回答没有任何问题,我只是没有使用它,因为这个查询还有其他特殊性,我没有考虑(为简单起见),这些特殊性会更适合我选择的方式。这是我的做法:
LEFT JOIN
availabilities a
ON a.user_id = 1234
AND case when cardinality(j.service_dates) > 0
THEN a.date IN ( SELECT unnest(j.service_dates) )
ELSE a.date IN ( SELECT GENERATE_SERIES(j.start_date::date, j.end_date::date, '1 day'::interval) )
END
更新 2
值得一提的是@Ely,他的回答在概念上是完全可以接受的table,我相信这是正确的。这对我不起作用,因为我未能在问题的初稿中公开细节,即 service_dates
和 start_date
以及 end_date
并不相互排斥。前 2 个始终存在,并填充了 service_date
的最小值和最大值。但他的想法可能适用于任何有类似问题但没有特定限制的人。所以,如果你是他的话,请给他投票:)
(A && !B) || (!A && B)
怎么样 - 这基本上就是您要实现的目标。非此即彼。
我会尝试将其翻译成以下内容:
SELECT j.*
FROM jobs j
LEFT JOIN
availabilities a
ON a.user_id = 1234
AND
(
(
a.date in (SELECT UNNEST(j.service_dates)
AND (cardinality(j.service_dates) = 0)
)
OR
(
a.date in (SELECT GENERATE_SERIES(j.start_date::date, j.end_date::date, '1 day'::interval)
AND NOT (cardinality(j.service_dates) = 0)
)
)
可能存在语法错误。我不是很精通,也没有从你写的东西中得到启发,但我希望你能理解。
此 table 结构无效或不完整(可用性中没有 user_id 字段 table)但只是为了展示一个想法
SELECT j.*
FROM jobs j
LEFT JOIN
availabilities a
ON a.user_id = 1234
WHERE
case when cardinality(j.service_dates) > 0
then a.date = any(j.service_dates)
else a.date between start_date and end_date
end
或者,如果需要生成系列
SELECT j.*
FROM jobs j
LEFT JOIN
availabilities a
ON a.user_id = 1234
WHERE
case when cardinality(j.service_dates) > 0
then a.date = any(j.service_dates)
else a.date = any ( select GENERATE_SERIES(j.start_date::date, j.end_date::date, '1 day'::interval))
end