JS UDF 返回标准 SQL / BigQuery 的结构并创建两列

Posted

技术标签:

【中文标题】JS UDF 返回标准 SQL / BigQuery 的结构并创建两列【英文标题】:JS UDF that returns a struct for Standard SQL / BigQuery and creates two columns 【发布时间】:2019-04-03 17:39:14 【问题描述】:

我正在尝试使用 javascript 为 BigQuery 编写一个用户定义的函数,该函数返回一个结构并生成两列:

CREATE TEMP FUNCTION exampleFunction(exampleString STRING)
  RETURNS STRUCT<index INT64, latency INT64> LANGUAGE js AS
  LANGUAGE js AS 
"""
    var exampleStruct = 1:100, 2:200
    return exampleStruct;
""";

我的查询是这样的:

SELECT
exampleCol,
exampleFunction(stringCol) -- use SELECT AS STRUCT somewhere here? with the aliases “First” and “Second”
FROM
[SOME DATATBASE HERE]

我希望exampleFunction(stringCol) 的输出生成两列(如果包括exampleCol,则总共三列)。例如,如果exampleCol 给了我们“SOMETHING”,我想返回列:“SOMETHING”例如Col,1 表示“First”,2 表示“Second”。这是可能的吗?

我无法从 JS 函数返回 STRUCT(不确定我的语法是否关闭)并正确获取查询。对于查询,我想避免两次运行 JavaScript 函数。谢谢!

【问题讨论】:

【参考方案1】:

以下示例适用于 BigQuery 标准 SQL

#standardSQL
CREATE TEMP FUNCTION exampleFunction(exampleString STRING)
  RETURNS STRUCT<index INT64, latency INT64> 
  LANGUAGE js AS 
"""
    arr = exampleString.split(':');
    this.index = arr[0];
    this.latency = arr[1];
    return this;
""";
WITH `project.dataset.table` AS (
  SELECT 1 exampleCol, '10:100' stringCol UNION ALL
  SELECT 2, '20:200' UNION ALL
  SELECT 3, '30:456'
)
SELECT exampleCol, exampleFunction(stringCol).*
FROM `project.dataset.table`
-- ORDER BY exampleCol   

结果

Row exampleCol  index   latency  
1   1           10      100  
2   2           20      200  
3   3           30      456   

注意:如果您希望列别名为 First、Second - 您可以将 indexlatency 分别替换为 firstsecond,如下例所示

#standardSQL
CREATE TEMP FUNCTION exampleFunction(exampleString STRING)
  RETURNS STRUCT<first INT64, second INT64> 
  LANGUAGE js AS 
"""
    arr = exampleString.split(':');
    this.first = arr[0];
    this.second = arr[1];
    return this;
""";
SELECT exampleCol, exampleFunction(stringCol).*
FROM `project.dataset.table`  

或者你可以使用下面的方法

#standardSQL
CREATE TEMP FUNCTION exampleFunction(exampleString STRING)
  RETURNS STRUCT<index INT64, latency INT64> 
  LANGUAGE js AS 
"""
    arr = exampleString.split(':');
    this.index = arr[0];
    this.latency = arr[1];
    return this;
""";
SELECT exampleCol, index AS first, latency AS second   
FROM (
  SELECT exampleCol, exampleFunction(stringCol).*
  FROM `project.dataset.table`
)

在这两种情况下都有以下结果

Row exampleCol  first   second   
1   1           10      100  
2   2           20      200  
3   3           30      456  

【讨论】:

【参考方案2】:

我想补充 Mikhail Berlyant 的答案,在这种情况下效果很好,但我在稍微不同的情况下遇到了问题。

我建议使用一个新对象来完成,而不是在 JavaScript 中使用“this”来保留跨行数据。

在我的示例中,我想再返回一列,此列的值基于另一现有列的值。如果延迟字段的值低于 150,我将再添加一个名为“latencyUnder150”的列,其值为“Y”,否则将该字段留空。

#standardSQL
CREATE TEMP FUNCTION exampleFunction(exampleString STRING)
  RETURNS STRUCT<index INT64, latency INT64, latencyUnder150 STRING> 
  LANGUAGE js AS 
"""
    arr = exampleString.split(':');
    this.index = arr[0];
    this.latency = arr[1];
    if (this.latency < 150) 
        this.latencyUnder150 = 'Y'
    
    return this;
""";
WITH `project.dataset.table` AS (
  SELECT 1 exampleCol, '10:100' stringCol UNION ALL
  SELECT 2, '20:200' UNION ALL
  SELECT 3, '30:456'
)
SELECT exampleCol, exampleFunction(stringCol).*
FROM `project.dataset.table`
-- ORDER BY exampleCol   

在JS中使用“this”变量,你会得到这个答案。

| Row | exampleCol | index | latency | latencyUnder150 |
|-----|------------|-------|---------|-----------------|
| 1   | 1          | 10    | 100     | Y               |
| 2   | 2          | 20    | 200     | Y               |
| 3   | 3          | 30    | 456     | Y               |

您可以看到字段latencyUnder150 保留了第一条记录中的“Y”值。

通过稍微更改代码以使用新对象,每一行开始时都没有前一行的值。

#standardSQL
CREATE TEMP FUNCTION exampleFunction(exampleString STRING)
  RETURNS STRUCT<index INT64, latency INT64, latencyUnder150 STRING> 
  LANGUAGE js AS 
"""
    var outObj = 
    arr = exampleString.split(':');
    outObj.index = arr[0];
    outObj.latency = arr[1];
    if (outObj.latency < 150) 
        outObj.latencyUnder150 = 'Y'
    
    return outObj;
""";
WITH `project.dataset.table` AS (
  SELECT 1 exampleCol, '10:100' stringCol UNION ALL
  SELECT 2, '20:200' UNION ALL
  SELECT 3, '30:456'
)
SELECT exampleCol, exampleFunction(stringCol).*
FROM `project.dataset.table`
-- ORDER BY exampleCol   
| Row | exampleCol | index | latency | latencyUnder150 |
|-----|------------|-------|---------|-----------------|
| 1   | 1          | 10    | 100     | Y               |
| 2   | 2          | 20    | 200     | (null)          |
| 3   | 3          | 30    | 456     | (null)          |

【讨论】:

以上是关于JS UDF 返回标准 SQL / BigQuery 的结构并创建两列的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server 2008 - UDF 参数类型和返回类型

UDF 返回不同的 SQL 语句

SQL递归udf总是返回null

sql server udf,返回与输入表达式相同的类型

如何在 Bigquery Legacy SQL 中创建真正的函数

udf 在 t-sql 编程中没有正确返回数据