【问题标题】:Generate Column representing the row's index within the group生成表示组内行索引的列
【发布时间】:2019-03-23 22:03:28
【问题描述】:

我有一个表格,其中描述了某个日期的测量值。但是,有不同类型的测量(可能具有相同的日期)。该表如下所示:

Table: measurements

type_id     date        value
--------------------------------
1           2018-12-31  40
1           2019-01-01  42
1           2019-01-02  43
1           2019-01-04  44
2           2019-01-01  80
2           2019-01-02  79
2           2019-01-05  78

(请注意,省略了 1/2019-01-03 和 2/2019-01-03 和 2/2019-01-04!)

不同类型之间没有可比性,因此需要严格区分。 问题是我必须将此表与另一个表连接起来,该表的顺序不是基于日期,而是使用基于基线日期(从计数开始)的偏移/索引方法(整数)!请注意,此索引计算,而不是“自...以来的天数”的偏移量。

为了这个例子,我们假设它看起来像这样(从现实生活中简化):

Table: type_master

type_id         base_date
--------------------------
1               2019-01-01
2               2019-01-01
3               2018-12-22
...

这就是为什么我想要“每个组中的行索引计数”。因此,SQL 语句(将在 MySQL/MariaDB 上执行)的结果集应该类似于以下内容:

type_id      date      value   index_in_group
-------------------------------------------
1           2018-12-31  40     null (and not -1!)
1           2019-01-01  42     1
1           2019-01-02  43     2
1           2019-01-04  44     3  (and not 4!)
2           2019-01-01  80     1
2           2019-01-02  79     2
2           2019-01-05  78     3  (and not 5!)

(索引应使用按日期升序进行 - 您可以假设那些被跳过的日期也被有意跳过)。

我已经开始尝试https://stackoverflow.com/a/5351692/6350762中提到的想法了

select @n := @n + 1 index, m.*
from (select @n:=0) initvars, measurements m

这给了我很好的索引计数 - 但它忽略了组的变化并继续计数,即使新组开始。

您还可以考虑以下几点:

  • 如果可能,所有这些最终都应该在视图中结束。
  • 可以编写 MySQL 过程,但会产生不希望的副作用 - 如果是这种情况,那么我还可以在运行在数据库之上的应用程序服务器上实现必要的逻辑。但是,由于数据访问的原因,最好将逻辑放在数据库中。
  • 良好的性能会很好,但不是最重要的:measurements 上的记录数在 40k 左右; type_master 上的记录数有几十个。
  • 服务器在 MariaDB 10.1.26 上运行。 (稍后根据评论更新

有什么想法可以通过可接受的 SQL 语句来实现吗?

【问题讨论】:

标签: mysql sql group-by


【解决方案1】:

在 MySQL 8+ 和最新版本的 MariaDB 中,您可以使用窗口函数:

select m.*,
       (case when m.date >= tm.base_date 
             then row_number() over (partition by type_id,
                                                  m.date >= tm.base_date
                                     order by m.date
                                    )
        end) as index_in_group
from measurements m left join
     type_master tm
     on m.type_id = tm.type_id;

在旧版本中,您可以使用变量。这有点棘手;我认为逻辑是:

select m.*,
       (@rn := if(m.date < tm.base_date, NULL,
                  if(@t = m.type_id, @rn + 1,
                     if(@t := m.type_id, 1, 1)
                    )
                 )
       ) as index_in_group
from (select m.*, tm.base_date
      from measurements m left join
           type_master tm
           on m.type_id = tm.type_id
      order by m.type_id, m.date
     ) m cross join
     (select @t := -1, @rn := 0) params

【讨论】:

  • sh*t;服务器在 MariaDB 10.1.26 上运行; ROW_NUMBER() 在 10.2 中引入(另见 mariadb.com/kb/en/library/row_number )。有没有简单的替代方案? (我已经相应地更新了问题) - 仍然投票“赞成”,因为这已经非常有帮助了。
  • 第二个变种正是我所需要的!谢谢一百万!
  • 为了完整起见:由于ErrorCode 1351: View's SELECT contains a variable or parameterSELECT 语句(上面的第二个变体)无法存储在 MariaDB 10.1.26 的视图中。然而,我相信这是我们能得到的结果,所以仍然保留这个答案的“已回答”标志。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-14
相关资源
最近更新 更多