2017-02-02 45 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)'。 –

相关问题