【问题标题】:How to declare variable inside BigQuery UDF body?如何在 BigQuery UDF 正文中声明变量?
【发布时间】:2020-11-18 16:45:43
【问题描述】:

我正在尝试在 BigQuery 上创建一个带有 while 循环的 UDF 函数,但我在文档中没有看到任何专门针对这种情况的语法指南,也没有针对 UDF 正文中的变量声明。

上下文:我正在尝试构建一个函数以将标题大小写应用于字符串。

我试过了:

CREATE CREATE OR REPLACE FUNCTION mydataset.title_case(word STRING) as (
    DECLARE i INT64;
    SET i = ARRAY_LENGTH(SPLIT(word, " "));
    ...
);

但是它不喜欢 UDF 正文中的 DECLARE 或 SET。什么是正确的语法?

【问题讨论】:

    标签: google-bigquery user-defined-functions


    【解决方案1】:

    关于如何在UDF 中使用DECLARESET 的问题,您必须在代码开头声明和设置变量。然后,将它作为参数传递给 UDF,语法为;

    DECLARE x ARRAY <String>; 
    SET x = (SELECT ARRAY_LENGTH(SPLIT(word, " ")) FROM `project_id.dataset.table`);
    
    CREATE CREATE OR REPLACE FUNCTION mydataset.title_case(word STRING, x INT64) as (
    #your function...
    );
    

    请注意,变量是根据表中的值设置的,使用 SELECT。此外,您将它作为参数传递给 UDF

    此外,我能够创建一个 JavaScript UDF 来将标题大小写应用于没有 SET 和 DECLARE 的字符串。我只使用了 JS 的内置方法。您可以按如下方式使用它:

    CREATE TEMP FUNCTION title_case(str String)
    RETURNS string
    LANGUAGE js AS """
      str = str.split(' ');
      for(var i = 0; i < str.length; i++){
        str[i] = str[i].charAt(0).toUpperCase() + str[i].slice(1); 
      }
      return str.join(' ');
    """;
    
    WITH data AS (
    SELECT "jack sparrow" AS name
    )
    
    SELECT title_case(name) as new_name FROM data
    

    和输出,

    Row new_name    
    1   Jack Sparrow
    

    【讨论】:

    • 所以 JS 在 SQL 中吧。模糊地知道这一点,但还没有开始使用它。很高兴知道。因此,将变量声明放在函数之外,我担心在调用函数时不会调用它们,以防函数是永久的。我试试看。
    • 实际上我改变了主意并选择了你的,因为它确实回答了实际问题。
    • @Benjamin,您可以将 UDf 与 SQL 或 JavaScript 一起使用,这非常有帮助。我很高兴知道它有帮助。
    【解决方案2】:

    上下文:我正在尝试构建一个函数以将标题大小写应用于字符串。

    我不想直接回答这个问题,而是想先解决我认为促使这个问题被问到的原因

    根据我在 SO 上的经验,很明显,OP 经常会提出问题,以帮助他们走错方向!在许多情况下,这是一种悲伤的经历,因为你明白你没有对这样的人提供很好的帮助,而是完全相反。多次参与其中让我感到内疚,因为并不总是很清楚真正的用例是什么,所以没有太多选择可以提供帮助,而不是回答被问到的确切问题

    我认为在这种情况下 - 上面的问题很好地暗示了真正的目的/用例 - 所以正如我已经说过的,我想回答它(用例)

    在大多数情况下,您实际上并不需要执行循环 - 您应该尝试以 sql 方式实现事物 - 基于集合!

    所以,提示在下面的语句中

    上下文:我正在尝试构建一个函数以将标题大小写应用于字符串。

    title case函数的简单处理方法如下

    #standardSQL
    CREATE TEMP FUNCTION TitleCase(text STRING) AS ((
      SELECT STRING_AGG(UPPER(SUBSTR(part, 1, 1)) || SUBSTR(part, 2), ' ' ORDER BY OFFSET)
      FROM UNNEST(SPLIT(text, ' ')) part WITH OFFSET
    ));
    SELECT text, 
      TitleCase(text) transformed_text
    FROM `project.dataset.table`
    

    您可以使用虚拟数据进行测试,如下例所示

    #standardSQL
    CREATE TEMP FUNCTION TitleCase(text STRING) AS ((
      SELECT STRING_AGG(UPPER(SUBSTR(part, 1, 1)) || SUBSTR(part, 2), ' ' ORDER BY OFFSET)
      FROM UNNEST(SPLIT(text, ' ')) part WITH OFFSET
    ));
    WITH `project.dataset.table` AS (
      SELECT 1 id, "google cloud platform" AS text UNION ALL
      SELECT 2, "o'brian"
    )
    SELECT text, 
      TitleCase(text) transformed_text
    FROM `project.dataset.table`
    

    输出如下

    Row text                        transformed_text     
    1   google cloud platform       Google Cloud Platform    
    2   o'brian                     O'brian  
    

    如您所见,使用空格作为分隔符来分割文本的初始方法并不是最好的方法 - O'brian 没有得到 b 大写

    要解决这个问题 - 您可以使用以下方法

    #standardSQL
    CREATE TEMP FUNCTION TitleCase(text STRING) AS ((
      SELECT STRING_AGG(char, '' ORDER BY OFFSET)
      FROM (
        SELECT IF(REGEXP_CONTAINS(LAG(char) OVER(ORDER BY OFFSET), r'\w'), char, UPPER(char)) char, OFFSET
        FROM UNNEST(SPLIT(text, '')) char WITH OFFSET
        )
    ));
    SELECT text, 
      TitleCase(text) transformed_text
    FROM `project.dataset.table`
    

    现在,当应用于相同的虚拟数据时 - 结果更合适

    Row text                        transformed_text     
    1   google cloud platform       Google Cloud Platform    
    2   o'brian                     O'Brian    
    

    注意:以上只是一个(或者更确切地说是两个)示例,说明如何避免基于光标的无效处理,而是在一个(基于集合的)回合中完成所有操作

    【讨论】:

    • 太好了!我喜欢在这里使用 unnest 希望我考虑过 :) 我不明白的是 || 的使用在第一个示例中的 string_agg() 内部。然后第二个例子我需要再喝一杯咖啡才能尝试理解它。谢谢
    • || 是一个连接运算符 :o) - 就像第二个例子一样 - 当然 - 休息一下然后进入它 :o)
    • 好的,我现在明白了。我建议SELECT IF(REGEXP_CONTAINS(LAG(char) OVER(ORDER BY OFFSET), r'\w'), LOWER(char), UPPER(char)) 添加LOWER() 以确保它也能纠正已经大写或混乱的字符串。
    • 我正在使用您的解决方案,但我选择了另一个答案作为答案,因为它直接回答了开头的问题(即变量)。不过我更喜欢你的 SQL 方法。
    • 我很高兴您注意到这里使用 LOWER 的细微差别。 :o)
    猜你喜欢
    • 2023-02-02
    • 1970-01-01
    • 2017-12-16
    • 2011-05-05
    • 2020-04-24
    • 1970-01-01
    • 2016-05-14
    • 1970-01-01
    • 2016-11-25
    相关资源
    最近更新 更多