OPEN JSON с парой ключей-значений Dynami c JSON - PullRequest
1 голос
/ 06 марта 2020

у нас есть документ JSON, где номера недель являются динамическими c ключами. Мы хотим загрузить их в реляционную таблицу.

Мы можем получить реляционный набор результатов, если жестко закодировать номера недель, как указано ниже. Но это похоже на обходной подход с жестко закодированными значениями. Мы хотим сделать его динамическим c.

Есть ли способ в T SQL динамически отображать пары ключ-значение как реляционную таблицу?

DECLARE @json NVARCHAR(MAX) = N'[
  {
    "ID": "1",
    "Measure": "Current Sales",
    "2019Week12": "33",
    "2019Week13": "33",
    "2019Week14": "34"
  },
  {
    "ID": "2",
    "Measure": "Current Sales",
    "2019Week12": "",
    "2019Week13": "10",
    "2019Week14": "60"
  }]';

SELECT ID,Measure, WeekNumber, Sales
FROM
(   SELECT * FROM OPENJSON(@json)
    with 
    ( ID int '$.ID',
    Measure VARCHAR(30) '$.Measure',
    [2019Week12] INT '$."2019Week12"',
    [2019Week13] INT '$."2019Week13"',
    [2019Week14] INT '$."2019Week14"'
    )
) as p
UNPIVOT
(
Sales FOR WeekNumber IN ([2019Week12],[2019Week13],[2019Week14]) 
) as unpvt

Набор результатов, который мы получил это:

+----+---------------+------------+-------+
| ID |    Measure    | WeekNumber | Sales |
+----+---------------+------------+-------+
|  1 | Current Sales | 2019Week12 |    33 |
|  1 | Current Sales | 2019Week13 |    33 |
|  1 | Current Sales | 2019Week14 |    34 |
|  2 | Current Sales | 2019Week12 |     0 |
|  2 | Current Sales | 2019Week13 |    10 |
|  2 | Current Sales | 2019Week14 |    60 |
+----+---------------+------------+-------+

1 Ответ

1 голос
/ 06 марта 2020

Вы не указали ожидаемый результат. То, что у меня есть: вы хотите получить то же, что и выше, без необходимости буквально указывать имена. Надеюсь, я правильно понял:

SELECT   JSON_VALUE(A.[value],'$.ID') AS ID
        ,JSON_VALUE(A.[value],'$.Measure') AS Measure
        ,B.[key] AS [varName]
        ,B.[value] AS [varValue]  
        ,ROW_NUMBER() OVER(PARTITION BY JSON_VALUE(A.[value],'$.ID') ORDER BY B.[key]) RowIndex
FROM OPENJSON(@json) A
CROSS APPLY OPENJSON(A.[value]) B
WHERE b.[key] NOT IN('ID','Measure');

Результат

+----+---------------+------------+----------+----------+
| ID | Measure       | varName    | varValue | RowIndex |
+----+---------------+------------+----------+----------+
| 1  | Current Sales | 2019Week12 | 33       | 1        |
+----+---------------+------------+----------+----------+
| 1  | Current Sales | 2019Week13 | 33       | 2        |
+----+---------------+------------+----------+----------+
| 1  | Current Sales | 2019Week14 | 34       | 3        |
+----+---------------+------------+----------+----------+
| 2  | Current Sales | 2019Week12 |          | 1        |
+----+---------------+------------+----------+----------+
| 2  | Current Sales | 2019Week13 | 10       | 2        |
+----+---------------+------------+----------+----------+
| 2  | Current Sales | 2019Week14 | 60       | 3        |
+----+---------------+------------+----------+----------+

Идея вкратце:

  • Мы используем OPENJSON(), чтобы погрузиться в ваш json строка Это вернет два объекта, содержащихся в производном наборе A.
  • Теперь мы снова используем OPENJSON(), передавая A.[value], то есть сам объект json.
  • Это вернул бы все содержащиеся в нем элементы, но мы исключаем ID и Measurement в пределах WHERE.
  • Два специальных столбца, ID и Measurement, мы выбираем непосредственно из A.[value], используя JSON_VALUE() .

ОБНОВЛЕНИЕ

Одним из усовершенствований может быть следующее:

SELECT   C.ID
        ,C.varName AS [varName]
        ,TRY_CAST(LEFT(C.varName,4) AS INT) AS MeasureYear
        ,TRY_CAST(RIGHT(C.varName,2) AS INT) AS MeasureWeek
        ,C.varContent AS [varValue]  
        ,ROW_NUMBER() OVER(PARTITION BY C.ID ORDER BY C.varName) RowIndex
FROM OPENJSON(@json) A
CROSS APPLY OPENJSON(A.[value]) B
CROSS APPLY (SELECT JSON_VALUE(A.[value],'$.ID') AS ID
                   ,JSON_VALUE(A.[value],'$.Measure') AS Measure
                   ,B.[key] AS varName
                   ,B.[value] AS varContent) C
WHERE C.varName NOT IN('ID','Measure');

Идея: добавление еще одного APPLY позволяет возвращать значения как обычные столбцы . Это облегчает работу со значениями и делает этот лот более читабельным.

...