Clickhouse LEFT JOIN 部分匹配(或子选择)

Posted

技术标签:

【中文标题】Clickhouse LEFT JOIN 部分匹配(或子选择)【英文标题】:Clickhouse LEFT JOIN with partial match (or subselect) 【发布时间】:2020-08-25 14:57:18 【问题描述】:

实际上 CH 不支持带有部分匹配(字符串等)的左连接,所以我试图在表达式列表中使用 select 子句构建查询,但它不起作用。 或者也许有一种全新的方式(对我来说)可以做到这一点,但我只是在寻找关于如何执行此操作的线索。

错误是“缺少列:'DomainName' while processing query”

select NumberInTypes,
       DomainName,
       Url,
       (select aa.group_name
        from (select t1.id, t1.url_part, ugu.name as group_name
              from Url t1
                       any
                       left join (select id, urlgroup_id, url_id, ug.name
                                  from UrlGroupUrl t2
                                           any
                                           left join (select id, name
                                                      from UrlGroup t3
                                      ) ug on t2.urlgroup_id = ug.id
                  ) ugu on t1.id = ugu.url_id) aa where t1.Url like '%' || aa.url_part || '%'
        ) as UrlGroup,
       KeywordId,
       ResultId,
       HashedContent,
       SearchEngine,
       client_name,
       project_name,
       group_name,
       DateParsed
from PositionNew t1
         any
         left join (
    select id as KeywordId, trimBoth(keyword) as keyword, groupid, group_name, project_name, client_name
    from Keyword
             any
             left join (
        select keywordgroup_id as groupid, keyword_id as KeywordId, group_name, project_name, client_name
        from KeywordGroupKeyword
                 any
                 left join (
            select id as groupid, name as group_name, project_id, project_name, client_name
            from KeywordGroup
                     any
                     left join (
                select id as project_id, name as project_name, client_id, client_name
                from Project
                         any
                         left join (
                    select id as client_id, name as client_name from Client
                    ) client using client_id
                ) project using project_id
            ) kgroup using groupid
        ) keywordgroup using KeywordId
    ) keyword using KeywordId
where DateParsed between '2020-07-13' and '2020-08-02'
  and PositionType in (1, 3)
  and client_name like '%ClientName%'
ORDER BY ResultId,
         DomainName,
         NumberInType
LIMIT
    1 BY ResultId, DomainName;

更新: 显然,您不能在 Clickhouse 的相关子查询中使用 out 查询中的列。所以我完全没有选择,开始认为这是不可能的。

重现问题的简化示例:

第一个表包含 URL

+------------------------------------+
| Url                                |
+------------------------------------+
| https://example.com/cat/page1.html |
+------------------------------------+
| https://example.com/cat/page2.html |
+------------------------------------+
| https://example2.com/page.html     |
+------------------------------------+

第二个表包含 UrlGroups

+-----------------+-----------+
| UrlPart         | GroupName |
+-----------------+-----------+
| example.com/cat | DomainCat |
+-----------------+-----------+
| example2.com    | Domain2   |
+-----------------+-----------+

我想要实现的是:

+------------------------------------+-----------+
| Url                                | GroupName |
+------------------------------------+-----------+
| https://example.com/cat/page1.html | DomainCat |
+------------------------------------+-----------+
| https://example.com/cat/page2.html | DomainCat |
+------------------------------------+-----------+
| https://example2.com/page.html     | Domain2   |
+------------------------------------+-----------+

ALL LEFT JOIN - 不起作用,因为它需要完全匹配 SUBQUERY - 不起作用,因为您不能使用外部查询中的列来过滤其结果

【问题讨论】:

很难重现你的问题——你能提供所有使用过的表的架构吗?或重现此问题的简单示例。 @vladimir 我已经用简化的例子更新了这个问题 【参考方案1】:

让我们依靠数组操作:

WITH 
    (
        SELECT (groupArray(UrlPart), groupArray(GroupName))
        FROM 
        (
            /* Emulate 'UrlGroups' table. */
            SELECT 
                data.1 AS UrlPart,
                data.2 AS GroupName
            FROM 
            (
                SELECT arrayJoin([
                  ('example.com/cat', 'DomainCat'), 
                  ('example2.com', 'Domain2')]) AS data
            )
        )
    ) AS urls_groups
SELECT 
    Url,
    arrayElement(
      urls_groups.2, 
      multiSearchFirstIndexCaseInsensitiveUTF8(Url, urls_groups.1)) AS GroupName
FROM 
(
    /* Emulate 'Urls' table. */
    SELECT data AS Url
    FROM 
    (        
        SELECT arrayJoin([
          'https://example.com/cat/page1.html', 
          'https://example.com/cat/page2.html', 
          'https://example2.com/page.html',
          'https://example_unknown.com/page.html']) AS data          
    )
)

/*
┌─Url───────────────────────────────────┬─GroupName─┐
│ https://example.com/cat/page1.html    │ DomainCat │
│ https://example.com/cat/page2.html    │ DomainCat │
│ https://example2.com/page.html        │ Domain2   │
│ https://example_unknown.com/page.html │           │
└───────────────────────────────────────┴───────────┘
*/

您应该定义使用哪个函数 - multiSearchFirstIndexCaseInsensitiveUTF8 或 multiSearchFirstIndexCaseInsensitive。

【讨论】:

使用简化示例,但是当我尝试使用真实数据时,我得到“DB::Exception: Scalar 17022771055411946142_18430558166935724235 不存在(内部错误)(版本 20.4.5.36(官方构建)) )" 错误... 奇怪 - 我已经注释掉 WHERE CLAUSE,现在它可以工作了......@vladimir 最后,我不得不将整个查询(使用 WHERE CLAUSE,但没有那个 arrayElement 的东西)放在另一个 SELECT 中,然后我把 arrayElement 放在那里。这样就成功了。谢谢弗拉基米尔! @EdgardGomezSennovskaya 很高兴为您提供帮助。

以上是关于Clickhouse LEFT JOIN 部分匹配(或子选择)的主要内容,如果未能解决你的问题,请参考以下文章

大数据(5n)ClickHouse异于平常SQL的语法

sql中left join、right join、inner join有啥区别

关于mysql中的left join和left outer join的区别

ClickHouse 极简教程分布式下的 IN/JOIN 及 GLOBAL关键字

大数据(5p)ClickHouse异于平常SQL的语法

SQL SERVER LEFT JOIN, INNER JOIN, RIGHT JOIN