MySQL:如何将多个区域的分区组合成尽可能大的连续区域

MySQL:如何将多个区域的分区组合成尽可能大的连续区域,mysql,Mysql,我一直在尝试实现一个相当复杂的SQL查询,也许很简单?压缩包含重复信息的表。我在SequelPro中使用MySQL 5.7.14。我是一个SQL新手,对连接、联合等有着基本的了解。我认为这一次需要一个包含一些group BY的子查询,但我不知道如何做到最好。 下表说明了我正在尝试做的一个简单示例: 对于每个col_1重复条目,当col_2和col_3设置的范围分别重叠时,我希望将其压缩为单个条目。对于col_4和col_5,应报告属于此范围的条目中的最大值。在上面的例子中,在col_1中,a有三

我一直在尝试实现一个相当复杂的SQL查询,也许很简单?压缩包含重复信息的表。我在SequelPro中使用MySQL 5.7.14。我是一个SQL新手,对连接、联合等有着基本的了解。我认为这一次需要一个包含一些group BY的子查询,但我不知道如何做到最好。 下表说明了我正在尝试做的一个简单示例:

对于每个col_1重复条目,当col_2和col_3设置的范围分别重叠时,我希望将其压缩为单个条目。对于col_4和col_5,应报告属于此范围的条目中的最大值。在上面的例子中,在col_1中,a有三个重叠的范围,我想用col_4和5的最大值将其压缩为col_1的最小值和col_2的最大值。对于col_2中的“b”,有两个范围31-50、12-15不重叠,因此它将按原样返回两行。对于c,它将返回一行,范围为100-300,列4和列5的值分别为3和2。此示例所需的全部结果如下所示:

我应该补充一点,在某些地方存在“null”值,它们应该被视为零。 有人知道最简单的方法吗? 提前谢谢你

更新:我尝试使用建议的范围设置查询,但出现错误。查询如下:

WITH a AS (SELECT range 
  , lower(col_2) AS startdate
  , max(upper(col_3)) OVER (ORDER BY range) AS `end`
   FROM   `combine`
   )
, b AS (
   SELECT *, lag(`end`) OVER (ORDER BY range) < `start` OR NULL AS step
   FROM   a
   )
, c AS (
   SELECT *, count(step) OVER (ORDER BY range) AS grp
   FROM   b
   )
SELECT daterange(min(`start`), max(`end`)) AS range
FROM   c
GROUP  BY grp
ORDER  BY 1;
我收到的错误是: 您的SQL语法有错误;查看与您的MySQL服务器版本对应的手册,以了解在“a AS SELECT range”附近使用的正确语法 ,低于起始日期时的2
,maxuppercol_3 OVE'在第1行这不是一件小事,但可以在一个查询中完成

最困难的部分是将一组间隔组合成最大可能的连续间隔。有关解决方案的详细信息,请参见

要获得您想要的结果,您现在需要:

使用链接中给出的查询,计算col1中每个值的最大可能连续间隔。 根据您的示例值,结果将是:

col_1 lower_bound upper_bound
a     20          60
b     12          15
b     31          50
c     100         300
将这些大间隔中的一个与_表中的每一行相关联。每行只能有一个这样的间隔,因此让我们内部联接:

选择我的\u表。*,大\u间隔。下限,大\u间隔。上限 从我的桌子上 内部连接我的超级查询您的表格大间隔 在大间隔上。col1=我的表。col1 和大间隔。下限=我的表。col3

你会得到:

col1 col2 col3 col4 col5 lower_bound upper_bound
a    45   50   1    0    20          60
a    50   61   6    0    20          60
a    20   45   0    5    20          60
b    31   50   0    1    31          50
b    12   15   5    0    12          15
c    100  200  3    2    100         300
c    150  300  1    2    100         300
然后很简单,只需按col1分组,下界,上界: 从上面的查询中,按col1、lower\U bound、upper\U bound选择col1、lower\U bound为col2、lower\U bound为col3、MAXcol4为col4、MAXcol5为col5

你得到的正是你想要的结果

回到最难的部分:上面提到的文章公开了PostgreSQL的解决方案。MySQL没有范围类型,但是可以调整解决方案。例如,不使用lowerrange,而是直接使用下限col2。该解决方案还使用了窗口函数,即lag和lead,但MySQL支持这两个函数,所以这里没有问题。还请注意,它们使用合并上限“无限”来防止未绑定的范围。因为你的范围是有限的,你不需要关心这个,你可以直接使用上限,即col3。以下是改编:

WITH a AS (
   SELECT
       col2,
       col3,
       col2 AS lower_bound, 
       MAX(col3) OVER (ORDER BY col2, col3) AS upper_bound
   FROM   combine
   )
, b AS (
   SELECT *, lag(upper_bound) OVER (ORDER BY col2, col3) < lower_bound OR NULL AS step
   FROM   a
   )
, c AS (
   SELECT *, count(step) OVER (ORDER BY col2, col3) AS grp
   FROM   b
   )
SELECT
    MIN(lower_bound) AS lower_bound,
    MAX(upper_bound) AS range
FROM   c
GROUP  BY grp
ORDER  BY 1;
这适用于单个组。如果要通过col1获得范围,可以如下调整:

WITH a AS (
   SELECT
       col1,
       col2,
       col3,
       col2 AS lower_bound, 
       MAX(col3) OVER (PARTITION BY col1 ORDER BY col2, col3) AS upper_bound
   FROM   combine
   )
, b AS (
   SELECT *, lag(upper_bound) OVER (PARTITION BY col1 ORDER BY col2, col3) < lower_bound OR NULL AS step
   FROM   a
   )
, c AS (
   SELECT *, count(step) OVER (PARTITION BY col1 ORDER BY col2, col3) AS grp
   FROM   b
   )
SELECT
    MIN(lower_bound) AS lower_bound,
    MAX(upper_bound) AS range
FROM   c
GROUP  BY col1, grp
ORDER  BY 1;
结合所有内容,我们可以得到以下内容,这些内容在您提供的示例中进行了测试,返回的结果与您期望的结果完全一致:

WITH a AS (
   SELECT
       col1,
       col2,
       col3,
       col2 AS lower_bound, 
       MAX(col3) OVER (PARTITION BY col1 ORDER BY col2, col3) AS upper_bound
   FROM   combine
   )
, b AS (
   SELECT *, lag(upper_bound) OVER (PARTITION BY col1 ORDER BY col2, col3) < lower_bound OR NULL AS step
   FROM   a
   )
, c AS (
   SELECT *, count(step) OVER (PARTITION BY col1 ORDER BY col2, col3) AS grp
   FROM   b
   )
, large_intervals AS (
    SELECT
        col1,
        MIN(lower_bound) AS lower_bound,
        MAX(upper_bound) AS upper_bound
    FROM   c
    GROUP  BY col1, grp
    ORDER  BY 1
    )
, combine_with_large_interval AS (
    SELECT
        combine.*,
        large_intervals.lower_bound,
        large_intervals.upper_bound
    FROM combine
    INNER JOIN large_intervals
        ON large_intervals.col1 = combine.col1
        AND large_intervals.lower_bound <= combine.col2
        AND large_intervals.upper_bound >= combine.col3
)
SELECT
    col1,
    lower_bound AS col2,
    upper_bound AS col3, 
    MAX(col4) AS col4, 
    MAX(col5) AS col5
FROM combine_with_large_interval
GROUP BY col1, lower_bound, upper_bound
ORDER BY col1, col2, col3;

我不认为你能在一个查询中完成。你可以用游标来做,但效率很低,或者为什么不在应用程序的代码中使用这种逻辑呢。谢谢你的回复,马蒂欧!谢谢你,马蒂欧!您很清楚,我需要组合一组区间,为每个col_1值定义一个范围,然后进行内部联接以将其链接到原始表。但是,我不确定如何执行链接中描述的第一步。它给出了四种不同的方法来实现这一点——我应该使用哪种方法?我认为我的示例与前一篇文章中的示例大不相同,因为我使用了两列中的信息来构建这个范围。所使用的sql函数超出了我的知识范围,这让我怀疑是否应该使用sql或其他方法。如果你能说得更清楚,我将不胜感激!我对答案进行了编辑,以使其更清楚地说明如何使解决方案适应您的用例,我认为您现在可以开始了。尽管Postgres更灵活,但您对MySQL很满意。你真的不需要理解文章中提到的解决方案的复杂性,只需用你的名称替换这些名称,并检查输出是否满足你的要求:也许可以在一张纸上应用该算法来了解其工作原理和原因。谢谢,我理解并调整了我对“获取范围”查询的输入
y、 但我有一些我不认识的错误。它们与我使用的查询一起出现在我上面帖子的“更新”部分。如果您有任何建议,我将不胜感激。您现在已经得到了完整的解决方案;