Postgresql 如何在最大点间距(SQL)中找到最小值和最大值

Postgresql 如何在最大点间距(SQL)中找到最小值和最大值,postgresql,Postgresql,目前,我有一个PostgreSQL数据库(和一个结构几乎相同的SQL Server数据库),其中包含一些数据,如下例: +----+---------+-----+ | ID | Name | Val | +----+---------+-----+ | 01 | Point A | 0 | | 02 | Point B | 050 | | 03 | Point C | 075 | | 04 | Point D | 100 | | 05 | Point E | 200 | | 06 |

目前,我有一个PostgreSQL数据库(和一个结构几乎相同的SQL Server数据库),其中包含一些数据,如下例:

+----+---------+-----+
| 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;
现在,假设我只有点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;
    
    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;
    
    初始值=点F(220);
    查找点F(220)的最近下限值:点E(200);
    200(E)<220(F)=正确;220(F)-200(E)<100=正确;
    迄今为止的最低值=E点(200)
    重复
    查找点E(200)的最近下限值:点D(100);
    100(D)<200(E)=正确;200(E)-100(D)<100=假;
    最低值=E点(200);打破
    
  • 寻找最大价值:

    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;
    
    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;
    
    初始值=点F(220);
    查找点F(220)的最大最近值:点G(310);
    310(G)>220(F)=正确;310(G)-220(F)<100=真;
    迄今为止的最大值=点G(310)
    重复
    查找点G(310)的最大最近值:点H(350);
    350(H)>310(G)=正确;350(H)-310(G)<100=正确;
    迄今为止的最大值=H点(350)
    重复
    查找点H(350)的最大最近值:点I(420);
    420(I)>350(H)=正确;420(I)-350(H)<100=真;
    迄今为止的最大值=点I(420)
    重复
    寻找点I(420)的最大最近值:点J(550);
    550(J)>420(I)=正确;550(J)-420(I)<100=假;
    最大值点I(420);打破
    
这可以通过使用和一些工作来完成

以一步一步的方式,您可以从以下选择定义一个表开始(我们称之为
point_和
):

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) |
窗口函数用于从表中获取上一个值和下一个值(按id排序,不按任何分区)

接下来,我们制作第二个表
point_和
,它使用
val
prev_val
Next_val
,计算到上一点的距离和到下一点的距离。这将通过以下选择进行计算:

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
以等效方式计算下降的第一个“错误点”

所有这些查询都可以使用(
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) ;
你可以在网上查一下


注:假设
id
列始终在增加。如果不是,则必须使用
val
列(显然,假设它一直在增长)。

这可以通过使用和一些工作来完成

以一步一步的方式,您可以从以下选择定义一个表开始(我们称之为
point_和
):

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) |
窗口函数用于从表中获取上一个值和下一个值(按id排序,不按任何分区)

接下来,我们制作第二个表
point_和
,它使用
val
prev_val
Next_val
,计算到上一点的距离和到下一点的距离。这将通过以下选择进行计算:

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
以等效方式计算下降的第一个“错误点”

所有这些查询都可以使用(
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) ;
你可以在网上查一下



注:假设
id
列始终在增加。如果不是,则必须使用
val
列(显然,假设它一直在增长)。

如何识别“初始值”?您是否有它的值(
Val
,即您的示例中的
220
),它的
ID
06
)或它的名称(OFC,仅当它唯一时)?
ID
是一个自动递增的数字
Val
是唯一的,
name
也是唯一的!我最初只有
ID
。从您对删除答案的评论(t不是点F和最小值和最大值之间小于100的间隔!它是上一点和下一点之间的间隔)我认为这是一个特殊问题。您如何识别“初始值”?您是否有它的值(
Val
,即您的示例中的
220
),它的
ID
06
)或它的名称(OFC,仅当它唯一时)?
ID
是一个自动递增的数字
Val
是唯一的,
name
也是唯一的!最初我只有
ID
。从你对删除答案的评论来看(在点F和最小值和最大值之间的间隔不是小于100!在上一点和下一点之间),我认为这是一个特殊问题。tks!我还没有测试(SQLFIDLE现在不工作)!但是我喜欢“逐步时尚”中的解释:在这个SQL中有一个“bug”。如果不搜索点F的“最小值和最大值”,而是搜索点A、B、C或D的较小值和较大值,则最小值将为空。如果我搜索点J的最大值,也会发生同样的情况。为了纠正这个“错误”,我只是在
中将条件更改为:
(dist\u To\u prev>=100或dist\t