2013-12-10 56 views
1

我迁移下面的SQL Server查询是否可以将SQL Server CTE查询转换为Postgres?

;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的我修改上面简单的开始/结束列名后“开始”和“结束”。事实证明,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'); 

回答

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 typesexclusion 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

你需要下一个可用的任命是现在,下订约会前处理的情况。我可能只是生成一个虚拟约会,结束于current_timestamp的UNION ALL,必要时舍入到小时。

这是一个使用tzranges的公式。我很惊讶没有找到一个简单明了的方法来做到这一点与索引自联接,但我不知道范围类型。这种处理下一个可用的app't现在(四舍五入到最接近的小时)的情况下,或者当它毕竟目前的预约完成:

http://sqlfiddle.com/#!15/712cb/4

如果你只在接下来的时间感兴趣,你可以直接用ORDER BY 1 ASC LIMIT 1

如果您想要最快的1小时块,只需使用upper(booked_time) + INTERVAL '1' HOUR来生成上限,而不是使用下一个时间段的下限。

+0

@ErwinBrandstetter错过了别名,谢谢。 –

+0

此外,这里的数据表达的范围类型相同。它们看起来不像我想象的那么有用,或者我不够聪明,无法解决如何使用它们。 http://sqlfiddle.com/#!15/712cb –

+0

哇 - 完全被你的答案完全吹走了!感谢您花时间帮助!一个问题 - 使用“lead”的第二个片段 - 这是建议的解决方案(实质上是改进原始的)? –

相关问题