我正在迁移以下SQL Server查询
;WITH CTE AS
(
SELECT
ID, StartAptDate, EndAptDate,
RowNumber = ROW_NUMBER() OVER( ORDER BY StartAptDate ASC)
FROM
Appointments
WHERE
StylistId = 1
AND StartAptDate > CAST(CONVERT(CHAR(8), GetDate(), 112) AS DATETIME)
)
SELECT
FirstApptAvail = min(a.EndAptDate)
FROM
CTE a
INNER JOIN
CTE b ON a.RowNumber = b.RowNumber - 1
WHERE
datediff(minute, a.EndAptDate, b.StartAptDate) >= 15
AND (CAST(CONVERT(CHAR(8), a.StartAptDate, 108) AS DATETIME) BETWEEN '1900-01-01 07:57:57' AND '1900-01-01 18:59:59'
AND CAST(CONVERT(CHAR(8), a.EndAptDate, 108) AS DATETIME) BETWEEN '1900-01-01 07:57:57' AND '1900-01-01 18:59:59')
AND ((DATEPART(dw, a.StartAptDate) + @@DATEFIRST) % 7) NOT IN (0, 1)
将数据库迁移到postgres之后,我将上面的开始/结束列名称修改为“start”和“end”。事实证明postgres不喜欢“结束”名称所以我一直试图逃避它,但由于我也在使用DATE_PART函数,我需要尊重单引号。这是我到目前为止所做的,但我希望得到一些帮助来完成最后的转换(假设这可以移植到postgres)
WITH RECURSIVE CTE AS (SELECT id, start, "end", RowNumber =
ROW_NUMBER() OVER(order by start asc)
FROM api_appointment
WHERE employee_id = 1 AND start > now())
SELECT FirstApptAvail = min( a."end" )
FROM CTE a
INNER JOIN CTE b ON a.RowNumber = b.RowNumber - 1
WHERE DATE_PART('minute', a.start - b.start) >= 0
AND a.start >= '1900-01-01 07:57:57'
AND a.start <= '1900-01-01 18:59:59'
AND a."end" >= '1900-01-01 07:57:57'
AND a."end" <= '1900-01-01 18:59:59');
答案 0 :(得分:2)
看起来相当合理。没有架构和一些示例数据(http://sqlfiddle.com/对此有好处)很难确定。
必须引用 end
,因为它是CASE WHEN .. THEN .. END
的关键字。
一些整理:使用row_number() OVER (...) AS row_number
而不是RowNumber = row_number() OVER (...)
。缩进CTE术语。引用“开始”,谁知道什么时候它可能成为SQL中的关键字;毕竟,Stack Overflow的SQL突出显示已经突出了语法。我更喜欢使用标准current_timestamp
而不是非标准now()
,但效果相同。
您不需要WITH RECURSIVE
(并且它不应该工作),因为CTE没有递归术语;没有UNION ALL ... SELECT ... FROM cte
。
WITH cte AS (
SELECT
id, "start", "end", row_number() OVER (ORDER BY "start" ASC) AS row_number
FROM api_appointment
WHERE employee_id = 1 AND "start" > current_timestamp
)
SELECT min( a."end" ) AS FirstApptAvail
FROM cte a
INNER JOIN cte b ON a.row_number = b.row_number - 1
WHERE DATE_PART('minute', a.start - b.start) >= 0
AND a."start" >= '1900-01-01 07:57:57'
AND a."start" <= '1900-01-01 18:59:59'
AND a."end" >= '1900-01-01 07:57:57'
AND a."end" <= '1900-01-01 18:59:59';
那就是说,我怀疑有一种更简单的方法可以做你想做的事,但我想看一些样本数据。我也认为你会想要调查PostgreSQL的range types和exclusion constraints,它们非常适合这类工作。范围类型是GiST可索引的,用于重叠测试等操作。
哦,就是这样。如果您不能使用范围类型,请使用lead
。类似的东西:
SELECT min("end")
FROM (
SELECT
id, "start", "end",
lead(id) OVER w AS next_id,
lead("start") OVER w AS next_start,
lead("end") OVER w AS next_end
FROM appointment
WHERE employee_id = 1 AND "start" > current_timestamp
WINDOW w AS (ORDER BY "start")
) AS appts
WHERE appts."end" <> appts.next_start;
(参见:http://sqlfiddle.com/#!15/77f63/6)
在下次预约时,您需要处理下一个可用约会现在的情况。我可能只是生成一个虚拟约会,其中UNION ALL以current_timestamp
结束,如有必要,则四舍五入到小时。
这是使用tzranges的公式。我很惊讶没有找到一个简单而明显的方法来使用可索引的自联接来完成此操作,但我不太了解范围类型。这将处理下一个可用app't现在(四舍五入到最接近的小时),或者在所有当前约会结束后的情况:
http://sqlfiddle.com/#!15/712cb/4
如果您只对下一次感兴趣,可以点击ORDER BY 1 ASC LIMIT 1
。
如果您想要最快的1小时阻止,只需使用upper(booked_time) + INTERVAL '1' HOUR
生成上限,而不是使用下一个时间段的下限。