【问题标题】:BigQuery - Find the closest regionBigQuery - 查找最近的区域
【发布时间】:2018-02-14 06:45:11
【问题描述】:

我有两张表,对于 A 中的每个区域,我想找到 B 中最近的区域。

A:
------------------------
ID | Start | End | Color 
------------------------
 1 |  400  | 500 | White
------------------------
 1 |  10   | 20  | Red 
------------------------
 2 |   2   |  10 | Blue 
------------------------
 4 |   88  |  90 | Color 
------------------------

B:
------------------------
ID | Start | End | Name 
------------------------
 1 |  1    | 2   | XYZ1 
------------------------
 1 |  50   | 60  | XYZ4 
------------------------
 2 |  150  | 160 | ABC1 
------------------------
 2 |  50   | 60  | ABC2 
------------------------
 4 |  100  | 120 | EFG 
------------------------

RS:
---------------------------------------
ID | Start | End | Color | Closest Name
---------------------------------------
 1 |  400  | 500 | White |   XYZ4
---------------------------------------
 1 |  10   | 20  | Red   |   XYZ1
---------------------------------------
 2 |   2   |  10 | Blue  |   ABC2
---------------------------------------
 4 |   88  |  90 | Color |   EFG
---------------------------------------

目前,我首先通过连接两个表来找到最小距离:

MinDist 表:

SELECT   A.ID,   A.Start,  A.End,   
MIN(CASE  
WHEN (ABS(A.End-B.Start)>=ABS(A.Start - B.End)) 
THEN ABS(A.Start-B.End)     
ELSE ABS(A.End - B.Start) 
END) AS distance 
FROM ( Select A ... ) 
Join B On A.ID=B.ID) 
Group By A.ID,   A.Start,  A.End

然后通过再次连接表 A 和 B 重新计算距离, GlobDist 表(注意,本例中查询检索 B.Name):

SELECT   A.ID,   A.Start,  A.End,   
CASE  
WHEN (ABS(A.End-B.Start)>=ABS(A.Start - B.End)) 
THEN ABS(A.Start-B.End)     
ELSE ABS(A.End - B.Start) 
END AS distance,
B.Name 
FROM ( Select A ... ) 
Join B On A.ID=B.ID) 

最后把这两个表MinDist和GlobDist表连接在上

GlobDist.ID= MinDist.ID, 
GlobDist.Start=MinDist.Start, 
GlobDist.End= MinDist.End, 
GlobDist.distance= MinDist.distance.

我在(ID、开始、结束)上测试了 ROW_NUMBER() 和 PARTITION BY,但花费的时间要长得多。那么,解决这个问题的最快和最有效的方法是什么?如何减少重复计算?

谢谢!

【问题讨论】:

    标签: sql google-bigquery


    【解决方案1】:

    以下解决方案适用于 BigQuery 标准 SQL,如下所示简单而简短

    #standardSQL
    SELECT a_id, a_start, a_end, color,  
      ARRAY_AGG(name ORDER BY POW(ABS(a_start - b_start), 2) + POW(ABS(a_end - b_end), 2) LIMIT 1)[SAFE_OFFSET(0)] name
    FROM A JOIN B ON a_id = b_id
    GROUP BY a_id, a_start, a_end, color
    -- ORDER BY a_id
    

    您可以在问题中使用虚拟数据测试/玩上述内容

    #standardSQL
    WITH A AS (
      SELECT 1 a_id, 400 a_start, 500 a_end, 'White' color UNION ALL
      SELECT 1, 10,  20  , 'Red' UNION ALL
      SELECT 2, 2,   10, 'Blue' UNION ALL
      SELECT 4, 88,  90, 'Color'
    ), B AS (
      SELECT 1 b_id, 1 b_start, 2 b_end, 'XYZ1' name UNION ALL
      SELECT 1, 50, 60,  'XYZ4' UNION ALL
      SELECT 2, 150, 160,'ABC1' UNION ALL
      SELECT 2, 50, 60,  'ABC2' UNION ALL
      SELECT 4, 100, 120,'EFG'
    )
    SELECT a_id, a_start, a_end, color,  
      ARRAY_AGG(name ORDER BY POW(ABS(a_start - b_start), 2) + POW(ABS(a_end - b_end), 2) LIMIT 1)[SAFE_OFFSET(0)] name
    FROM A JOIN B ON a_id = b_id
    GROUP BY a_id, a_start, a_end, color
    ORDER BY a_id  
    

    结果如下

    Row a_id    a_start a_end   color   name     
    1   1       400     500     White   XYZ4     
    2   1       10      20      Red     XYZ1     
    3   2       2       10      Blue    ABC2     
    4   4       88      90      Color   EFG
    

    【讨论】:

    • 谢谢米哈伊尔!还有两个问题。 1)为什么要计算平方欧几里得距离?您不认为对于大型表,这可能会影响 MaximumBillingTier 吗? 2)如果表 B 有多个字段(例如,name1、name2、...、nameN),那么您会为每个字段调用 ARRAY_AGG 吗?
    • 1) 我使用欧几里得距离作为示例,因为我真的不知道您的数据的性质以使用更合适的数据。但你可以使用任何符合你逻辑的东西。从 Max Billing Tier 开始 - 不,我不这么认为,正如您可能知道的那样 - 不再有基于计费层的成本 - 低于 100 层的所有东西都被定价为 1 层,高于 100 的所有东西都失败了 2) cmets 格式通常不允许回答您提出的新问题 - 如果您在为下一个用例扩展提供的解决方案时遇到问题,我建议您发布新问题我们将很乐意提供帮助
    • 嘿,米哈伊尔,您是否也可以看看以下问题:stackoverflow.com/questions/48846398/…
    • 我觉得这个问题进展顺利,由 Gordon 处理:o)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-05
    • 2017-05-18
    • 1970-01-01
    • 2022-01-10
    • 1970-01-01
    相关资源
    最近更新 更多