Как перенести таблицу JSON, содержащую массив, в SQL? - PullRequest
6 голосов
/ 06 августа 2020

Я стажер интерфейсного разработчика. Никогда не любил бэкэнд, поэтому я очень мало знаю о SQL.

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

{
  "id": 123,
  "userId": 522,
  "effortDate": "2020-06-05",
  "data": [
    {
      "projectId": 14,
      "value": 7
    },
    {
      "projectId": 23,
      "value": 3
    }
  ],
  "updatedAt": "2020-06-08T10:13:11-03:00",
  "createdAt": "2020-06-08T10:13:11-03:00"
}

Мы переходим к базе данных на основе SQL. Итак, с моим базовым c пониманием того, как работают реляционные базы данных,

я построил эту модель:

Я построил эту модель .

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

Моя первая идея заключалась в том, чтобы смешать их, ну, вы знаете, силой, двумя разными командами SELECT. Что, я думаю, не так уж и плохо (возможно, потому, что это работает) , за исключением того, что мы имеем дело с тысячами записей, поэтому я боюсь, что все может замедлиться. Вот код, который я использовал для его анализа:

public function getAllEfforts() {
  /* Get all data from `effort` */
  $effortQuery = "SELECT id, userId, effortDate, createdAt, updatedAt
                  FROM efforts;";

  $efforts = $this->pdo
    ->query($effortQuery)
    ->fetchAll(PDO::FETCH_ASSOC);

  /* Get all data from `effort_data` */
  $effortDataQuery = "SELECT id, effortId, projectId, value
                      FROM efforts_data;";

  $effortsData = $this->pdo
    ->query($effortDataQuery)
    ->fetchAll(PDO::FETCH_ASSOC);

  /* Since, `effort` doesn't have a data field, let's define the array wished here */
  foreach($efforts as &$effortsUnity) {
    $effortsUnity['data'] = [];
  }

  /* Push the stuff from `effort_data` to the just created data array */
  foreach($effortsData as &$effortDataUnity) {
    $effortIndex = ($effortDataUnity['effortId'] - 1);

    unset($effortDataUnity['id']);
    unset($effortDataUnity['effortId']);

    array_push(
      $efforts[$effortIndex]['data'],
      $effortDataUnity
    );
  }
  
  return $efforts;
}

Мой вопрос: есть ли способ сделать это, используя только SQL? Или любой другой оптимальный способ сделать это помимо использования MongoDB или другой БД на основе JSON, что в настоящее время невозможно.

Извините, если я недостаточно ясно понял, это мой первый вопрос, я готов прояснить любые вопросы.

Большое спасибо.

Ответы [ 2 ]

4 голосов
/ 06 августа 2020

Вы можете использовать функции MySQL JSON для создания вложенной структуры JSON непосредственно из базы данных.

Рассмотрим:

SELECT 
    id, 
    userId, 
    effortDate, 
    createdAt, 
    updatedAt,
    (
        SELECT JSON_ARRAYAGG(
            JSON_OBJECT('projectId', ed.projectId, 'value', ed.value)
        )
        FROM efforts_data ed
        WHERE ed.effortId = e.id
    ) AS data
FROM efforts

Это дает вам одну строку на efforts, со столбцом под названием data, который содержит JSON полезную нагрузку, состоящую из массива объектов, поступающих в таблицу efforts_data.

Вы можете go на один шаг вперед и заполнить каждую строку один объект, если это то, что вы хотите:

SELECT JSON_OBJECT(
    'id', id, 
    'userId', userId, 
    'effortDate', effortDate, 
    'createdAt', createdAt, 
    'updatedAt', updatedAt,
    'data', (
        SELECT JSON_ARRAYAGG(
            JSON_OBJECT('projectId', ed.projectId, 'value', ed.value)
        )
        FROM efforts_data ed
        WHERE ed.effortId = e.id
    )
) res
FROM efforts

В MySQL <5.7.22 или MariaDB <10.5.0, где <code>JSON_ARRAYAGG() еще не доступен, один обходной путь - GROUP_CONCAT() и конкатенация строк. По сути, вы бы переписали это:

SELECT JSON_ARRAYAGG(
           JSON_OBJECT('projectId', ed.projectId, 'value', ed.value)
)
FROM efforts_data ed
WHERE ed.effortId = e.id

как:

SELECT CONCAT(
    '[', 
    GROUP_CONCAT(JSON_OBJECT('projectId', ed.projectId, 'value', ed.value)),
    ']'
)
FROM efforts_data ed
WHERE ed.effortId = e.id
0 голосов
/ 06 августа 2020

Мой вопрос: есть ли способ сделать это, используя только SQL?

Да, поэтому у вас есть связь между efforts id и efforts_data effortId:

SELECT e.id, e.userId, e.effortDate, e.createdAt, e.updatedAt,
       ed.id, ed.effortId, ed.projectId, ed.value
FROM efforts AS e, efforts_data AS ed
WHERE e.id = ed.effortId
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...