Hive 上的 CROSS APPLY SQL Server 查询

Posted

技术标签:

【中文标题】Hive 上的 CROSS APPLY SQL Server 查询【英文标题】:CROSS APPLY SQL Server query on Hive 【发布时间】:2016-11-03 10:13:26 【问题描述】:

HDP-2.5.0.0 使用 Ambari 2.4.0.1

Hive 表 ReportSetting 如下:

id int

serializedreportsetting String

“serializedreportsetting”列是源 SQL Server 数据库中的 XML 数据类型,但在 Sqoop 导入期间转换为字符串,这是它在 SQL Server 中的外观:

<ReportSettings4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Trigger>
  <Manual>true</Manual>
  </Trigger>
<StartTime>
    <Year>8</Year>
    <Month>1</Month>
    <Day>1</Day>
    <Hour>0</Hour>
    <Minute>0</Minute>
  </StartTime>
  <ReportPeriod>
    <Month>0</Month>
    <Day>0</Day>
    <Hour>0</Hour>
    <Minute>5</Minute>
  </ReportPeriod>
  <Theft>
    <DigitalInput>true</DigitalInput>
    <Can>false</Can>
  </Theft>
  <SequenceNo>0</SequenceNo>
</ReportSettings4>

在 Hive 表中:

<ReportSettings4 xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><Trigger><Manual>true</Manual></Trigger><StartTime><Year>8</Year><Month>12</Month><Day>31</Day><Hour>23</Hour><Minute>34</Minute></StartTime><ReportPeriod><Month>0</Month><Day>0</Day><Hour>4</Hour><Minute>0</Minute></ReportPeriod><Theft><DigitalInput>false</DigitalInput><Can>false</Can></Theft><SequenceNo>3</SequenceNo></ReportSettings4>

在 SQL Server 上运行良好的查询:

SELECT
r.VehicleId
,rs.value('(Trigger/Manual)[1]', 'bit') AS RS_Trigger_Manual, ,CAST(CONCAT(CASE WHEN rs.value('(StartTime/Year)[1]', 'int') < 10 THEN CONCAT('200',rs.value('(StartTime/Year)[1]', 'int')) ELSE CONCAT('20',rs.value('(StartTime/Year)[1]', 'int')) END,'-',rs.value('(StartTime/Month)[1]', 'int'),'-',rs.value('(StartTime/Day)[1]', 'int'),' ',rs.value('(StartTime/Hour)[1]', 'int'),':',rs.value('(StartTime/Minute)[1]', 'int'),':','00.000') AS datetime) AS RS_StartTime
,rs.value('(ReportPeriod/Month)[1]', 'int') AS RS_ReportPeriod_Month
,rs.value('(ReportPeriod/Day)[1]', 'int') AS RS_ReportPeriod_Day
,rs.value('(ReportPeriod/Hour)[1]', 'int') AS RS_ReportPeriod_Hour
,rs.value('(ReportPeriod/Minute)[1]', 'int') AS RS_ReportPeriod_Minute
,rs.value('(Theft/DigitalInput)[1]', 'bit') AS RS_Theft_DigitalInput
,rs.value('(Theft/Can)[1]', 'bit') AS RS_Theft_Can,rs.value('(SequenceNo)[1]', 'int') 

AS RS_SequenceNo FROM ReportSetting r
  CROSS APPLY SerializedReportSetting.nodes('/*') AS ReportSettings(rs)

我可以想到/做以下事情:

    要使用CROSS APPLY,我猜需要使用lateral view,这里我没有将serializedreportsetting 作为数组,所以explode() 不起作用。有人可以验证我的想法是否正确

    我只是尝试使用built-in xpath udf 将serializedreportsetting 中的数据作为列获取,但是,我没有得到任何记录,几个试验如下:

    select xpath(SerializedReportSetting,'/*') from ReportSetting limit 1;

    从 ReportSetting 限制 1 中选择 xpath(SerializedReportSetting,'/ReportSettings4');

    select xpath(SerializedReportSetting,'/Trigger/Manual') from ReportSetting limit 1;

**********UPDATE-1**********

我使用 regexp_replace 来处理上述挑战:

SELECT id,
  xpath_string(SerializedReportSetting,'/ReportSettings/Trigger/Manual')        AS RS_Trigger_Manual,
  xpath_string(SerializedReportSetting,'/ReportSettings/Trigger/DriveChange')   AS RS_Trigger_DriveChange
FROM
  (SELECT id,
    regexp_replace(SerializedReportSetting, 'ReportSettings+\\d','ReportSettings') AS SerializedReportSetting
  FROM reportsetting
  WHERE id IN (1701548,3185,1700231,1700232)
  ) reportsetting_regex;

【问题讨论】:

【参考方案1】:

在xpath 中,他们明确表示:

xpath() 函数总是返回一个 hive 字符串数组。如果表达式产生非文本值(例如,另一个 xml 节点),则函数将返回一个空数组

所以你可以使用:xpath(SerializedReportSetting,'/ReportSettings4/Trigg‌​er/Manual/text()') from ReportSetting limit 1;

或者更好的选择是使用xpath_boolean/xpath_int:

xpath_boolean - 如果 XPath 表达式的计算结果为 true,或者找到匹配的节点,则返回 true。

xpath_boolean(SerializedReportSetting,'/ReportSettings4/Trigg‌​er/Manual') from ReportSetting limit 1;

xpath_short、xpath_int、xpath_long 这些函数返回一个整数数值,如果没有找到匹配项,或者找到匹配项但该值不是数字,则返回零值。 支持数学运算。如果值溢出返回类型,则返回该类型的最大值。

xpath_int(SerializedReportSetting,'/ReportSettings4/ReportPeriod/Month') from ReportSetting limit 1;

【讨论】:

是否可以编写一个可以处理“正则表达式”的通用 xpath 表达式,因为 SerializedReportSetting 可以有 ReportSettings、ReportSettings1、ReportSettings2、ReportSettings3 和 ReportSettings4? 从 xpath 文档中不清楚 xpath_int(SerializedReportSetting,'/ReportSettings*/ReportPeriod/Month') 这样的东西是否可以工作 我想到了一个解决方法。您可以这样做:xpath_int(regexp_replace(SerializedReportSetting, 'ReportSettings[^ ]*','ReportSettings'),'/ReportSettings/ReportPeriod/Month').that 会在调用 xpath_int 之前从 xml 中删除数字 /ReportSettings* 不起作用,但我使用 regexp_replace 让它工作 /ReportSettings* 不起作用,但我使用 regexp_replace 让它工作,更新了我的问题(参见“Update-1”)。你能检查一下,从逻辑上讲,这是否是正确的方法吗?

以上是关于Hive 上的 CROSS APPLY SQL Server 查询的主要内容,如果未能解决你的问题,请参考以下文章

SQL 关于apply的两种形式cross apply 和 outer apply

sql MS SQL Cross和Outer Apply Sample

SQL Server outer apply 和 cross apply

SQL - CROSS APPLY 无法正常工作

如何处理 CROSS APPLY [SQL Server] 中的空行

ORA-00933: 使用 CROSS APPLY 时 SQL 命令未正确结束