SQL中分区上的字符串连接?
Posted
技术标签:
【中文标题】SQL中分区上的字符串连接?【英文标题】:String Concat over partition in SQL? 【发布时间】:2021-01-23 04:44:29 【问题描述】:我想在排名到新列(group by
似乎不起作用)后连接周围的行(在以下示例中只有周围的 2 行),这是我拥有的数据:
架构 (MySQL v8.0)
CREATE TABLE log_table (
`user_id` VARCHAR(5),
`date_time` DATETIME,
`event_name` VARCHAR(10),
`trivial` int
);
INSERT INTO log_table
(`user_id`, `date_time`, `event_name`, `trivial`)
VALUES
('001', '2020-12-10 10:00:02', 'c', 3),
('001', '2020-12-10 10:00:01', 'b', 9),
('001', '2020-12-10 10:00:40', 'e', 2),
('001', '2020-12-10 10:00:20', 'd', 6),
('001', '2020-12-10 10:00:00', 'a', 1),
('002', '2020-12-10 10:00:10', 'C', 9),
('002', '2020-12-10 10:00:50', 'D', 0),
('002', '2020-12-10 10:00:02', 'A', 2),
('002', '2020-12-10 10:00:09', 'B', 4);
为了说明我想要做什么。我可以使用 sum
子句对数值求和,如下所示:Query #1
SELECT *,
SUM(trivial)
over(
PARTITION BY user_id
ORDER BY user_id, date_time ROWS BETWEEN 2 preceding AND 2 following)
AS
trivial_new
FROM log_table;
user_id | date_time | event_name | trivial | trivial_new |
---|---|---|---|---|
001 | 2020-12-10 10:00:00 | a | 1 | 13 |
001 | 2020-12-10 10:00:01 | b | 9 | 19 |
001 | 2020-12-10 10:00:02 | c | 3 | 21 |
001 | 2020-12-10 10:00:20 | d | 6 | 20 |
001 | 2020-12-10 10:00:40 | e | 2 | 11 |
002 | 2020-12-10 10:00:02 | A | 2 | 15 |
002 | 2020-12-10 10:00:09 | B | 4 | 15 |
002 | 2020-12-10 10:00:10 | C | 9 | 15 |
002 | 2020-12-10 10:00:50 | D | 0 | 13 |
View on DB Fiddle
对于字符串字段event_name
,我尝试了这个sn-p:Query #2
SELECT *,
Concat(event_name)
over(
PARTITION BY user_id
ORDER BY user_id, date_time ROWS BETWEEN 2 preceding AND 2 following)
AS
event_name_new
FROM log_table
这是我的预期结果:
user_id | date_time | event_name | trivial | event_name_new |
---|---|---|---|---|
001 | 2020-12-10 10:00:00 | a | 1 | abc |
001 | 2020-12-10 10:00:01 | b | 9 | abcd |
001 | 2020-12-10 10:00:02 | c | 3 | abcde |
001 | 2020-12-10 10:00:20 | d | 6 | bcde |
001 | 2020-12-10 10:00:40 | e | 2 | cde |
002 | 2020-12-10 10:00:02 | A | 2 | ABC |
002 | 2020-12-10 10:00:09 | B | 4 | ABCD |
002 | 2020-12-10 10:00:10 | C | 9 | ABCD |
002 | 2020-12-10 10:00:50 | D | 0 | BCD |
但是 Query #2 无法将我带到这里,我已经在 Google 上搜索过,但我只能找到关于 group by
的信息(请参阅 this 和 this 和 this)。
我知道我可以通过使用 LAG
和 LEAD
(对于以下行)来解决这个问题,但是我需要连接新列,当我需要连接许多行时,我需要做很多手动工作比如用,
等分隔符连接它们。
我可以在不使用LAG
和LEAD
的情况下一步完成吗?
【问题讨论】:
根据您提供的示例数据和预期输出,我不清楚使用LAG
有什么问题?
@Nick 如果sum
可以在没有LAG
的情况下这样做,我想知道是否可以连接字符串?
@Nick 例如,我想将周围的 5 行(在该行之前的 5 行和在该行之后的 5 行)连接到新列(如何)我可以巧妙地做到这一点?
不幸的是,没有办法为字符串连接指定窗口函数......你能用一些更复杂的预期输出数据来编辑你的问题吗? (例如 2 之前,1 之后),这将更容易想出一个解决方案
@Nick 请检查更新的问题。
【参考方案1】:
关联子查询可能是最简单的解决方案:
with l as (
select l.*,
cast(row_number() over (partition by user_id order by date_time) as signed) as seqnum
from log_table l
)
select l.*,
(select group_concat(l2.event_name order by l2.date_time separator '')
from l l2
where l2.user_id = l.user_id and
l2.seqnum between l.seqnum - 2 and l.seqnum + 2
) as new_event_name
from l;
如果事件名称是一个字符,可以去掉相关子查询,使用字符串操作:
with l as (
select l.*, full_concat,
cast(row_number() over (partition by user_id order by date_time) as signed) as seqnum
from log_table l join
(select user_id, group_concat(event_name order by date_time separator '') as full_concat
from log_table l
group by user_id
) ll
using (user_id)
)
select l.*, substr(full_concat, greatest(seqnum - 2, 1), least(5, seqnum + 2))
from l;
Here 是一个 dbfiddle。
【讨论】:
【参考方案2】:您可以使用 CTE 解决此问题:首先计算每个 user_id
的行号,按 date_time
排序;然后根据行号在该行的最小/最大行号范围内(从行号 - 之前到行号 + 之后)将表加入到自身中。然后你可以在 JOINed 表中 GROUP_CONCAT
event_name
字段:
SET @before := 2;
SET @after := 2;
WITH rns AS (
SELECT *,
CAST(ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY date_time) AS SIGNED) AS rn
FROM log_table
)
SELECT r1.user_id, r1.date_time, r1.event_name,
GROUP_CONCAT(r2.event_name SEPARATOR '') AS event_name_new
FROM rns r1
JOIN rns r2 ON r2.user_id = r1.user_id
AND r2.rn BETWEEN r1.rn - @before AND r1.rn + @after
GROUP BY r1.user_id, r1.date_time, r1.event_name
ORDER BY r1.user_id, r1.rn
输出(2 之前和 2 之后):
user_id date_time event_name event_name_new
001 2020-12-10 10:00:00 a abc
001 2020-12-10 10:00:01 b abcd
001 2020-12-10 10:00:02 c abcde
001 2020-12-10 10:00:20 d bcde
001 2020-12-10 10:00:40 e cde
002 2020-12-10 10:00:02 A ABC
002 2020-12-10 10:00:09 B ABCD
002 2020-12-10 10:00:10 C ABCD
002 2020-12-10 10:00:50 D BCD
Demo on db-fiddle
【讨论】:
对我来说太复杂了,让我花点时间消化一下。你能做一些解释吗?比如代码中的一些cmets。 @LernerZhang 看看db-fiddle.com/f/5dKasZPNK7SqjnXvsj7qqX/13,它显示了中间 CTE 的输出,以及如果你取消分组,你从最终查询中得到什么。这可能会有所帮助... @LernerZhang 我得走了……有什么问题请在 cmets 留言,我会尽快回复。 @LernerZhang 睡了之后我意识到可以简化查询。请查看我的编辑。 @LernerZhang 是的,COUNT(*)
不再需要,这是我编辑后不再需要的原始查询的一部分。我已将其从答案中删除。您确实需要 ORDER BY
子句中的 r1.rn
,否则行将不会按 date_time
排序。以上是关于SQL中分区上的字符串连接?的主要内容,如果未能解决你的问题,请参考以下文章