У меня есть две таблицы базы данных, содержащие информацию о земельных контрактах. Они связаны с land_contract_annual_price.land_contract_id -> land_contract.land_contract_id.
Таблица 'land_contract_annual_price'
Если земельный контракт имеет значение «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;
}
@RoAchterberg Спасибо за ваш ответ. Раньше у меня был запрос с LEFT JOIN, но я не знал, смогу ли я получить эту структуру JSON и как. Какие-либо предложения?
Я предлагаю вам взглянуть на различные функции JSON_*, предоставляемые MySQL, для получения вашего результата в виде представления данных столбца в кодировке JSON.
@RoAchterberg Функция JSON не работает. Оказывается, на моем хосте работает Maria DB. Я действительно думал, что это MySQL.
@RoAchterberg Есть ли способ создать JSON, который я хочу, с помощью PHP (из одного запроса sql)?






Вам лучше использовать запрос 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 ')'.
Исправил (добавил скобки). У меня другая ошибка. Дай мне проверить.
Также изменено LEFT JOIN land_contract_annual_price_year на land_contract_annual_price. Теперь это работает! Однако «Кажется, вам нужно ЛЕВОЕ СОЕДИНЕНИЕ, так как вы хотите получить результаты в land_contract независимо от того, есть ли совпадение в land_contract_annual_price или нет». не правда. Я не знал, что LEFT JOIN так работает. Мне нужны результаты только в том случае, если есть совпадение является в land_contract_annual_price. Это возможно?
Да, тогда вам нужен INNER JOIN (то же самое, что JOIN).
В качестве примечания вам не нужно называть свои столбцы префиксом имени таблицы. Например, land_contract_annual_price_amount довольно длинное — вы можете просто использовать amount. Затем схема становится land_contract_annual_price.amount (table.column вместо table.table_column), с которой намного проще работать.
Я пробовал это, но кажется, что я хочу что-то среднее (что может быть невозможно). Посмотрите на мои справочные структуры JSON в моем исходном вопросе. По сути, я хочу повторить ВСЕ земельные контракты. Но если земельный контракт имеет связан со значениями в таблице land_contract_annual_price_year, я хочу, чтобы они были добавлены в виде массива в конце JSON (версия 1). Если земельный контракт имеет значения, связанные с нет в таблице land_contract_annual_price_year, мне не нужен пустой массив в конце JSON (версия 2). Это возможно?
Что-то вроде вышеприведенного? Используйте LEFT JOIN и проверьте, есть ли результаты в обеих таблицах, проверив значение из правой таблицы (цены).
Все наоборот. Теперь я получаю (пустой) массив, если есть данные, связанные с нет, но нет массива, если есть: D
Ой, перевернутая логика. Дерп, держись.
Вы не исправили логику, верно? Не знаю ждать или нет :D
Я пробовал, и ни один земельный контракт не имеет массива в конце. Вот почему я не был уверен, закончили вы или нет.
Кстати, если я публикую 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.
Я изменил $sql .= 'WHERE land_contract_id = ?'; на $sql .= 'WHERE lc.land_contract_id = ?';, и это решило проблему. Логическая часть все еще неверна :)
Давайте продолжить обсуждение в чате.
Я внес некоторые изменения с момента нашего последнего разговора, и добавление идентификатора в SQL-запрос больше не работает. Смотрите мой текущий код в моем исходном вопросе. if ($Id) { $sql .= 'WHERE lc.Id = ?'; $params[] = $Id; }обрабатывается как WHERE lc.Id = ? в запросе SQL. Почему?
Это потому, что вы на самом деле не видите окончательную строку запроса - заполнители ? будут заполнены только во втором цикле обращения к базе данных (сначала выполняется запрос с заполнителями, а затем отправляет данные). Если у вас может быть несколько условий в предложении WHERE, лучше поместить их в массив, implode() в AND и добавить WHERE к строке, прежде чем поместить ее в запрос.
Кажется, вам было бы полезно реализовать SQL присоединяется. Предполагая, что вы используете MySQL, вы можете использовать JSON-функции в своем запросе.