簡體   English   中英

dbt 宏:如何在循環中連接多個列上的表

[英]dbt macro: How to join tables on multiple columns in a loop

我正在編寫一個 dbt model 來連接多個列上的兩個表,編譯為如下所示:

SELECT 
  A.col1,
  A.col2,
  A.col3,
FROM
   A
LEFT JOIN
   B
ON
   (A.col1 = B.col1 OR (IS_NAN(A.col1) AND IS_NAN(B.col1))
   AND (A.col2 = B.col2 OR (IS_NAN(A.col2) AND IS_NAN(B.col2))
   AND (A.col3 = B.col3 OR (IS_NAN(A.col3) AND IS_NAN(B.col3))

這個邏輯將應用於許多表對,所以我需要一個宏。 所有列的連接邏輯都是相同的,因此在 ON 子句中的列上循環將是完美的,就像這樣

SELECT 
  {% for col in all_cols %}
    A.{{ col }},
  {% endfor %}
FROM
   A
LEFT JOIN
   B
ON
    {% for col in all_cols %}
        (A.{{col}} = B.{{col}} OR (IS_NAN(A.{{col}}) AND IS_NAN(B.{{col}})),
       <-- What to put here for AND the next condition???
    {% endfor %}

遍歷列時,如何將 ON 子句中的條件與AND連接?

您的示例查詢缺少)

首先生成一個數據集Test和兩個表AB

CREATE OR REPLACE TABLE
  Test.B AS
SELECT
IF
  (RAND()>0.5,NULL,RAND()) col1,
IF
  (RAND()>0.5,NULL,RAND()) col2,
IF
  (RAND()>0.5,NULL,RAND()) col3
FROM
  UNNEST(GENERATE_ARRAY(1,100)) a

然后在同一區域運行此查詢,如果不是美國則必須手動設置。

DECLARE col_list ARRAY<STRING> ;
DECLARE col_list_A STRING ;
DECLARE col_list_B STRING ;
DECLARE col_list_on STRING ;
EXECUTE IMMEDIATE
  "Select array_agg(column_name) from Test.INFORMATION_SCHEMA.COLUMNS where TABLE_NAME='A'" INTO col_list;

EXECUTE IMMEDIATE
"Select STRING_AGG(concat('A.',cols)) FROM UNNEST(?) cols" INTO col_list_A USING col_list;

EXECUTE IMMEDIATE
"Select STRING_AGG(concat('B.',cols)) FROM UNNEST(?) cols" INTO col_list_B USING col_list;

EXECUTE IMMEDIATE
"Select STRING_AGG(concat('(A.',cols,' = B.',cols,' OR (IS_NAN(A.',cols,') AND IS_NAN(B.',cols,')) ) '),' AND ') FROM UNNEST(?) cols" INTO col_list_on USING col_list;

EXECUTE IMMEDIATE
"SELECT " || col_list_A || "," || col_list_B || " FROM Test.A LEFT JOIN Test.B ON " || col_list_on

首先DECLARE所有變量。 然后查詢表A的列名到變量col_list 使用concat構建A.col1, A.col2...列表,然后也構建B. .。 concat再次用於ON條件。

最后將所有變量放入查詢中。

我想警告說,這個最終查詢在較大的表上表現不佳。 如果這對您來說是個問題,請隨意提出另一個問題,並提供有關您目標的更多詳細信息。

可愛的方式(添加一個始終為真的謂詞,因此您可以使用AND開始每個語句):

SELECT 
  {% for col in all_cols %}
    A.{{ col }},
  {% endfor %}
FROM
   A
LEFT JOIN
   B
ON
    1=1
    {% for col in all_cols %}
        AND (A.{{col}} = B.{{col}} OR (IS_NAN(A.{{col}}) AND IS_NAN(B.{{col}})))
    {% endfor %}

不那么可愛的方法,使用loop.firstloop是 jinja 在 for 循環中設置的一個變量,它有一些方便的屬性loop.firstloop.last特別有用):

SELECT 
  {% for col in all_cols %}
    A.{{ col }},
  {% endfor %}
FROM
   A
LEFT JOIN
   B
ON
    {% for col in all_cols %}
        {% if not loop.first %}AND{% endif %} (A.{{col}} = B.{{col}} OR (IS_NAN(A.{{col}}) AND IS_NAN(B.{{col}})))
    {% endfor %}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM