2016-02-05 61 views
2

我們假設我們有一個表邊界(country1,country2)包含兩個彼此相鄰的國家,例如。 (瑞典,挪威)等。我想找到所有可以從特定國家到達的國家,比如說瑞典,只使用過境。PostgreSQL遞歸

這裏是我的解決方案的第一部分:

WITH RECURSIVE border(countryin) AS (
    select distinct country 
    from (select country2::character varying(4) as country 
      from borders where country1 = 'S' 
      union 
      select country1::character varying(4) as country 
      from borders where country2 = 'S') a 
    UNION 
    select distinct sp.country::varchar(4) 
    from (select country1::varchar(4) as country, country2 as n 
      from borders) sp 
    join (select country2::varchar(4) as country, country1 as n, countryin as temp 
      from borders, border) st 
     on sp.country = st.n 
    and sp.country in st.temp 
    where true    
) 
SELECT distinct countryin, name 
FROM border, country 
where countryin = code ; 

,我不能去工作的唯一的事情就是如何讓在結果邊境表存在一個特定的國家設置的約束。我嘗試在st.temp和其他幾種方法中使用和sp.country,但是我無法使其工作。

有人能告訴我如何解決這個問題嗎?

當前結果

  • 現在,我得到一個錯誤,指出「 錯誤:語法錯誤或接近 」ST「 線4:... S,邊框)ST上SP。國= st.n和sp.country在st.temp 「

期望的結果

  • 列出可以使用從'S'開始的邊界以遞歸方式到達的所有縣。因此,如果我們有(S,N),(N,R),(R,C),(D,A),我們將得到:(N,R,C)
+0

我不明白這個問題,你能提供一個當前結果和慾望輸出的例子嗎?請閱讀http:// stackoverflow。com/help/how-to-ask 這裏是[** START **]的好地方(http://spaghettidba.com/2015/04/24/how-to-post-at-sql-question -on-a-public-forum /) –

+0

@JuanCarlosOropeza,完成! – Artem

回答

1

我相信改善空間,但看起來像做這項工作。

基本情況,你得到的「其他」的國家,「S」出現在任何一側

遞歸的情況下得到邊境新的國家,已經在行駛路徑上的任何國家,但避免了一個與'S'不能返回原點。還包括一個變量來跟蹤遞歸深度,因此不會永遠循環。 (不記得現在有多少個國家)。

完成後,我添加過濾器DISTINCT刪除重複。

也許我可以在recursive case上包含一個過濾器,以避免返回相同的國家。不確定哪一個更有效。

AND ( b.country1 NOT IN (SELECT country FROM Travel) 
    AND b.country2 NOT IN (SELECT country FROM Travel) 
    ) 

SQL Fiddle DEMO

WITH RECURSIVE travel(r_level, country) AS (
    select distinct 1 as r_level, 
        CASE WHEN country1 = 'S' THEN country2 
         ELSE country1   
        END as country 
    from borders 
    where country1 = 'S' 
     or country2 = 'S'  
    UNION 
    select distinct t.r_level + 1 as r_level, 
        CASE WHEN b.country1 = t.country THEN b.country2 
         ELSE b.country1 
        END as country   
    from borders b 
    join travel t 
     ON (b.country1 = t.country OR b.country2 = t.country) 
    AND (b.country1 <> 'S' AND b.country2 <> 'S') 
    WHERE t.r_level < 300 
) 
SELECT DISTINCT country 
FROM travel 

輸出

| country | 
|---------| 
|  N | 
|  R | 
|  C | 

請隨時提供更完整sqlFiddle與更多的國家提高了測試。

+0

我已經用循環測試了一個更大的數據,它的工作原理。我有一個可以消除循環依賴的替代解決方案,但是你的查詢要簡單得多(並且畢竟效率不算低)。 – klin

+0

感謝@klin很高興知道。如果可能的話分享您的解決方案和大量數據,我希望做更多的測試。 –

+0

它不是*非常大*數據... [SqlFiddle](http://sqlfiddle.com/#!15/ae549/4) – klin