如何对 BigQuery 中的两个表进行条件连接?
Posted
技术标签:
【中文标题】如何对 BigQuery 中的两个表进行条件连接?【英文标题】:How to do conditional join on two tables in BigQuery? 【发布时间】:2020-07-09 19:37:19 【问题描述】:我想加入两个表,如果列不匹配,则应应用另一个条件来加入表。表应基于两个产品和级别列进行连接。如果产品匹配但级别不匹配,则匹配顺序应为低 > 中 > 高。例如,在场景 1 中,表 2 应与表 1 的 Level Med(第 2 行)匹配。如果没有“Med”级别,则将与“High”级别匹配。例如,在场景 2 中,表 2 应与表 1 的 Level High(第 1 行)匹配。如果产品不匹配或 level 为 NA,则 Value 应为 0。
这是我的问题的不同场景。表 1 为主表,表 2 可以有不同的场景。
表 1
ID Product_1 Product_2 Level Value
1 C D High 10
2 A B Med 11
3 A B High 12
4 B C Med 13
5 B C High 9
表2的不同场景
场景 1,表 2
Product_1 Product_2 Level
A B Low
预期输出
Product_1 Product_2 Level Value
A B Med 11
场景 2,表 2
Product_1 Product_2 Level
C D Low
预期输出
Product_1 Product_2 Level Value
C D High 10
场景 3,表 2
Product_1 Product_2 Level
A B Med
预期输出
Product_1 Product_2 Level Value
A B Med 11
场景 4,表 2
Product_1 Product_2 Level
M N High
预期输出
Product_1 Product_2 Level Value
M N High 0
场景 5,表 2
Product_1 Product_2 Level
A B NA
预期输出
Product_1 Product_2 Level Value
A B NA 0
我试过的代码,基本匹配,如果不匹配则返回“0”
WITH `project.dataset.Table1` AS (
SELECT 1 ID, 'C' Product_1, 'D' Product_2, 'High' Level, 10 Value UNION ALL
SELECT 2, 'A', 'B', 'Med', 11 UNION ALL
SELECT 3, 'A', 'B', 'High', 12 UNION ALL
SELECT 4, 'B', 'C', 'Med', 13 UNION ALL
SELECT 5, 'B', 'C', 'High', 9
),
`project.dataset.Table2` AS (
SELECT 2 ID, 'A' Product_1, 'B' Product_2, 'Low' Level
),
-- The Above table idea is taken from Mikhail's solution
get_values as (
select
t1.ID
,t2.Product_1
,t2.Product_2
,t2.Level
,t1.Value
FROM `project.dataset.Table1` AS t1
join `project.dataset.Table2` t2 using(Product_1,Product_2,Level)
)
select t2.ID
,t2.Product_1
,t2.Product_2
,t2.Level
,IFNULL(Value, 0) as Value
from `project.dataset.Table2` t2
left join get_values gv on t2.ID = gv.ID
谢谢
【问题讨论】:
请use text, not images/links, for text--including tables & ERDs。仅将图像用于无法表达为文本或增强文本的内容。在图像中包含图例/键和说明。请在代码问题中给出minimal reproducible example--cut & paste & runnable code,包括最小的代表性示例输入作为代码;期望和实际输出(包括逐字错误消息);标签和版本;明确的规范和解释。对于包含 DBMS 和 DDL(包括约束和索引)和输入为格式化为表的代码的 SQL。 How to Ask 请显示您可以执行的部分/相关查询并与您的目标相关。在暂停该目标时询问错误。请不要要求我们编写您的代码。 PS 以下是如何描述 DB 表的特征(这样您就可以对它们进行推理和交流):当给出业务关系(船舶)/关联或表(基本或查询结果)时,说出其中的一行说明了什么就其列值而言的业务情况。 PS 关系查询不使用“条件连接”。 CROSS/INNER JOIN 首先给出每个表中输入行的所有组合。 ON/WHERE 保留一些。你要哪个? 我只是一个想帮助你的用户。我看不到您是如何检查我的 cmets 或其链接的。例如,我们无法从您的帖子中剪切和粘贴并运行您或我们的尝试。例如,您没有说出您认为您的查询做了什么以及这与您的目标有何关系;你只是转储错误的代码。 (另外,我用粗体表示了一些东西,并说它对帮助很重要,但你还没有采取行动。我问你想要哪几行交叉连接而你没有采取行动。这些不是强制性的,我是只是向您展示如何摆脱困境。)如果您不理解某些内容并想要,请发表评论。 当我试图根据您的评论尽可能首先改进问题时,我没有检查虚拟数据上的虚拟代码,因此代码中有错误。但我提到了我的查询做了什么,这应该给出这个想法。我不明白为什么我需要在粗体线中说与列值的业务关系!或者我只是不明白你的意思。无论如何..我得到了解决方案。感谢您的 cmets 改进我的问题。 我刚刚告诉过你,你不需要遵循粗体字(“不是强制性的”),但它是基本的并且会帮助你。 (因为它直接导致相应的 SQL 代码。Is there any rule of thumb to construct SQL query from a human-readable description?)“应该给出的想法”是一个不合理的信念。 PS 请当您提供任何输入表时,请以表格形式将它们初始化代码,可以剪切和粘贴,而不仅仅是人们阅读。祝你好运。 【参考方案1】:以下是 BigQuery 标准 SQL
#standardSQL
SELECT Scenario, Product_1, Product_2, candidates.Level, candidates.Value
FROM (
SELECT Scenario, Product_1, Product_2, t2.Level,
ARRAY_AGG(
STRUCT(IF(t2.Level = 'NA', 'NA', IFNULL(t1.Level, t2.Level)) AS Level, IF(Value IS NULL OR t2.Level = 'NA', 0, Value) AS Value)
ORDER BY CASE t2.Level
WHEN 'Low' THEN CASE t1.Level WHEN 'Low' THEN 1 WHEN 'Med' THEN 2 WHEN 'High' THEN 3 END
WHEN 'Med' THEN CASE t1.Level WHEN 'Low' THEN 77 WHEN 'Med' THEN 1 WHEN 'High' THEN 2 END
WHEN 'High' THEN CASE t1.Level WHEN 'Low' THEN 77 WHEN 'Med' THEN 66 WHEN 'High' THEN 1 END
ELSE 0
END
)[OFFSET(0)] candidates
FROM `project.dataset.table2` t2
LEFT JOIN `project.dataset.table1` t1
USING(Product_1, Product_2)
GROUP BY Scenario, Product_1, Product_2, Level
)
如果应用到您的问题中的示例数据,如下例所示
#standardSQL
WITH `project.dataset.table1` AS (
SELECT 'C' Product_1, 'D' Product_2, 'High' Level, 10 Value UNION ALL
SELECT 'A', 'B', 'Med', 11 UNION ALL
SELECT 'A', 'B', 'High', 12 UNION ALL
SELECT 'B', 'C', 'Med', 13 UNION ALL
SELECT 'B', 'C', 'High', 9
),`project.dataset.table2` AS (
SELECT 1 Scenario, 'A' Product_1, 'B' Product_2, 'Low' Level UNION ALL
SELECT 2, 'C', 'D', 'Low' UNION ALL
SELECT 3, 'A', 'B', 'Med' UNION ALL
SELECT 4, 'M', 'N', 'High' UNION ALL
SELECT 5, 'A', 'B', 'NA'
)
SELECT Scenario, Product_1, Product_2, candidates.Level, candidates.Value
FROM (
SELECT Scenario, Product_1, Product_2, t2.Level,
ARRAY_AGG(
STRUCT(IF(t2.Level = 'NA', 'NA', IFNULL(t1.Level, t2.Level)) AS Level, IF(Value IS NULL OR t2.Level = 'NA', 0, Value) AS Value)
ORDER BY CASE t2.Level
WHEN 'Low' THEN CASE t1.Level WHEN 'Low' THEN 1 WHEN 'Med' THEN 2 WHEN 'High' THEN 3 END
WHEN 'Med' THEN CASE t1.Level WHEN 'Low' THEN 77 WHEN 'Med' THEN 1 WHEN 'High' THEN 2 END
WHEN 'High' THEN CASE t1.Level WHEN 'Low' THEN 77 WHEN 'Med' THEN 66 WHEN 'High' THEN 1 END
ELSE 0
END
)[OFFSET(0)] candidates
FROM `project.dataset.table2` t2
LEFT JOIN `project.dataset.table1` t1
USING(Product_1, Product_2)
GROUP BY Scenario, Product_1, Product_2, Level
)
输出是
Row Scenario Product_1 Product_2 Level Value
1 1 A B Med 11
2 2 C D High 10
3 3 A B Med 11
4 4 M N High 0
5 5 A B NA 0
我认为,以上内容主要为您提供所需的内容,但可能需要一些调整,我希望您能够做到
【讨论】:
嗨 Mikhail Berlyant,感谢您的快速回复。我会尽快检查并回复您。【参考方案2】:基本上我尝试通过使用临时表来解决上述问题。
1.(src) 我们正在为每个级别分配一个分数,这将有助于在没有相同级别的情况下获取 table1 中可用的最低级别。此外,我们正在使用完全外连接,这将有助于查找 table1 中缺少的产品 2. (final) 这个表格将给我们所有产品的产品以及它们在两张表格之间的级别匹配。 3. (min_score) 它将给出每组product_1和product_2的最低分数
with src as
(
select t1.product1 as t1p1, t1.product_2 as t1p2, t2.product_1 as t2p1, t2.product_2 as t2p2, t1.level as t1_level, t2.level as t2_level,case when t1.level='Low' then 1
when t1.level='Med' then 2
when t1.level='High' then 3
end as level_score from table1 t1 full outer join table2 t2 on(t1.product_1=t2.product_1 and t1.product_2=t2.product_2)
),
final as
(select t1p1,t1p2,t2p1,t2p2,case when t1_level=t2_level then t1_level else
'#NA#' as level from src join table1 on(product_1=t1p1 and product2=t1p2) where t1p1 is not null and t1p2 is not null
),
min_score as
(
select t1p1,t1p2,min(level_score) as level_score from src group by t1p1,t1p2
)
SELECT t2p1,t2p2,t2_level,0 as value FROM src WHERE (t1p1 IS NULL AND t1p2 IS NULL) OR (t2_level='NA')
UNION ALL
SELECT t1p1,t1p2,t1_level,value from final f join table1 tab1 on ( t1p1=product_1 and t1p2=product2 and t1_level=level) where level !='#NA#'
UNION ALL
SELECT t1p1,t2p2, CASE WHEN level_score=1 then 'Low' WHEN level_score=2 THEN 'Med' ELSE 'High' END as final_level,value from final f join table1 on(product_1=t1p1 and product2=t1p2) where f.level='#NA#' and table1.level= final_level
;
我使用了 UNION ALL,因为它将包括每一行(如果有的话,也会重复)。 3 个选择查询的输出将是-
-
第一个选择查询将给出 value=0 的所有产品,即 table1 中缺少产品或 table2 中的 level=NA。(场景 4,场景 5)
第二个查询将提供所有产品及其价值,其中产品及其级别存在于两个表中(场景 3)
如果我们在两个表中都有产品但它们的级别不匹配,它将为我提供价值。为此,我找到了 table1 中 product_1 和 product_2 组合的最低级别,然后通过比较级别得到了值。
【讨论】:
您好 Prakhar,感谢您的快速回复。以上是关于如何对 BigQuery 中的两个表进行条件连接?的主要内容,如果未能解决你的问题,请参考以下文章