使用单个查询从相关数据库表中回显嵌套的 JSON 数组?

Posted

技术标签:

【中文标题】使用单个查询从相关数据库表中回显嵌套的 JSON 数组?【英文标题】:Echo nested JSON array from related database tables with a single query? 【发布时间】:2019-12-04 03:52:12 【问题描述】:

我有两个包含土地合同信息的数据库表。它们与land_contract_annual_price.land_contract_id -> land_contract.land_contract_id 相关。

表'land_contract'

表格'land_contract_annual_price'

如果土地合同在字段land_contract_price_type 中具有值“Rörligt pris”,则表中存在相关值 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 中的值为“Fast pris”,则表中没有相关值 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"
 
]

我没想到的是,当我获取所有土地合同时,这个解决方案很糟糕。如果每当土地合同在land_contract_price_type 字段中的值为“Rörligt pris”时,我要对另一个表进行第二次查询,我将进行数百次额外查询。

当土地合同在 land_contract_price_type 字段中的值为“Rörligt pris”时,是否有办法通过一 (1) 个查询创建嵌套 JSON 数组?

谢谢!

下面是我当前的代码。

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 joins 中受益。假设您使用的是 mysql,您可能希望在查询中使用 JSON functions。 @RoAchterberg 感谢您的回答。我曾经使用 LEFT JOIN 进行查询,但我不知道是否或如何获得该 JSON 结构。有什么建议吗? 我建议您查看 MySQL 提供的各种 JSON_* 函数,以将您的结果检索为列数据的 JSON 编码表示形式。 @RoAchterberg JSON 函数不起作用。原来我的主机正在运行 Maria DB。我真的以为是 MySQL。 @RoAchterberg 有没有办法用 php(从一个 sql 查询)创建我想要的 JSON? 【参考方案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 ')' 已修复(添加了括号)。我又犯了一个错误。让我检查一下。 还将LEFT JOIN land_contract_annual_price_year 更改为land_contract_annual_price。现在它起作用了!但是,“您似乎想要一个 LEFT JOIN,因为无论是否在 land_contract_annual_price 中匹配,您都希望得到 land_contract 的结果。”不是真的。我不知道 LEFT JOIN 是这样工作的。我只想要在land_contract_annual_price 中有 匹配的结果。这可能吗? 是的,那么您需要INNER JOIN(与JOIN 相同)。 作为旁注,您不必使用表名的前缀来命名列。例如,land_contract_annual_price_amount 相当长 - 您可以简单地使用 amount。然后架构变为land_contract_annual_price.amounttable.column 而不是table.table_column),这更容易使用。

以上是关于使用单个查询从相关数据库表中回显嵌套的 JSON 数组?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 PHP 从 mySQLi 查询中回显每一行?

php,json_encode,嵌套数组,带有一个“左连接”查询

在php中回显/返回一个数组

如何从数据库中回显具有特定变量的行

如何使用 php 在 WordPress 插件中回显 json 数据 [重复]

如何在laravel刀片中从json解码中回显键名和键值