【问题标题】:SQL query to 'collapse' rows which are close togetherSQL查询“折叠”靠近在一起的行
【发布时间】:2020-04-02 14:54:35
【问题描述】:

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

(它看起来像一棵倒置的圣诞树......)

我们在内部需要这些数据来计算搜索次数(也称为 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

【问题讨论】:

  • 这里的大多数人希望样本表数据和预期结果为格式化文本,而不是图像。
  • 现在看起来好多了!
  • SELECT max(date), max(ev) FROM table GROUP BY cid?

标签: sql clickhouse


【解决方案1】:

一个看起来像倒置的圣诞树的日志文件;假设最后一个条目位于顶部可能是安全的,因此如果可以获取每个组的第一个记录,那么您的问题将得到解决,包括该人拼写错误然后更正的情况。这使得日期和时间与找到解决方案几乎无关。

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

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;

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

DROP PROCEDURE IF EXISTS SP_cursor1;
DELIMITER $$
CREATE PROCEDURE `SP_cursor1`()
BEGIN
    DECLARE done INT DEFAULT FALSE;
    DECLARE tempCid VARCHAR(255);

    DECLARE _date DATETIME;
    DECLARE _ts DATETIME;
    DECLARE _tid VARCHAR(255);
    DECLARE _cid VARCHAR(255);
    DECLARE _xid VARCHAR(255);
    DECLARE _xvar INT;
    DECLARE _ec VARCHAR(255);
    DECLARE _ea VARCHAR(255);
    DECLARE _ev VARCHAR(255);
    
    DECLARE cursor1 CURSOR for 
    SELECT * FROM inputTable;
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
    
    OPEN cursor1;
    SET TempCid = "";
    DROP TABLE IF EXISTS tempTbl;
    CREATE TABLE tempTbl AS 
        SELECT * FROM inputTable WHERE 1 = 0;
    read_loop: LOOP
        fetch cursor1 into _date, _ts, _tid, _cid, _xid, _xvar, _ec, _ea, _ev;
        IF done THEN
            LEAVE read_loop;
        END IF;    
        IF tempCid <> _cid then
        INSERT INTO tempTbl
            SELECT _date, _ts, _tid, _cid, _xid, _xvar, _ec, _ea, _ev;
            SET tempCid = _cid;
        END IF; 
    END LOOP;
    CLOSE cursor1;
    SELECT ev FROM tempTbl;
    DROP TABLE tempTbl;
END$$

call SP_cursor1();

希望对你有所帮助。

【讨论】:

    【解决方案2】:

    不确定基于时间的方法是否最合适,因为无论选择哪个值(例如 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 cleanr”,则查询将同时返回“vacuum cleanr”和“vacuum cleanr”。

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

    【讨论】:

    • 我认为另一个问题是当有人输入“vacuum cleanert”然后删除“t”以最终搜索“vaccum cleaner”,这不是最长的长度。
    • 好点。但是这样做之后,他们可能会使用退格键一一删除所有字母,然后开始输入“hoover”。不确定一个简单的算法如何检测到第一次搜索是针对“真空吸尘器”的?假设额外的字母错误不会经常发生,是否值得尝试使用长度方法并将其称为边缘情况? (也可能取决于对返回的统计信息做了什么,例如,在这种情况下是否可以使用人工校正......)
    • 好点。- 答案现已更新为不再使用EXISTS
    【解决方案3】:

    我认为用户不仅可以添加新字符,还可以删除字符,因此“圣诞树”规则不适用于每个最终查询。

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

    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 │
    └────────────────┴─────────┘
    */
    

    【讨论】:

      【解决方案4】:

      问题看起来很旧。但无论如何,我会分享我的想法,它会帮助别人。

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

      在那之后,我将使用 timestamp_Xcid 对数据集进行分组,并使用这些组键查询数据集,并获取每个组中最新的时间戳到和订购。

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

      希望这有帮助!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-05-03
        • 2010-10-16
        • 2016-07-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多