Как заменить только часть строки после конкретной строки, сохраняя часть строки для замены в SQL? - PullRequest
0 голосов
/ 08 ноября 2019

У меня есть таблица с именем пользователя с данными столбца. Данные содержат очень длинную строку (это JSON, вложенный в столбец данных в виде строки). Я хочу изменить все значения ключа fontSize на int во всех случаях, когда значение fontSize является строкой, но значение int должно быть сохранено. Поэтому в основном это то, что я хочу сделать:

{
  "id": 1,
  "name": "New Window",
  "resources": {
    "widgets": [
      {
        "id": 1,
        "color": "#ffffff",
        "fontSize": "5",
        "width": 150
      },
      {
        "id": 2,
        "color": "#aaaaaa",
        "fontSize": "10",
        "width": 200
      }
    ]
  }
}

изменить на:

{
  "id": 1,
  "name": "New Window",
  "resources": {
    "widgets": [
      {
        "id": 1,
        "color": "#ffffff",
        "fontSize": 5,
        "width": 150
      },
      {
        "id": 2,
        "color": "#aaaaaa",
        "fontSize": 10,
        "width": 200
      }
    ]
  }
}

во всех случаях, когда значение fontSize является строкой (в некоторых случаях это уже целое число),Значения размера шрифта различны, и они должны быть сохранены.

Я знаю логику. Я должен найти все случаи в таблице пользователя, где столбец данных, как '% "fontSize": "%", затем удалить сначала "после" fontSize ": шаблон и сначала" после числового. Как я могу это сделать? Возможно ли это в SQL? Спасибо за вашу помощь!

1 Ответ

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

Это очень уродливо и показывает случай использования, когда денормализация модели с использованием JSON является плохим выбором (если бы это было правильное отношение один-ко-многим, столбец font_size был бы определен как целое число, иу вас не возникло бы этой проблемы для начала).


Единственное, о чем я могу думать, - это разложить все элементы массива, изменить значение на целое, объединить «виджеты» обратно вмассив и использовать его для обновления таблицы.

В следующем коде предполагается, что в вашей таблице есть столбец первичного ключа с именем id.

Первый шаг - заменить значение fontSize на правильное целое число.

select t.id, jsonb_set(w.jw, '{fontSize}', to_jsonb((w.jw ->> 'fontSize')::int))
from the_table t
  cross join jsonb_array_elements(t.the_value -> 'resources' -> 'widgets') as w(jw)

jsonb_array_elements() возвращает каждый виджет как одно значение JSONB (в отдельной строке). to_jsonb((w.jw ->> 'fontSize')::int преобразует текущее значение ключа fontSize в целое число, а jsonb_set() возвращает его обратно в значение JSON.

При получении данных примера это возвращает (включая предполагаемый столбец первичного ключа)

id | jsonb_set                                                  
---+------------------------------------------------------------
42 | {"id": 1, "color": "#ffffff", "width": 150, "fontSize": 5} 
42 | {"id": 2, "color": "#aaaaaa", "width": 200, "fontSize": 10}

Теперь это можно объединить обратно в массив:

select t.id, jsonb_agg(jsonb_set(w.jw, '{fontSize}', to_jsonb((w.jw ->> 'fontSize')::int))) as new_widgets
from the_table t
  cross join jsonb_array_elements(t.the_value -> 'resources' -> 'widgets') as w(jw)
group by id

Учитывая приведенную выше дату выборки, теперь возвращается:

id | new_widgets                                                                                                               
---+---------------------------------------------------------------------------------------------------------------------------
42 | [{"id": 1, "color": "#ffffff", "width": 150, "fontSize": 5}, {"id": 2, "color": "#aaaaaa", "width": 200, "fontSize": 10}] 

Этот запрос теперь можно использоватьв качестве источника для оператора UPDATE, который снова использует jsonb_set() для изменения значения в таблице.

update the_table
  set the_value = jsonb_set(the_value, '{resources, widgets}', x.new_widgets)
from (  
  select t.id, jsonb_agg(jsonb_set(w.jw, '{fontSize}', to_jsonb((w.jw ->> 'fontSize')::int))) as new_widgets
  from the_table t
    cross join jsonb_array_elements(t.the_value -> 'resources' -> 'widgets') as w(jw)
  group by id
) as x
where x.id = the_table.id;

Полный онлайн-пример: https://rextester.com/OIFOBX92774


Если ваш столбецне определен как jsonb (что и должно быть), вам нужно привести его the_column::jsonb в вышеупомянутых запросах.

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