T-SQL `FOR JSON` в столбце с одним разделителем - PullRequest
4 голосов
/ 08 ноября 2019

У меня есть один столбец в SQL Server 2008, запрашиваемый через SQL Server 2017 (нет поддержки OPEN_JSON в режиме совместимости, только FOR JSON), который имеет значения, подобные следующим.

A.1
A.2
B.1
AB.12

И я пытаюсь использовать FOR JSON AUTO для возврата объектов, подобных следующему.

{
   "A": {
    1: true,
    2: true
   }
}

ИЛИ

{
   "ID": {
      "A": [1,2],
      "B": [1]
   }
}

Я понял, что моя лучшая ставка для разделения значений:вероятно PARSENAME(ID,1), но я не уверен, как использовать это в качестве имени ключа в FOR JSON. Нечто подобное.

SELECT TOP 100 PARSENAME(ID,2) as PARSENAME(ID,1)
FROM [SERVER1].[DB1].[SCHEMA1].[TABLE1] a
FOR JSON PATH


ОБНОВЛЕНИЕ

Поскольку я обнаружил, что PARSENAME идет в обратном порядке, я получил следующий запрос, чтобы получить результатыв правильном порядке и разделены на столбцы, хотя я все еще не уверен, как получить JSON из этого.

SELECT
CASE 
      WHEN PARSENAME(ID,3) IS NOT NULL THEN PARSENAME(ID,3)
      WHEN PARSENAME(ID,2) IS NOT NULL THEN PARSENAME(ID,2)
      WHEN PARSENAME(ID,1) IS NOT NULL THEN PARSENAME(ID,1)
      ELSE NULL
END AS a,
CASE 
      WHEN PARSENAME(ID,3) IS NOT NULL THEN PARSENAME(ID,2)
      WHEN PARSENAME(ID,2) IS NOT NULL THEN PARSENAME(ID,1)
      ELSE NULL
END AS b,
CASE 
      WHEN PARSENAME(ID,3) IS NOT NULL THEN PARSENAME(ID,1)
      ELSE NULL
END AS c
FROM [SERVER1].[DB1].[SCHEMA1].[TABLE1]

Just results in 
a  |  b  |  c
--------------
A  |  1  | null
A  |  2  | null
B  |  1  | null
AB |  12 | null

Ответы [ 2 ]

1 голос
/ 08 ноября 2019

Если я правильно понимаю ваш вопрос, следующий подход может помочь вам найти подходящее решение вашей проблемы. Я не думаю, что вы можете сгенерировать вывод JSON с переменными ключами, используя только FOR JSON PATH или FOR JSON AUTO, но если вы используете SQL Server 2017, вы можете попытаться сгенерировать выходной результат, используя комбинацию между JSON_MODIFY(), FOR JSON PATH и манипуляции со строками:

Таблица:

CREATE TABLE Data (
   ID nvarchar(50)
)
INSERT INTO Data 
   (ID)
VALUES
  ('A.1'),
  ('A.2'),
  ('B.1'),
  ('AB.12')

Оператор:

DECLARE @json nvarchar(max) = N'{}'
;WITH ParsedCTE AS (
   SELECT 
      LEFT(ID, CHARINDEX('.', ID) - 1) AS a,
      TRY_CONVERT(int, RIGHT(ID, LEN(ID) - CHARINDEX('.', ID))) AS b
   FROM Data      
)
SELECT @json = JSON_MODIFY(@json, CONCAT('append $.', a), b)   
FROM ParsedCTE

SELECT JSON_QUERY(@json) AS ID
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER

Вывод:

{"ID":{"A":[1,2],"B":[1],"AB":[12]}}
0 голосов
/ 08 ноября 2019

Выбранный ответ действительно отвечает на тот вопрос, который я изначально задавал, поэтому он все еще правильный. Но вот что я нашел, используя это. Во-первых, если у вас есть числовые значения, вам нужно будет включать кавычки с обеих сторон в выражении concat. Во-вторых, чтобы это работало, мне пришлось включить параметр lax в JSON_MODIFY, чтобы он игнорировал несуществующие пути. И это сценарий, который я использовал для преобразования столбца с тремя значениями во вложенные объекты:

DECLARE @json nvarchar(max) = N'{}'
;WITH ParsedCTE AS (
   SELECT
      CASE WHEN CHARINDEX('.', ID) = 0
            THEN ID
            ELSE LEFT(ID, CHARINDEX('.', ID) - 1)
            END
      AS a
   FROM [SERVER].[DATABASE].[SCHEMA].[TABLE]
)

SELECT @json = JSON_MODIFY(@json, CONCAT('lax $."', a, '"'), JSON_QUERY(N'{}'))
FROM ParsedCTE

;WITH ParsedCTE AS (
   SELECT
      CASE WHEN CHARINDEX('.', ID) = 0
            THEN ID
            ELSE LEFT(ID, CHARINDEX('.', ID) - 1)
            END
      AS a,
      CASE WHEN CHARINDEX('.', ID) = 0
                  THEN null
            WHEN CHARINDEX('.', ID, CHARINDEX('.', ID) + 1) = 0
                  THEN RIGHT(ID, LEN(ID) - CHARINDEX('.', ID))
            ELSE SUBSTRING(ID, CHARINDEX('.', ID) + 1, LEN(ID) - CHARINDEX('.', ID, CHARINDEX('.', ID) + 1))
            END
      AS b
   FROM [SERVER].[DATABASE].[SCHEMA].[TABLE]
   WHERE 
      CASE WHEN CHARINDEX('.', ID) = 0
                  THEN null
            WHEN CHARINDEX('.', ID, CHARINDEX('.', ID) + 1) = 0
                  THEN RIGHT(ID, LEN(ID) - CHARINDEX('.', ID))
            ELSE SUBSTRING(ID, CHARINDEX('.', ID) + 1, LEN(ID) - CHARINDEX('.', ID, CHARINDEX('.', ID) + 1))
            END IS NOT NULL
)

SELECT @json = JSON_MODIFY(@json, CONCAT('lax $."', a, '"."', b, '"'), JSON_QUERY(N'{}'))
FROM ParsedCTE

;WITH ParsedCTE AS (
   SELECT
      CASE WHEN CHARINDEX('.', ID) = 0
            THEN ID
            ELSE LEFT(ID, CHARINDEX('.', ID) - 1)
            END
      AS a,
      CASE WHEN CHARINDEX('.', ID) = 0
                  THEN null
            WHEN CHARINDEX('.', ID, CHARINDEX('.', ID) + 1) = 0
                  THEN RIGHT(ID, LEN(ID) - CHARINDEX('.', ID))
            ELSE SUBSTRING(ID, CHARINDEX('.', ID) + 1, LEN(ID) - CHARINDEX('.', ID, CHARINDEX('.', ID) + 1))
            END
      AS b,
      CASE WHEN CHARINDEX('.', ID) = 0
                  THEN null
            WHEN CHARINDEX('.', ID, CHARINDEX('.', ID) + 1) = 0
                  THEN null
            ELSE RIGHT(ID, LEN(ID) - CHARINDEX('.', ID, CHARINDEX('.', ID) + 1))
            END
       AS c
   FROM [SERVER].[DATABASE].[SCHEMA].[TABLE]
   WHERE 
      CASE WHEN CHARINDEX('.', ID) = 0
                  THEN null
            WHEN CHARINDEX('.', ID, CHARINDEX('.', ID) + 1) = 0
                  THEN null
            ELSE RIGHT(ID, LEN(ID) - CHARINDEX('.', ID, CHARINDEX('.', ID) + 1))
            END IS NOT NULL
)

SELECT @json = JSON_MODIFY(@json, CONCAT('lax $."', a, '"."', b, '"."', c, '"'), 'true')
FROM ParsedCTE

SELECT JSON_QUERY(@json) AS ID
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER

Я пытался сделать все это в одном рекурсивном выражении CTE, но это всегда перезаписывает объекты, так какэто не создало экземпляры родителей как способных к путям объектов. Поэтому, чтобы достичь этого, мне пришлось заново модифицировать JSON для каждого уровня, который я хотел определить.

К сожалению, это привело к времени выполнения ~ 4s . Так что это определенно не подходит для данных в реальном времени, сейчас я думаю о реализации кэша на моем внутреннем сервере, чтобы уменьшить это влияние.


Другое обновление

Я пошел дальше и вернулся к чему-то более похожему на сценарий исходного ответа, поскольку у него более короткое (но все же немаловажное) время выполнения, и я понял, что оно не стоит всех усилий, когда у меня только ограниченное числопредметов, которые имеют три значения. Я просто собираюсь, чтобы второе и третье поля всегда были одной строкой. Но я надеюсь, что кто-то может извлечь выгоду из сценария.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...