如何在 BigQuery 中实现通用 Oracle DECODE 函数?

Posted

技术标签:

【中文标题】如何在 BigQuery 中实现通用 Oracle DECODE 函数?【英文标题】:How to implement generic Oracle DECODE function in BigQuery? 【发布时间】:2019-09-25 20:37:13 【问题描述】:

我正在考虑将 Oracle DECODE 函数实现为 UDF。

下面是外部功能 https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions040.htm

以下是 Oracle 中解码的外部功能和语法:

Oracle:

DECODE( <expr> , <search1> , <result1> [ , <search2> , <result2> ... ] [ , <default> ] )

SELECT product_id,
       DECODE (warehouse_id, 1, 'Southlake', 
                             2, 'San Francisco', 
                             3, 'New Jersey', 
                             4, 'Seattle',
                                'Non domestic') 
       "Location of inventory" FROM inventories;

首先,对于 BigQuery UDF SQL 或 javascript,对于 BigQuery UDF,当您定义 UDF 函数时,您需要知道您接受和输入的参数数量。当您定义 SQL UDF 函数时,您还可以接受任何类型的数组,但我不确定它是否可以工作并且 SQL UDF 是否可以使用数组执行我们想要的操作。似乎基于 Javascript UDF 文档,所有参数都被命名和键入并且预先知道。

有没有办法使用 BigQuery UDF 来实现这一点,它必须像 Oracle 解码一样是动态的,并且适合你放在它前面的任何场景,而不是知道你在解码什么的静态

【问题讨论】:

【参考方案1】:

以下是 BigQuery 标准 SQL

CREATE TEMP FUNCTION DECODE(expr ANY TYPE, map ANY TYPE, `default` ANY TYPE ) AS ((
  IFNULL((SELECT result FROM UNNEST(map) WHERE search = expr), `default`)
));  

你可以使用下面的例子来看看它是如何工作的

#standardSQL
CREATE TEMP FUNCTION DECODE(expr ANY TYPE, map ANY TYPE, `default` ANY TYPE ) AS ((
  IFNULL((SELECT result FROM UNNEST(map) WHERE search = expr), `default`)
));
WITH `project.dataset.inventories` AS (
  SELECT 1 product_id, 4 warehouse_id UNION ALL
  SELECT 2, 2 UNION ALL
  SELECT 3, 5
)
SELECT product_id, warehouse_id,
  DECODE(warehouse_id, 
    [STRUCT<search INT64, result STRING>
      (1,'Southlake'),
      (2,'San Francisco'),
      (3,'New Jersey'),
      (4,'Seattle')
    ], 'Non domestic') AS `Location_of_inventory`
FROM `project.dataset.inventories`

结果

Row product_id  warehouse_id    Location_of_inventory    
1   1           4               Seattle  
2   2           2               San Francisco    
3   3           5               Non domestic       

另一个使用例子是:

#standardSQL
CREATE TEMP FUNCTION DECODE(expr ANY TYPE, map ANY TYPE, `default` ANY TYPE ) AS ((
  IFNULL((SELECT result FROM UNNEST(map) WHERE search = expr), `default`)
));
WITH `project.dataset.inventories` AS (
  SELECT 1 product_id, 4 warehouse_id UNION ALL
  SELECT 2, 2 UNION ALL
  SELECT 3, 5
), map AS (
  SELECT 1 search, 'Southlake' result UNION ALL                               
  SELECT 2, 'San Francisco' UNION ALL                               
  SELECT 3, 'New Jersey' UNION ALL                               
  SELECT 4, 'Seattle'                           
)

SELECT product_id, warehouse_id,
  DECODE(warehouse_id, kv, 'Non domestic') AS `Location_of_inventory`
FROM `project.dataset.inventories`,
(SELECT ARRAY_AGG(STRUCT(search, result)) AS kv FROM map) arr

输出相同

更新到地址 - “对于可重用的 UDF,不必命名字段使其更接近 Oracle 的实现。”

CREATE TEMP FUNCTION DECODE(expr ANY TYPE, map ANY TYPE, `default` ANY TYPE ) AS (
  IFNULL((
    SELECT result FROM (
      SELECT NULL AS search, NULL AS result UNION ALL SELECT * FROM UNNEST(map) 
    )
    WHERE search = expr
  ), `default`)
);

所以现在 - 之前的示例可以在没有显式命名字段的情况下使用,如下例所示

#standardSQL
CREATE TEMP FUNCTION DECODE(expr ANY TYPE, map ANY TYPE, `default` ANY TYPE ) AS (
  IFNULL((
    SELECT result FROM (
      SELECT NULL AS search, NULL AS result UNION ALL SELECT * FROM UNNEST(map) 
    )
    WHERE search = expr
  ), `default`)
);
WITH `project.dataset.inventories` AS (
  SELECT 1 product_id, 4 warehouse_id UNION ALL
  SELECT 2, 2 UNION ALL
  SELECT 3, 5
)
SELECT product_id, warehouse_id,
  DECODE(warehouse_id, 
    [ (1,'Southlake'),
      (2,'San Francisco'),
      (3,'New Jersey'),
      (4,'Seattle')
    ], 'Non domestic') AS `Location_of_inventory`
FROM `project.dataset.inventories`

还是和之前一样的输出

【讨论】:

以上是关于如何在 BigQuery 中实现通用 Oracle DECODE 函数?的主要内容,如果未能解决你的问题,请参考以下文章

在 mapreduce 中实现 BigQuery UDF 作为地图的动机是啥?

如何在 Swift 中实现一个管理 UserDefaults 的键值对的通用结构?

如何在布局页面中实现通用的jquery数据表

Oracle:如何在 SQL 查询中实现“自然”排序?

如何在 Spring Boot 中实现通用 JPA 存储库 - 它可以自动装配到任何实体/类类型的 Spring 服务中

如何在 routes.js 中为生成的菜单项在通用反应 redux 样板中的边栏中实现动态路由,由 erikras