XML-анализ Snowflake не работает для вложенной структуры, когда существует только 1 экземпляр - PullRequest
0 голосов
/ 22 октября 2019

У нас есть промежуточная таблица с именем «портфолио» в Snowflake, в которой есть один столбец вариантов с именем «cdc_xml», в котором хранится XML-документ, загруженный Snowpipe через S3.

XML выглядит следующим образом:

<xyz>
<jmsTimestamp>1570068080385</jmsTimestamp>
<portfolio>
<id>1234</id>
<portfolioNumber>909</portfolioNumber>
<portfolioName>Hello World</portfolioName>
<master>
  <attribute fieldName="active" value="1" oldValue="0"/>
  <attribute fieldName="name" value="Hello Co" oldValue="Hello Company"/>
  <attribute fieldName="startDate" value="04/02/1988" oldValue="04/01/1988"/>
</master>
<characteristics>
  <characteristic fieldName="currency" value="JPY" oldValue="USD"/>
  <characteristic fieldName="duplicate" value="YES" oldValue="NO"/>
  <characteristic fieldName="clone" value="TRUE" oldValue="FALSE"/>
</characteristics>
</portfolio>
</xyz>

И ниже приведен код сглаживания боковых снежинок, который предположительно анализирует XML для получения всех «@fieldName» и «@value» на уровне <master><attribute> и всех «@fieldName» и «@value» на <characteristics><characteristic> уровень. Все эти данные будут получены в виде пар имя-значение.

-- flatten the characteristics nested structure to get all characteristic nvps
select 'XYZ' as source_name,
       xmlget(xmlget(src1.cdc_xml, 'portfolio'), 'id'):"$"::string as source_portfolio_id,
       xmlget(xmlget(src1.cdc_xml, 'portfolio'), 'portfolioNumber'):"$"::string as portfolio_number,
       xmlget(xmlget(src1.cdc_xml, 'portfolio'), 'portfolioName'):"$"::string as name,
       get(flt1.value, '@fieldName')::string as field_name,
       nvl(decode(get(flt1.value, '@value')::string, '', null, get(flt1.value, '@value')::string), '\b') as field_value -- deletion CDC if new value is null or empty
  from staging.portfolio src1,
       lateral flatten(xmlget(xmlget(src1.cdc_xml, 'portfolio'), 'characteristics'):"$") flt1
 union
-- flatten the master nested structure to get all attribute nvps
select 'XYZ' as source_name,
       xmlget(xmlget(src2.cdc_xml, 'portfolio'), 'id'):"$"::string as source_portfolio_id,
       xmlget(xmlget(src2.cdc_xml, 'portfolio'), 'portfolioNumber'):"$"::string as portfolio_number,
       xmlget(xmlget(src2.cdc_xml, 'portfolio'), 'portfolioName'):"$"::string as name,
       get(flt2.value, '@fieldName')::string as field_name,
       nvl(decode(get(flt2.value, '@value')::string, '', null, get(flt2.value, '@value')::string), '\b') as field_value -- deletion CDC if new value is null or empty
  from staging.portfolio src2,
       lateral flatten(xmlget(xmlget(src2.cdc_xml, 'portfolio'), 'master'):"$") flt2

Это прекрасно работает для приведенного выше примера. Но если XML выглядит следующим образом (только с 1 экземпляром вложенной структуры <master><attribute>), то этот 1 экземпляр <master><attribute> не может быть проанализирован, и все его "@fieldName" и "@value" равны NULL (вместо этогоof "startDate" и "02.11.1988").

Аналогично, если XML выглядит как тот, что находится внизу (только с 1 экземпляром вложенной структуры <characteristics><characteristic>), этот 1 экземпляр<characteristics><characteristic> не может быть проанализирован, и его "@fieldName" и "@value" имеют значение NULL (вместо "clone" и "TRUE").

Любая помощь приветствуется. Заранее спасибо!

<xyz>
<jmsTimestamp>1570068080300</jmsTimestamp>
<portfolio>
<id>9876</id>
<portfolioNumber>808</portfolioNumber>
<portfolioName>Another Example</portfolioName>
<master>
  <attribute fieldName="startDate" value="11/02/1988" oldValue="11/01/1988"/>
</master>
<characteristics>
  <characteristic fieldName="currency" value="JPY" oldValue="USD"/>
  <characteristic fieldName="duplicate" value="YES" oldValue="NO"/>
  <characteristic fieldName="clone" value="TRUE" oldValue="FALSE"/>
</characteristics>
</portfolio>
</xyz>

<xyz>
<jmsTimestamp>1570068080300</jmsTimestamp>
<portfolio>
<id>9876</id>
<portfolioNumber>808</portfolioNumber>
<portfolioName>Another Example</portfolioName>
<master>
  <attribute fieldName="active" value="0" oldValue="1"/>
  <attribute fieldName="name" value="Example Inc" oldValue="Example LLC"/>
  <attribute fieldName="startDate" value="11/02/1988" oldValue="11/01/1988"/>
</master>
<characteristics>
  <characteristic fieldName="clone" value="TRUE" oldValue="FALSE"/>
</characteristics>
</portfolio>
</xyz>

Ответы [ 2 ]

2 голосов
/ 23 октября 2019

Очень похоже на решение, только что предоставленное Симеоном Пилигримом, вы можете безоговорочно преобразовать каждый список элементов в массив, чтобы избежать попытки FLATTEN "взорвать" элемент на его атрибуты компонента (чтоэто то, что вы испытываете). Таким образом, это будет работать также:

select 'XYZ' as source_name,
       xmlget(xmlget(src1.cdc_xml, 'portfolio'), 'id'):"$"::string as source_portfolio_id,
       xmlget(xmlget(src1.cdc_xml, 'portfolio'), 'portfolioNumber'):"$"::string as portfolio_number,
       xmlget(xmlget(src1.cdc_xml, 'portfolio'), 'portfolioName'):"$"::string as name,
       get(flt1.value, '@fieldName')::string as field_name,
       nvl(decode(get(flt1.value, '@value')::string, '', null, get(flt1.value, '@value')::string), '\b') as field_value -- deletion CDC if new value is null or empty
  from staging.portfolio src1,
       lateral flatten(to_array(xmlget(xmlget(src1.cdc_xml, 'portfolio'), 'characteristics'):"$")) flt1
 union
-- flatten the master nested structure to get all attribute nvps
select 'XYZ' as source_name,
       xmlget(xmlget(src2.cdc_xml, 'portfolio'), 'id'):"$"::string as source_portfolio_id,
       xmlget(xmlget(src2.cdc_xml, 'portfolio'), 'portfolioNumber'):"$"::string as portfolio_number,
       xmlget(xmlget(src2.cdc_xml, 'portfolio'), 'portfolioName'):"$"::string as name,
       get(flt2.value, '@fieldName')::string as field_name,
       nvl(decode(get(flt2.value, '@value')::string, '', null, get(flt2.value, '@value')::string), '\b') as field_value -- deletion CDC if new value is null or empty
  from staging.portfolio src2,
       lateral flatten(to_array(xmlget(xmlget(src2.cdc_xml, 'portfolio'), 'master'):"$")) flt2```

1 голос
/ 23 октября 2019

У нас та же проблема - JavaScript с библиотекой разбирает XML на JSON, нам пришлось извлечь узел Master, а затем проверить, является ли он массивом, а если нет, привести его к массиву.

Luckyкажется, что у снежинки есть IS_ARRAY в полуструктурированных функциях

, поэтому, если IS_ARRAY и TO_ARRAY работают, как ожидалось, то это должно работать:

select source_name,
    source_portfolio_id,
    portfolio_number,
    name,
    get(flt2.value, '@fieldName')::string as field_name,
    nvl(decode(get(flt2.value, '@value')::string, '', null, get(flt2.value, '@value')::string), '\b') as field_value -- deletion CDC if new value is null or empty
from (
    select 'XYZ' as source_name,
           xmlget(portfolio, 'id'):"$"::string as source_portfolio_id,
           xmlget(portfolio, 'portfolioNumber'):"$"::string as portfolio_number,
           xmlget(portfolio, 'portfolioName'):"$"::string as name,
           xmlget(xmlget(src2.cdc_xml, 'portfolio'), 'master'):"$" AS master_raw
           IFF(IS_ARRAY(master_raw), master_raw, TO_ARRAY(master_raw)) as master
    from (
        select xmlget(src2.cdc_xml, 'portfolio') as portfolio
        from staging.portfolio src2
    )
),
lateral flatten(master) flt2
...