2013-12-10 113 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」的第二個片段 - 這是建議的解決方案(實質上是改進原始的)? –

相關問題