SQL查询'折叠'在一起的行

时间:2019-12-09 13:31:02

标签: sql clickhouse

我们有一个应用程序可以捕获用户进行的搜索。由于搜索的性质(我们在几个字符后提供搜索结果)以及人们打字的速度,因此每个搜索/字母都会得到一个日志条目。看起来像这样:

Search log

(看起来像是一棵颠倒的圣诞树...)

我们内部需要此数据来计算搜索次数(也称为API调用),但要向客户报告,就“半”查询进行报告并不是很好。

我正在寻找一种方法来将这些行折叠成搜索时间最长/最长时间的行。

有一个陷阱: 一个用户(cid)在一个会话中可以进行1次以上的搜索,但是如果我们查看时间戳,我们可以将其分开。

它必须类似于:

1)对相距不超过2秒的行进行分组

2)按长度(或最后一个)查询顺序以获得最终查询

3)按字词分组,以获取一个字词用于报告的频率的计数

数据为文本:

2019-12-09  2019-12-09 12:58:45 5dea585477c94502b52c43fb    92cd6cef-3ed8-4416-ac2d-cc347780b976    search  1   search  query   vacuum cleaner
2019-12-09  2019-12-09 12:58:45 5dea585477c94502b52c43fb    92cd6cef-3ed8-4416-ac2d-cc347780b976    search  1   search  query   vacuum cleane
2019-12-09  2019-12-09 12:58:44 5dea585477c94502b52c43fb    92cd6cef-3ed8-4416-ac2d-cc347780b976    search  1   search  query   vacuum clean
2019-12-09  2019-12-09 12:58:43 5dea585477c94502b52c43fb    92cd6cef-3ed8-4416-ac2d-cc347780b976    search  1   search  query   vacuum clea
2019-12-09  2019-12-09 12:58:43 5dea585477c94502b52c43fb    92cd6cef-3ed8-4416-ac2d-cc347780b976    search  1   search  query   vacuum cle
2019-12-09  2019-12-09 12:58:42 5dea585477c94502b52c43fb    92cd6cef-3ed8-4416-ac2d-cc347780b976    search  1   search  query   vacuum cl
2019-12-09  2019-12-09 12:58:41 5dea585477c94502b52c43fb    92cd6cef-3ed8-4416-ac2d-cc347780b976    search  1   search  query   vacuum c
2019-12-09  2019-12-09 12:58:40 5dea585477c94502b52c43fb    92cd6cef-3ed8-4416-ac2d-cc347780b976    search  1   search  query   vacuum
2019-12-09  2019-12-09 12:58:39 5dea585477c94502b52c43fb    92cd6cef-3ed8-4416-ac2d-cc347780b976    search  1   search  query   vacuu
2019-12-09  2019-12-09 12:58:38 5dea585477c94502b52c43fb    92cd6cef-3ed8-4416-ac2d-cc347780b976    search  1   search  query   vacu
2019-12-09  2019-12-09 12:58:37 5dea585477c94502b52c43fb    92cd6cef-3ed8-4416-ac2d-cc347780b976    search  1   search  query   vac
2019-12-09  2019-12-09 12:58:15 5dea585477c94502b52c43fb    9b41fb1d-59d2-4a12-8974-b2261b2fe484    search  0   search  query   blue widget
2019-12-09  2019-12-09 12:58:14 5dea585477c94502b52c43fb    9b41fb1d-59d2-4a12-8974-b2261b2fe484    search  0   search  query   blue widge
2019-12-09  2019-12-09 12:58:13 5dea585477c94502b52c43fb    9b41fb1d-59d2-4a12-8974-b2261b2fe484    search  0   search  query   blue widg
2019-12-09  2019-12-09 12:58:12 5dea585477c94502b52c43fb    9b41fb1d-59d2-4a12-8974-b2261b2fe484    search  0   search  query   blue wid
2019-12-09  2019-12-09 12:58:12 5dea585477c94502b52c43fb    9b41fb1d-59d2-4a12-8974-b2261b2fe484    search  0   search  query   blue wi
2019-12-09  2019-12-09 12:58:11 5dea585477c94502b52c43fb    9b41fb1d-59d2-4a12-8974-b2261b2fe484    search  0   search  query   blue w
2019-12-09  2019-12-09 12:58:10 5dea585477c94502b52c43fb    9b41fb1d-59d2-4a12-8974-b2261b2fe484    search  0   search  query   blue
2019-12-09  2019-12-09 12:58:09 5dea585477c94502b52c43fb    9b41fb1d-59d2-4a12-8974-b2261b2fe484    search  0   search  query   blu
2019-12-09  2019-12-09 12:57:38 5dea585477c94502b52c43fb    f96305d9-590b-4a10-95a2-2d49a9fc63a3    search  1   search  query   widget
2019-12-09  2019-12-09 12:57:37 5dea585477c94502b52c43fb    f96305d9-590b-4a10-95a2-2d49a9fc63a3    search  1   search  query   widge
2019-12-09  2019-12-09 12:57:36 5dea585477c94502b52c43fb    f96305d9-590b-4a10-95a2-2d49a9fc63a3    search  1   search  query   widg
2019-12-09  2019-12-09 12:57:35 5dea585477c94502b52c43fb    f96305d9-590b-4a10-95a2-2d49a9fc63a3    search  1   search  query   wid

预期结果:

vacuum cleaner  1
blue widget     1
widget          1

4 个答案:

答案 0 :(得分:1)

不确定采用基于时间的方法是否最合适,因为无论选择哪个值(例如2秒),用户在键入搜索字词时可能会暂停更长的时间。相反,建议您查找那些本身不是较长的搜索词的子字符串的字符串,该子词稍后在同一会话中输入。这种简单的方法可以通过标准SQL来实现:

SELECT ev,
       COUNT(ev) AS ev_count
FROM tbl t1
WHERE
  (SELECT COUNT(*)
   FROM tbl t2
   WHERE t2.cid = t1.cid
     AND t2.date <= t1.date
     AND t1.ev <> t2.ev
     AND t2.ev LIKE CONCAT(t1.ev, '%')) = 0
GROUP BY ev;

一个限制是,例如,如果用户在删除“ r”并正确键入之前错误地键入了“ vacuum cleaner”,则查询将同时返回“ vacuum cleanr”和“ vacuum cleaner”。

See demo here (用MySQL编写,但ClickHouse应该相似)

答案 1 :(得分:1)

一个看起来像颠倒的圣诞树的日志文件;可以假设最后一个条目在最前面是安全的,因此,如果可以抓住每个组的第一条记录,那么您的问题将得到解决,包括该人拼错了某些东西然后改正的情况。这使得日期和时间与寻找解决方案几乎无关。

假设您的字段“ cid”代表一个搜索,那么这是一个基于您的数据的解决方案,可以准确地产生您的预期结果。输入数据应位于创建为以下内容的名为“ inputTable”的表中:

[resizeImage][3]

在不同的情况下运行以下命令,以查找可能无法正常工作且需要微调的特殊情况。

CREATE TABLE `inputTable` (
  `date` datetime DEFAULT NULL,
  `ts` datetime DEFAULT NULL,
  `tid` varchar(255) DEFAULT NULL,
  `cid` varchar(255) DEFAULT NULL,
  `xid` varchar(255) DEFAULT NULL,
  `xvar` int(11) DEFAULT NULL,
  `ec` varchar(255) DEFAULT NULL,
  `ea` varchar(255) DEFAULT NULL,
  `ev` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

希望这对您有所帮助。

答案 2 :(得分:0)

我想用户不仅可以添加新字符,还可以删除新字符,因此“ xmas tree”规则不适用于每个最终查询。

此查询返回会话中最新的搜索输入(最终查询),可能不是会话中最长的搜索输入。

SELECT search_input, count()
FROM (
  SELECT 
    /* create group of pairs (input_seconds, input_text). */
    groupArray((toInt32(ts), ev)) inputs,
    /* define the end of each session. */
    arrayMap((x, index) -> index = 1 ? 1 : (inputs[index - 1].1 - x.1 > 2 /* 2 is max delay between inputs */ ? index : 0), inputs, arrayEnumerate(inputs)) session_end_points,
    /* take the latest input in each session. */
    arrayMap(x -> inputs[x].2, arrayFilter(x -> x > 0, session_end_points)) search_inputs,
    arrayJoin(search_inputs) search_input
  FROM (
    /* test data, sorted by DESCending */
    SELECT toDateTime(data.1) ts, data.2 cid, data.3 ev
    FROM (
      SELECT arrayJoin([
      ('2019-12-09 12:58:55', '92cd6cef-3ed8-4416-ac2d-cc347780b976', 'X'),    

      ('2019-12-09 12:58:45', '92cd6cef-3ed8-4416-ac2d-cc347780b976', 'vacuum cleaner'),
      ('2019-12-09 12:58:45', '92cd6cef-3ed8-4416-ac2d-cc347780b976', 'vacuum cleane'),
      ('2019-12-09 12:58:44', '92cd6cef-3ed8-4416-ac2d-cc347780b976', 'vacuum clean'),
      ('2019-12-09 12:58:43', '92cd6cef-3ed8-4416-ac2d-cc347780b976', 'vacuum clea'),
      ('2019-12-09 12:58:43', '92cd6cef-3ed8-4416-ac2d-cc347780b976', 'vacuum cle'),
      ('2019-12-09 12:58:42', '92cd6cef-3ed8-4416-ac2d-cc347780b976', 'vacuum cl'),
      ('2019-12-09 12:58:41', '92cd6cef-3ed8-4416-ac2d-cc347780b976', 'vacuum c'),
      ('2019-12-09 12:58:40', '92cd6cef-3ed8-4416-ac2d-cc347780b976', 'vacuum'),
      ('2019-12-09 12:58:39', '92cd6cef-3ed8-4416-ac2d-cc347780b976', 'vacuu'),
      ('2019-12-09 12:58:38', '92cd6cef-3ed8-4416-ac2d-cc347780b976', 'vacu'),
      ('2019-12-09 12:58:37', '92cd6cef-3ed8-4416-ac2d-cc347780b976', 'vac'),

      ('2019-12-09 12:58:15', '9b41fb1d-59d2-4a12-8974-b2261b2fe484', 'blue widget'),
      ('2019-12-09 12:58:14', '9b41fb1d-59d2-4a12-8974-b2261b2fe484', 'blue widge'),
      ('2019-12-09 12:58:13', '9b41fb1d-59d2-4a12-8974-b2261b2fe484', 'blue widg'),
      ('2019-12-09 12:58:12', '9b41fb1d-59d2-4a12-8974-b2261b2fe484', 'blue wid'),
      ('2019-12-09 12:58:12', '9b41fb1d-59d2-4a12-8974-b2261b2fe484', 'blue wi'),
      ('2019-12-09 12:58:11', '9b41fb1d-59d2-4a12-8974-b2261b2fe484', 'blue w'),
      ('2019-12-09 12:58:10', '9b41fb1d-59d2-4a12-8974-b2261b2fe484', 'blue'),
      ('2019-12-09 12:58:09', '9b41fb1d-59d2-4a12-8974-b2261b2fe484', 'blu'),

      ('2019-12-09 12:57:38', 'f96305d9-590b-4a10-95a2-2d49a9fc63a3', 'widget'),
      ('2019-12-09 12:57:37', 'f96305d9-590b-4a10-95a2-2d49a9fc63a3', 'widge'),
      ('2019-12-09 12:57:36', 'f96305d9-590b-4a10-95a2-2d49a9fc63a3', 'widg'),
      ('2019-12-09 12:57:35', 'f96305d9-590b-4a10-95a2-2d49a9fc63a3', 'wid'),

      ('2019-12-09 12:58:34', '92cd6cef-3ed8-4416-ac2d-cc347780b976', 'vaX'),  

      ('2019-12-09 12:58:30', '92cd6cef-3ed8-4416-ac2d-cc347780b976', 'vacuum clean'),
      ('2019-12-09 12:58:28', '92cd6cef-3ed8-4416-ac2d-cc347780b976', 'vacuum cleane'),
      ('2019-12-09 12:58:26', '92cd6cef-3ed8-4416-ac2d-cc347780b976', 'vacuum clean'),
      ('2019-12-09 12:58:24', '92cd6cef-3ed8-4416-ac2d-cc347780b976', 'vacuum clea'),
      ('2019-12-09 12:58:22', '92cd6cef-3ed8-4416-ac2d-cc347780b976', 'vacuum cle'),

      ('2019-12-09 12:58:15', '92cd6cef-3ed8-4416-ac2d-cc347780b976', 'vac'),
      ('2019-12-09 12:58:14', '92cd6cef-3ed8-4416-ac2d-cc347780b976', 'vacu'),
      ('2019-12-09 12:58:13', '92cd6cef-3ed8-4416-ac2d-cc347780b976', 'vac'),

      ('2019-12-09 12:57:34', '92cd6cef-3ed8-4416-ac2d-cc347780b976', 'vaX')]) data)
    ORDER BY ts DESC)
  GROUP BY cid)
GROUP BY search_input;

/* Result:
┌─search_input───┬─count()─┐
│ widget         │       1 │
│ vacuum cleaner │       1 │
│ blue widget    │       1 │
│ vac            │       1 │
│ vaX            │       2 │
│ X              │       1 │
│ vacuum clean   │       1 │
└────────────────┴─────────┘
*/

答案 3 :(得分:0)

问题看起来很老。但是无论如何,我会在这方面分享我的想法,这会帮助某人。

查看日志文件,我发现它缺少用于选择每个客户的最新搜索的 strong组密钥。我从日志中注意到的是,每个客户的每次搜索都在 1分钟内完成。考虑到这一事实,我要做的是获取每个日志条目的timestamp列,在数据集上创建一个新列(假设 timestamp_X )并将其格式化为“ yyyy-MM-dd HH:mm”(因此秒数将被截断或设为00)。所以我有一个强大的分组键集( timestamp_X cid )。

之后,我要使用 timestamp_X cid 将数据集分组,并使用这些组键查询数据集,并通过和获取时间戳上每个分组的最新信息排序。

在T-SQL中,这可以通过WITH语句(公用表表达式)来实现。不确定Clickhouse中的相​​似之处。但是,我确信上述逻辑可以通过任何SQL语言实现。

希望这对您有所帮助!