bigquery 中的条件连接

Posted

技术标签:

【中文标题】bigquery 中的条件连接【英文标题】:conditional join in bigquery 【发布时间】:2015-07-30 08:28:06 【问题描述】:

我有两张桌子。

表 1 是一列整数。

表 2 有三列:start_integer、end_integer、data

简单的查询是将整数列与数据相连接

  integer >= start_integer AND integer <= end_integer

在许多 SQL 实现中,这可以通过左条件 JOIN ... ON BETWEEN 来完成

SELECT tbl1.integer, tbl2.data FROM tbl1
LEFT JOIN tbl2 ON tbl1.integer BETWEEN tbl2.start_integer AND 
tbl2.end_integer;

但 BigQuery 似乎只支持只有一个 = 条件的 JOIN ON。

这可以通过交叉连接来完成,但 BigQuery 抱怨我的表太大。 CROSS JOIN EACH 无效。

如何在 BigQuery 的 SQL 限制下完成这个连接任务?

下面是我的 BigQuery SQL:

SELECT tbl1.integer, tbl2.data
FROM bq:data.tbl1 
CROSS JOIN bq:data.tbl2
WHERE tbl1.integer BETWEEN tbl2.start_integer AND tbl2.end_integer;

返回错误:

错误:4.1 - 4.132:JOIN 运算符的右侧表必须是小表。如果左侧表较小,则切换表,如果两个表都大于http://goo.gl/wXqgHs 中描述的最大值,则使用 JOIN EACH。

【问题讨论】:

你的 between 需要移动到 WHERE,并使用 CROSS JOIN。根据文档,CROSS JOIN 操作可以返回大量数据。如果不起作用,请发布交叉查询。没有 EACH 交叉。 CROSS JOIN 查询和错误发布。 数据的性质是什么?可能有解决此问题的好方法,具体取决于问题的性质。 IP 地址列表和与 IP 地址范围相关的数据。 我也有这个问题。是否可以使用 UDF 将每个 ip 映射到右表中的范围? 【参考方案1】:

好消息(2016 年)! BigQuery 现在确实支持不等式连接 - 请务必取消选中“使用旧版 SQL 选项”。

查询示例:

SELECT * 
FROM (
  SELECT 1 x
) a JOIN (
  SELECT 2 y
) b
ON a.x<b.y

使用旧版 SQL:

Error: ON clause must be AND of = comparisons of one field name from each table, ...

使用标准 SQL:

1     2

文档:https://cloud.google.com/bigquery/sql-reference/enabling-standard-sql 讨论:https://code.google.com/p/google-bigquery/issues/detail?id=448#c31

【讨论】:

哦,太好了,刚刚花了一天时间试图用 R 中的数据表解决这个问题。迫不及待地想看看。【参考方案2】:

BigQuery 不支持右侧表的交叉联接。

【讨论】:

【参考方案3】:

只是添加我如何解决这个问题的概述 - 有点 hacky,但我发现这是最快的方法,可以很好地扩展。

输入表如下:


    "ip": "130.211.149.140",
    "ip_int": "2194904460",
    "ip_part1": "130",
    "ip_part2": "211",
    "ip_part3": "149",
    "ip_part4": "140",
    "num_requests": "6811"
  

查找表是这样的:


    "de_ip_key": "DE18_92.66.156.93_92.66.156.112",
    "ip_key": "92.66.156.93_92.66.156.112",
    "ip_from_int": "1547869277",
    "ip_to_int": "1547869296",
    "ip_from": "92.66.156.93",
    "ip_to": "92.66.156.112",
    "naics_code": "518210",
    "ip_from_part1": "92",
    "ip_from_part2": "66",
    "ip_from_part3": "156",
    "ip_from_part4": "93",
    "ip_to_part1": "92",
    "ip_to_part2": "66",
    "ip_to_part3": "156",
    "ip_to_part4": "112"
  

因此,使用 ip 地址的第 1 部分和第 2 部分来加入作为减少搜索空间的一种方式(我的查找表中的 from 和 to 范围往往不会像第 1 部分和第 2 部分一样宽- 如果是这样,这种方法失败了)。

select
  ip,
  ip_int,
  -- pick first info from de
  first(ip_key) as ip_key,
  first(de_ip_key) as de_ip_key,
  first(naics_code) as naics_code
from
  (
  select 
    ip as ip,
    ip_int as ip_int,
    ip_key as ip_key,
    de_ip_key as de_ip_key,
    naics_code as naics_code,
  from 
    -- join based on part 1 and 2 of ip from range
    (
    select 
      input.ip as ip,
      input.ip_int as ip_int,
      if(input.ip_int between de.ip_from_int and de.ip_to_int,de.ip_key,null) as ip_key,
      if(input.ip_int between de.ip_from_int and de.ip_to_int,de.de_ip_key,null) as de_ip_key,
      if(input.ip_int between de.ip_from_int and de.ip_to_int,de.naics_code,null) as naics_code,
    from
      [ip.lookup_input_tbl]  input
    left outer join each 
      [digital_element.data_naics_code] de
    on
      input.ip_part1=de.ip_from_part1
      and
      input.ip_part2=de.ip_from_part2
    group by 1,2,3,4,5
    ),
    -- join based on part 1 and 2 of ip to range
    (
    select 
      input.ip as ip,
      input.ip_int as ip_int,
      if(input.ip_int between de.ip_from_int and de.ip_to_int,de.ip_key,null) as ip_key,
      if(input.ip_int between de.ip_from_int and de.ip_to_int,de.de_ip_key,null) as de_ip_key,
      if(input.ip_int between de.ip_from_int and de.ip_to_int,de.naics_code,null) as naics_code,
    from
      [ip.lookup_input_tbl]  input
    left outer join each 
      [digital_element.data_naics_code] de
    on
      input.ip_part1=de.ip_to_part1
      and
      input.ip_part2=de.ip_to_part2
    group by 1,2,3,4,5
    ),
  group by 1,2,3,4,5
  -- order so null records from either join go to bottom and get left behind on the first group by
  order by ip_int,ip_key desc
  )
group by 1,2

所以它基本上会吹出数据(通过在 ip 地址的第 1 部分和第 2 部分以及 ip_from 和 ip_to 地址上进行相等连接),然后使用 if between 语句在组上减少它(这样做而不是 where 条件确保您获得正确的左外连接,因此您还可以查看您处理了哪些记录但在查找表中没有信息)。

Defo 不是最漂亮的,可能还有一两种优化它的方法,但现在对我有用,并在 10-20 秒内根据 16M 记录的查找表查找 500K 输入 IP 地址。

【讨论】:

【参考方案4】:

您是否尝试过以下查询:

SELECT tbl1.integer, tbl2.data
FROM bq:data.tbl1 
JOIN EACH bq:data.tbl2
ON tbl1.integer >= tbl2.start_integer AND tbl1.integer <= tbl2.end_integer;

【讨论】:

BigQuery 不支持 JOIN 上的不等式。 2016 年更新:新的 BigQuery SQL 确实支持 JOIN 上的不等式 - 取消选中“旧版 SQL”。 :)

以上是关于bigquery 中的条件连接的主要内容,如果未能解决你的问题,请参考以下文章

BigQuery - 使用子查询和 OR 语句连接多个条件

BigQuery:加入集群字段

BigQuery Left Outer 出现 OR 条件错误

BigQuery 脚本中的条件逻辑

使用 BigQuery 中的条件计算运行总计

BigQuery 中的多个左连接