2017-02-02 48 views
0

目前,我有一個PostgreSQL數據庫(和一個結構幾乎相同的SQL Server數據庫)以及一些數據,如下例所示:如何在點之間的最大距離中找到最小值和最大值(SQL)

+----+---------+-----+ 
| ID | Name | Val | 
+----+---------+-----+ 
| 01 | Point A | 0 | 
| 02 | Point B | 050 | 
| 03 | Point C | 075 | 
| 04 | Point D | 100 | 
| 05 | Point E | 200 | 
| 06 | Point F | 220 | 
| 07 | Point G | 310 | 
| 08 | Point H | 350 | 
| 09 | Point I | 420 | 
| 10 | Point J | 550 | 
+----+---------+-----+ 

ID = PK (auto increment); 
Name = unique; 
Val = unique; 

現在,假設我只有Point F (220),我想找到的最低值和最大的價值與數據之間的最大距離小於100。

所以,我的結果必須返回:

  • 最低:點E(200)
  • 最大:點I(420)

一步解釋步驟(和因爲英語不是我的主要語言):

  • 尋找最低值:

    Initial value = Point F (220); 
    Look for the lower closest value of Point F (220): Point E (200); 
    200(E) < 220(F) = True; 220(F) - 200(E) < 100 = True; 
    Lowest value until now = Point E (200) 
    
    Repeat 
    
    Look for the lower closest value of Point E (200): Point D (100); 
    100(D) < 200(E) = True; 200(E) - 100(D) < 100 = False; 
    Lowest value = Point E (200); Break; 
    
  • 展望FOT最大的價值:

    Initial value = Point F (220); 
    Look for the biggest closest value of Point F (220): Point G (310); 
    310(G) > 220(F) = True; 310(G) - 220(F) < 100 = True; 
    Biggest value until now = Point G (310) 
    
    Repeat 
    
    Look for the biggest closest value of Point G (310): Point H (350); 
    350(H) > 310(G) = True; 350(H) - 310(G) < 100 = True; 
    Biggest value until now = Point H (350) 
    
    Repeat 
    
    Look for the biggest closest value of Point H (350): Point I (420); 
    420(I) > 350(H) = True; 420(I) - 350(H) < 100 = True; 
    Biggest value until now = Point I (420) 
    
    Repeat 
    
    Look for the biggest closest value of Point I (420): Point J (550); 
    550(J) > 420(I) = True; 550(J) - 420(I) < 100 = False; 
    Biggest value Point I (420); Break; 
    
+0

您如何識別您的「初始值」?你是否有它的價值('Val',在你的例子中是'220'),它的'ID'(在你的例子中是'06')還是它的名字(OFC,只有它是唯一的)? – pozs

+0

「ID」是一個自動增量編號。 'Val'是獨一無二的,'name'也是獨一無二的!我最初只有'ID'。 –

+0

從你對已刪除答案的評論(* t在F點與最小值和最大值之間的間隔不小於100,它在上一點和下一點*之間),在我看來這是一個特殊的[標籤:間隙 - 和問題。 – pozs

回答

2

這可以通過使用Windows Functions和一些工作來完成。

SELECT 
    id, name, val, 
    lag(val) OVER(ORDER BY id) AS prev_val, 
    lead(val) OVER(ORDER BY id) AS next_val 
FROM 
    points 

產生:

| id | name | val | prev_val | next_val | 
|----|---------|-----|----------|----------| 
| 1 | Point A | 0 | (null) |  50 | 
| 2 | Point B | 50 |  0 |  75 | 
| 3 | Point C | 75 |  50 |  100 | 
| 4 | Point D | 100 |  75 |  200 | 
| 5 | Point E | 200 |  100 |  220 | 
| 6 | Point F | 220 |  200 |  310 | 
| 7 | Point G | 310 |  220 |  350 | 
| 8 | Point H | 350 |  310 |  420 | 
| 9 | Point I | 420 |  350 |  550 | 
| 10 | Point J | 550 |  420 | (null) | 

lag and lead窗口功能

在一步步的方式,則可以通過將具有這種選擇定義一個表(姑且稱之爲point_and_prev_next)啓動用於獲取表格中的前一個值和下一個值(按ID排序,而不是任何分區)。

接下來,我們製作第二個表point_and_dist_prev_next,它使用val,prev_valnext_val來計算到上一點的距離和到下一點的距離。這將與下面的SELECT來計算:

SELECT 
    id, name, val, (val-prev_val) AS dist_to_prev, (next_val-val) AS dist_to_next 
FROM 
    point_and_prev_next 

這是執行它後你會得到什麼:

| id | name | val | dist_to_prev | dist_to_next | 
|----|---------|-----|--------------|--------------| 
| 1 | Point A | 0 |  (null) |   50 | 
| 2 | Point B | 50 |   50 |   25 | 
| 3 | Point C | 75 |   25 |   25 | 
| 4 | Point D | 100 |   25 |   100 | 
| 5 | Point E | 200 |   100 |   20 | 
| 6 | Point F | 220 |   20 |   90 | 
| 7 | Point G | 310 |   90 |   40 | 
| 8 | Point H | 350 |   40 |   70 | 
| 9 | Point I | 420 |   70 |   130 | 
| 10 | Point J | 550 |   130 |  (null) | 

而且,在這一點上,(與「F」點開始),我們可以獲得第一個「錯點了」(失敗的第一個「距離前面的」 < 100)以下查詢方式:

SELECT 
     max(id) AS first_wrong_up 
FROM 
    point_and_dist_prev_next 
WHERE 
    dist_to_prev >= 100 
    AND id <= 6  -- 6 = Point F 

這只是看起來對於最靠近我們的一個參考( 「F」),其與前一個< 100失敗。

結果是:

| first_wrong_up | 
|----------------| 
|    5 | 

第一個「錯點」下去以等同的方式進行計算。

所有這些查詢可以使用Common Table Expressions,也稱爲WITH查詢放在一起,你會得到:

WITH point_and_dist_prev_next AS 
(
    SELECT 
     id, name, val, 
     val - lag(val) OVER(ORDER BY id) AS dist_to_prev, 
     lead(val) OVER(ORDER BY id)- val AS dist_to_next 
    FROM 
     points 
), 
first_wrong_up AS 
(
SELECT 
    max(id) AS first_wrong_up 
FROM 
    point_and_dist_prev_next 
WHERE 
    dist_to_prev >= 100 
    AND id <= 6  -- 6 = Point F 
), 
first_wrong_down AS 
(
SELECT 
    min(id) AS first_wrong_down 
FROM 
    point_and_dist_prev_next 
WHERE 
    dist_to_next >= 100 
    AND id >= 6  -- 6 = Point F 
) 
SELECT 
    (SELECT name AS "lowest value" 
     FROM first_wrong_up 
     JOIN points ON id = first_wrong_up), 
    (SELECT name AS "biggest value" 
     FROM first_wrong_down 
     JOIN points ON id = first_wrong_down) ; 

它提供了以下結果:

| lowest value | biggest value | 
|--------------|---------------| 
|  Point E |  Point I | 

你可以在SQLFiddle檢查。


注意:假定id列總是增加。如果不是,則需要使用val列代替(顯然,假設它始終保持增長)。

+0

tks!我還沒有測試過(現在SQLFiddle無法正常工作)!但我喜歡'逐步時尚'的解釋:D –

+0

這個SQL有一個「bug」。如果不是搜索F點的「最低和最大值」,而是搜索A點,B,C或D點的較低和較大值,則最低值將爲NULL。如果我搜索Point J的最大值,會發生同樣的事情。 –

+0

爲了糾正這個「bug」,我在'WHERE'條件中改變了:'(dist_to_prev> = 100或dist_to_prev IS NULL)'AND'(dist_to_next> = 100或dist_to_next IS NULL)'。 –

相關問題