Повторить вложенный массив JSON из связанных таблиц базы данных с помощью одного запроса?

У меня есть две таблицы базы данных, содержащие информацию о земельных контрактах. Они связаны с land_contract_annual_price.land_contract_id -> land_contract.land_contract_id.

Таблица 'land_contract'Повторить вложенный массив JSON из связанных таблиц базы данных с помощью одного запроса?

Таблица 'land_contract_annual_price'Повторить вложенный массив JSON из связанных таблиц базы данных с помощью одного запроса?

Если земельный контракт имеет значение «Rörligt pris» в поле land_contract_price_type, в таблице есть соответствующие значения. land_contract_annual_price. На данный момент я делаю два запроса, по одному к каждой таблице. Затем я объединяю результаты и представляю земельный контракт в виде вложенного массива JSON, например:

Версия 1

[  
 {  
  "land_contract_id":118,
  "land_contract_name":"Avtalsnamn",
  "location_id":71,
  "land_contract_link":"",
  "land_contract_notes":"",
  "land_owner_id":2,
  "land_contract_start_date":"2019-07-25",
  "land_contract_end_date":"2023-07-25",
  "land_contract_terminated":"false",
  "land_contract_payment_interval":"Halv\u00e5rsvis",
  "land_contract_price_type":"R\u00f6rligt \u00e5rspris",
  "land_contract_fixed_annual_price":null,
  "land_contract_annual_prices":[  
    {"year":1, "price":873.00},
    {"year":2, "price":77289.00},
    {"year":3, "price":8.00},
    {"year":4, "price":0.00},
    {"year":5, "price":8729.00}
  ]
 }
]

Если в земельном контракте в поле land_contract_price_type указано значение «Быстрая цена», в таблице нет связанных значений land_contract_annual_price. В этом случае я представляю договор на землю так (без лишнего массива в конце):

Версия 2

[
 {
  "land_contract_id":13,
  "land_contract_name":null,
  "location_id":null,
  "land_contract_link":"https://www.something.com/preview/Sl%C3%A4pvdam%20Edda/Kddal/Bddkta/Besika%20Markavtal%20%20Halmstad%202016-03-08.pdf?role=personal",
  "land_contract_notes":"",
  "land_owner_id":null,
  "land_contract_start_date":"2016-03-08",
  "land_contract_end_date":"2026-03-08",
  "land_contract_terminated":"true",
  "land_contract_payment_interval":"\u00c5rsvis",
  "land_contract_price_type":"Fast \u00e5rspris",
  "land_contract_fixed_annual_price":"6000.00"
 }
]

Чего я не подумал, так это того, что это решение плохо, когда я получаю ВСЕ земельные контракты. Если я собираюсь делать второй запрос к другой таблице всякий раз, когда в земельном контракте есть значение «Rörligt pris» в поле land_contract_price_type, я буду делать сотни дополнительных запросов.

Есть ли способ создать вложенный массив JSON с одним (1) запросом, когда земельный контракт имеет значение «Rörligt pris» в поле land_contract_price_type?

Спасибо!

Ниже мой текущий код.

function read($pdo, $Id = null, $ResponseMessage = null) {

    $params = [];
    $array = [];

    $sql = "SELECT  lc.Id, lc.Name, lc.LocationId, l.Name AS LocationName, lc.Notes, lc.LandOwnerId, lo.Name AS LandOwnerName, lc.StartDate, lc.EndDate, lc.IsTerminated, lc.PaymentInterval, lc.PriceType, lc.FixedAnnualPrice, lc.Link, lc.Created, lc.Updated, lcap.AnnualPriceYear AS Year, lcap.AnnualPriceAmount AS Amount
            FROM LandContract lc
            LEFT JOIN Location l ON l.Id = lc.LocationId
            LEFT JOIN LandOwner lo ON lo.Id = lc.LandOwnerId
            LEFT JOIN LandContractAnnualPrice lcap ON lcap.LandContractId = lc.Id  
            ORDER BY lc.Id  DESC, lcap.AnnualPriceYear DESC
            ";
    if ($Id) {
        $sql .= 'WHERE lc.Id = ?';
        $params[] = $Id;
    }

    echo $sql;

    $stmt = $pdo->prepare($sql);
    $stmt->execute($params);
    while ($row = $stmt->fetch()) {
        // Fields we want to extract from the select statement into the array 
        $select_fields = ['Id', 'Name', 'LocationId', 'LocationName', 'Link', 'Notes', 'LandOwnerId', 'LandOwnerName',
                            'StartDate', 'EndDate', 'IsTerminated', 'PaymentInterval', 
                            'PriceType', 'FixedAnnualPrice ', 'Created', 'Updated'];

        if (!isset($array[$row['Id']])) {
            // initialize the subarray if it has not been set already 
            $array[$row['Id']] = array_intersect_key($row, array_flip($select_fields));

            if ($row['Year'] != null) {
                $array[$row['Id']]['AnnualPrices'] = [];
            } else {
                $array[$row['Id']]['AnnualPrice'] = $row['FixedAnnualPrice'];
            }
        }

        if ($row['Year'] != null) {
            $array[$row['Id']]['AnnualPrices'][] = ['Year' => $row['Year'], 'Amount' => $row['Amount']];
        }

    }

    if (empty($array)) {
        $ResponseMessage = new ResponseMessage();
        $ResponseMessage->Status = 'Error';
        $ResponseMessage->Message = 'No results';
        echo json_encode($ResponseMessage, JSON_UNESCAPED_UNICODE);
        exit;
    }

    $Response = array();

    if ($ResponseMessage) {
        $Response['Status'] = $ResponseMessage->Status;
        $Response['Message'] = $ResponseMessage->Message;
    }

    $Response['LandContracts'] = array_values($array);

    echo json_encode($Response, JSON_UNESCAPED_UNICODE);

    $stmt = null;
}

Кажется, вам было бы полезно реализовать SQL присоединяется. Предполагая, что вы используете MySQL, вы можете использовать JSON-функции в своем запросе.

Ro Achterberg 25.07.2019 22:14

@RoAchterberg Спасибо за ваш ответ. Раньше у меня был запрос с LEFT JOIN, но я не знал, смогу ли я получить эту структуру JSON и как. Какие-либо предложения?

Rawland Hustle 25.07.2019 22:58

Я предлагаю вам взглянуть на различные функции JSON_*, предоставляемые MySQL, для получения вашего результата в виде представления данных столбца в кодировке JSON.

Ro Achterberg 25.07.2019 23:01

@RoAchterberg Функция JSON не работает. Оказывается, на моем хосте работает Maria DB. Я действительно думал, что это MySQL.

Rawland Hustle 26.07.2019 11:02

@RoAchterberg Есть ли способ создать JSON, который я хочу, с помощью PHP (из одного запроса sql)?

Rawland Hustle 26.07.2019 11:07
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
0
5
141
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вам лучше использовать запрос JOIN, а затем структурировать свой массив из результата — наличие запроса в цикле часто является очень плохой идеей и индикатором того, что вместо этого вы можете использовать JOIN.

Вы хотите использовать LEFT JOIN, присоединив их к land_contract_id в обеих таблицах.

Затем зациклите свои результаты и создайте свой массив, который вы можете в конечном итоге закодировать в строку JSON после завершения.

$params = [];
$array = [];

$sql = "SELECT lc.*, 
               py.land_contract_annual_price_year AS `year`,  
               py.land_contract_annual_price_amount AS `amount`
        FROM land_contract AS lc
        LEFT JOIN land_contract_annual_price AS py 
            ON py.land_contract_id = lc.land_contract_id
        ";
if (isset($_POST['land_contract_id'])) {
    $sql .= 'WHERE lc.land_contract_id = ?';
    $params[] = $_POST["land_contract_id"];
}

$stmt = $pdo->prepare($sql);
$stmt->execute($params);
while ($row = $stmt->fetch()) {
    // Fields we want to extract from the select statement into the array 
    $select_fields = ['land_contract_id', 'land_contract_name', 'location_id', 'land_contract_link', 'land_contract_notes', 'land_owner_id', 
                        'land_contract_start_date', 'land_contract_end_date', 'land_contract_terminated', 'land_contract_payment_interval', 
                        'land_contract_price_type', 'land_contract_fixed_annual_price '];

    if (!isset($array[$row['land_contract_id']])) {
        // initialize the subarray if it has not been set already 
        $array[$row['land_contract_id']] = array_intersect_key($row, array_flip($select_fields));

        if ($row['year'] != null) {
            $array[$row['land_contract_id']]['land_contract_annual_prices'] = [];
        } else {
            $array[$row['land_contract_id']]['land_contract_annual_price'] = $row['land_contract_fixed_annual_price'];
        }
    }

    if ($row['year'] != null) {
        $array[$row['land_contract_id']]['land_contract_annual_prices'][] = ['year' => $row['year'], 'amount' => $row['amount']];
    }

}

if (empty($array)) {
    echo "No results";
    exit;
}

echo json_encode($array, JSON_UNESCAPED_UNICODE);

Спасибо! PHP жалуется на эту строку: $array[$row['land_contract_id']] = array_intersect_key($row, array_flip($select_fields);. Там написано "Parse error: syntax error, unexpected ';', expecting ')'.

Rawland Hustle 26.07.2019 13:49

Исправил (добавил скобки). У меня другая ошибка. Дай мне проверить.

Rawland Hustle 26.07.2019 13:50

Также изменено LEFT JOIN land_contract_annual_price_year на land_contract_annual_price. Теперь это работает! Однако «Кажется, вам нужно ЛЕВОЕ СОЕДИНЕНИЕ, так как вы хотите получить результаты в land_contract независимо от того, есть ли совпадение в land_contract_annual_price или нет». не правда. Я не знал, что LEFT JOIN так работает. Мне нужны результаты только в том случае, если есть совпадение является в land_contract_annual_price. Это возможно?

Rawland Hustle 26.07.2019 13:54

Да, тогда вам нужен INNER JOIN (то же самое, что JOIN).

Qirel 26.07.2019 13:56

В качестве примечания вам не нужно называть свои столбцы префиксом имени таблицы. Например, land_contract_annual_price_amount довольно длинное — вы можете просто использовать amount. Затем схема становится land_contract_annual_price.amount (table.column вместо table.table_column), с которой намного проще работать.

Qirel 26.07.2019 14:01

Я пробовал это, но кажется, что я хочу что-то среднее (что может быть невозможно). Посмотрите на мои справочные структуры JSON в моем исходном вопросе. По сути, я хочу повторить ВСЕ земельные контракты. Но если земельный контракт имеет связан со значениями в таблице land_contract_annual_price_year, я хочу, чтобы они были добавлены в виде массива в конце JSON (версия 1). Если земельный контракт имеет значения, связанные с нет в таблице land_contract_annual_price_year, мне не нужен пустой массив в конце JSON (версия 2). Это возможно?

Rawland Hustle 26.07.2019 14:03

Что-то вроде вышеприведенного? Используйте LEFT JOIN и проверьте, есть ли результаты в обеих таблицах, проверив значение из правой таблицы (цены).

Qirel 26.07.2019 14:07

Все наоборот. Теперь я получаю (пустой) массив, если есть данные, связанные с нет, но нет массива, если есть: D

Rawland Hustle 26.07.2019 14:11

Ой, перевернутая логика. Дерп, держись.

Qirel 26.07.2019 14:13

Вы не исправили логику, верно? Не знаю ждать или нет :D

Rawland Hustle 26.07.2019 14:41

Я пробовал, и ни один земельный контракт не имеет массива в конце. Вот почему я не был уверен, закончили вы или нет.

Rawland Hustle 26.07.2019 14:56

Кстати, если я публикую land_contract_id для получения одного земельного контракта, строка $stmt = $pdo->prepare($sql); вызывает Fatal error: Uncaught PDOException: SQLSTATE[23000]: Integrity constraint violation: 1052 Column 'land_contract_id' in where clause is ambiguous.

Rawland Hustle 26.07.2019 15:00

Я изменил $sql .= 'WHERE land_contract_id = ?'; на $sql .= 'WHERE lc.land_contract_id = ?';, и это решило проблему. Логическая часть все еще неверна :)

Rawland Hustle 26.07.2019 15:03

Давайте продолжить обсуждение в чате.

Rawland Hustle 26.07.2019 15:07

Я внес некоторые изменения с момента нашего последнего разговора, и добавление идентификатора в SQL-запрос больше не работает. Смотрите мой текущий код в моем исходном вопросе. if ($Id) { $sql .= 'WHERE lc.Id = ?'; $params[] = $Id; }обрабатывается как WHERE lc.Id = ? в запросе SQL. Почему?

Rawland Hustle 18.08.2019 21:12

Это потому, что вы на самом деле не видите окончательную строку запроса - заполнители ? будут заполнены только во втором цикле обращения к базе данных (сначала выполняется запрос с заполнителями, а затем отправляет данные). Если у вас может быть несколько условий в предложении WHERE, лучше поместить их в массив, implode() в AND и добавить WHERE к строке, прежде чем поместить ее в запрос.

Qirel 18.08.2019 21:46

Другие вопросы по теме