Дополнительное условие для существующего кода синтаксического анализа XML

#sql #sql-server #xml #tsql #xquery

#sql #sql-сервер #xml #tsql #xquery

Вопрос:

Основное SequenceNum внимание уделяется столбцу / полю. В этом поле текущий код подсчитывает и нумерует цепочки последовательностей между полностью совпадающими координатами X и Y. Например, в узле в пределах Parcel ID = 1 есть одинаковые координаты с PointNum 1 и 4, 5 и 7. Таким образом, каждая последовательность между равными координатами получает одинаковое число в SequenceNum поле, например 1-1-1-1, 2-2-2, 3-3-3-3 и т.д.. (И как общее правило: первое число точек всегда равно последнему числу точек внутри каждого элемента посылки).

Цель этого вопроса — объяснить ситуацию, когда отсутствуют внутренние последовательности полного совпадения координат. Никаких дополнительных цепочек между первым PointNum = 1 и последним PointNum = 1. Хотя технически эта ситуация в поле SequenceNum эксплицируется как 1-then-null-null-null (для всех значений точек). Мне нужно расширить существующий код дополнительным условием, которое учитывало бы эту ситуацию, если она имеет место, и отметить переписать ее в значение ‘9999’ для всех нулей в SequenceNum поле.

[UPD!] И также перепишите значение «1» в самой первой записи SequenceNum как «9999».

Присутствующий код (здесь он учитывает пример с наличием внутренних SequenceNum последовательностей)

 <Parcel ID="1">
    <EntitySpatial>
        <SpatialElement>

            <SpelementUnit PointNum="1">
                <Ordinate X="100.1" Y="-100.1"/>
            </SpelementUnit>
            <SpelementUnit PointNum="2">
                <Ordinate X="100.2" Y="-100.2"/>
            </SpelementUnit>
            <SpelementUnit PointNum="3">
                <Ordinate X="100.3" Y="-100.3"/>
            </SpelementUnit>
            <SpelementUnit PointNum="4">
                <Ordinate X="100.1" Y="-100.1"/>
            </SpelementUnit>

            <SpelementUnit PointNum="5">
                <Ordinate X="100.5" Y="-100.5"/>
            </SpelementUnit>
            <SpelementUnit PointNum="6">
                <Ordinate X="100.6" Y="-100.6"/>
            </SpelementUnit>
            <SpelementUnit PointNum="7">
                <Ordinate X="100.5" Y="-100.5"/>
            </SpelementUnit>

            <SpelementUnit PointNum="8">
                <Ordinate X="100.8" Y="-100.8"/>
            </SpelementUnit>
            <SpelementUnit PointNum="9">
                <Ordinate X="100.9" Y="-100.9"/>
            </SpelementUnit>
            <SpelementUnit PointNum="10">
                <Ordinate X="100.11" Y="-100.11"/>
            </SpelementUnit>
            <SpelementUnit PointNum="1">
                <Ordinate X="100.1" Y="-100.1"/>
            </SpelementUnit>

        </SpatialElement>
    </EntitySpatial>
</Parcel>
<Parcel ID="2">
    <EntitySpatial>
        <SpatialElement>

            <SpelementUnit PointNum="1">
                <Ordinate X="200.1" Y="-200.1"/>
            </SpelementUnit>
            <SpelementUnit PointNum="2">
                <Ordinate X="200.2" Y="-200.2"/>
            </SpelementUnit>
            <SpelementUnit PointNum="3">
                <Ordinate X="200.1"" Y="-200.1"/>
            </SpelementUnit>

            <SpelementUnit PointNum="4">
                <Ordinate X="200.4" Y="-200.4"/>
            </SpelementUnit>
            <SpelementUnit PointNum="5">
                <Ordinate X="200.5" Y="-200.5"/>
            </SpelementUnit>
            <SpelementUnit PointNum="1">
                <Ordinate X="200.1" Y="-200.1"/>
            </SpelementUnit>

        </SpatialElement>
    </EntitySpatial>
</Parcel>
 

 WITH parseXml AS (
SELECT
    base.value('@ID', 'VARCHAR(1000)') AS Parcel_ID,
    outr.value('for $se in . return count(//SpatialElement[. << $se])   1', 'INTEGER') AS SpatialElement_Count,
    outr2.value('for $o in . return count(../SpelementUnit[. << $o])   1', 'INTEGER') AS Ordinate_position,
    outr2.value('@PointNum', 'INTEGER') AS PointNum,
    outr2.value('(Ordinate/@X)[1]', 'NVARCHAR(100)') AS Ordinate_X,
    outr2.value('(Ordinate/@Y)[1]', 'NVARCHAR(100)') AS Ordinate_Y
FROM @xml.nodes('Parcels/Parcel') as x(base)
    OUTER APPLY base.nodes('EntitySpatial/SpatialElement') AS B(outr)
    OUTER APPLY outr.nodes('SpelementUnit') AS C(outr2)
),
groups AS (
    SELECT
        Parcel_ID, Ordinate_X, Ordinate_Y,
        --Exclude last row in parcel_id
        MIN(CASE WHEN PointNum = Ordinate_position THEN Ordinate_position END) AS StartSequence,
        MAX(CASE WHEN PointNum = Ordinate_position THEN Ordinate_position END) AS EndSequence,
        ROW_NUMBER() OVER(PARTITION BY Parcel_ID
            ORDER BY MIN(CASE WHEN PointNum = Ordinate_position THEN Ordinate_position END)) AS SequenceNum
    FROM
        parseXml
    GROUP BY
        Parcel_ID, Ordinate_X, Ordinate_Y
    HAVING COUNT(*) > 1
)
SELECT
    ref.Parcel_ID, ref.SpatialElement_Count, ref.Ordinate_X, ref.Ordinate_Y, ref.PointNum,
    groups.SequenceNum
FROM
    parseXml ref
    LEFT JOIN groups
        ON groups.StartSequence <= ref.Ordinate_position
        AND groups.EndSequence >= ref.Ordinate_position
        AND groups.Parcel_ID = ref.Parcel_ID
ORDER BY
    ref.Parcel_ID, Ordinate_position;
 

результат этого кода

  ----------- ---------------------- ------------ ------------ -------------------------- 
| Parcel_ID | SpatialElement_Count | Ordinate_X | Ordinate_Y |  PointNum |  SequenceNum |
 ----------- ---------------------- ------------ ------------ -------------------------- 
|         1 |                    1 |      100.1 |     -100.1 |         1 |        1     |
|         1 |                    1 |      100.2 |     -100.2 |         2 |        1     |
|         1 |                    1 |      100.3 |     -100.3 |         3 |        1     |
|         1 |                    1 |      100.1 |     -100.1 |         4 |        1     |
|           |                      |            |            |           |              | 
|         1 |                    1 |      100.5 |     -100.5 |         5 |        2     |
|         1 |                    1 |      100.6 |     -100.6 |         6 |        2     |
|         1 |                    1 |      100.5 |     -100.5 |         7 |        2     |
|           |                      |            |            |           |              |  
|         1 |                    1 |      100.8 |     -100.8 |         8 |      null    |
|         1 |                    1 |      100.9 |     -100.9 |         9 |      null    |
|         1 |                    1 |     100.11 |    -100.11 |        10 |      null    |
|         1 |                    1 |      100.1 |     -100.1 |         1 |      null    |
|           |                      |            |            |           |              |  
|           |                      |            |            |           |              |  
|         2 |                    2 |      200.1 |     -200.1 |         1 |       3      |
|         2 |                    2 |      200.2 |     -200.2 |         2 |       3      |
|         2 |                    2 |      200.1 |     -200.1 |         3 |       3      |
|           |                      |            |            |           |              |  
|         2 |                    2 |      200.4 |     -200.4 |         4 |      null    |
|         2 |                    2 |      200.5 |     -200.5 |         5 |      null    |
|         2 |                    2 |      200.1 |     -200.1 |         1 |      null    |
 ----------- ---------------------- ------------ ------------ -------------------------- 
 

ЖЕЛАЕМЫЙ РЕЗУЛЬТАТ:

значение «9999» в SequenceNum столбце для всех точек, если нет внутренних цепочек последовательностей

Комментарии:

1. итак, если SequenceNum равен нулю в конечном результате, вы хотите заменить его на 9999? Если это так, то замените groups.SequenceNum на COALESCE(groups.SequenceNum, 9999) AS SequenceNum

2. @TimMylott Спасибо за этот намек… Но вопрос немного сложнее. Я уточню. Ваш вариант — это своего рода глобальное условие, которое заменяет все нули на 9999. В то время как мне нужно, чтобы все числа заменялись только при возникновении описанного случая (без дополнительных внутренних последовательностей). Это также означает, что в этом случае a) Что первая запись значения «1» в SequenceNum (которая также PointNum равна = 1) также должна быть заменена на 9999 (помимо нулей). б) если внутренние цепочки сформированы (как в примере кода) — null должны быть сохранены (не заменены).