首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 以指定欄位為鍵名重組資料庫查詢結果陣列

     編輯:PHP 相關知識

場景痛點

查詢資料庫取得結果集後,常需以某欄位(如 id、sid)為鍵名重組陣列,方便後續 O(1) 查找、合併、比對,避免迴圈遍歷。

通用函數實作

<?php
/**
 * 查詢結果重組:以指定欄位值為鍵名
 *
 * @param resource|PDOStatement $result 查詢結果資源
 * @param string                $keyField  作為鍵名的欄位名
 * @param string                $fetchMode 取得模式:assoc|num|both|obj
 * @return array 重組後的關聯陣列
 */
function fetchAllKeyed($result, $keyField, $fetchMode = 'assoc') {
    $data = [];
    $fetchFunc = [
        'assoc' => MYSQLI_ASSOC,
        'num'   => MYSQLI_NUM,
        'both'  => MYSQLI_BOTH,
        'obj'   => MYSQLI_ASSOC,  // 物件模式另行處理
    ][$fetchMode] ?? MYSQLI_ASSOC;

    // mysqli_result
    if ($result instanceof mysqli_result) {
        while ($row = $result->fetch_assoc()) {
            if (!array_key_exists($keyField, $row)) {
                trigger_error("欄位 '$keyField' 不存在於結果集", E_USER_WARNING);
                continue;
            }
            $key = $row[$keyField];
            $data[$key] = $row;
        }
        return $data;
    }

    // PDOStatement
    if ($result instanceof PDOStatement) {
        while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
            if (!array_key_exists($keyField, $row)) {
                trigger_error("欄位 '$keyField' 不存在於結果集", E_USER_WARNING);
                continue;
            }
            $key = $row[$keyField];
            $data[$key] = $row;
        }
        return $data;
    }

    // 陣列輸入(已取得的資料)
    if (is_array($result)) {
        foreach ($result as $row) {
            if (is_array($row) && array_key_exists($keyField, $row)) {
                $data[$row[$keyField]] = $row;
            }
        }
        return $data;
    }

    throw new InvalidArgumentException('不支援的結果集類型');
}

實戰範例:站點流量統計報表

資料表結構

-- 站點設定表(讀多寫少 → MyISAM)
CREATE TABLE site_config (
    id SMALLINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    sid SMALLINT UNSIGNED UNIQUE COMMENT '站點ID',
    site_url VARCHAR(128) NOT NULL,
    site_name VARCHAR(80) NOT NULL,
    add_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=MyISAM CHARSET=utf8mb4;

-- 流量統計表(寫多讀少 → InnoDB)
CREATE TABLE site_stat (
    id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    sid SMALLINT UNSIGNED NOT NULL,
    ip_address VARCHAR(45) NOT NULL,
    add_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB CHARSET=utf8mb4;

查詢與重組

<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8mb4', 'user', 'pass');

// 1. 取得所有站點(不需重組,或以 sid 為鍵)
$sitesStmt = $pdo->query('SELECT sid, site_url, site_name FROM site_config');
$sites = fetchAllKeyed($sitesStmt, 'sid');
// 結果:[200 => ['sid'=>200, 'site_url'=>'baidu.com', ...], 201 => [...], ...]

// 2. 取得今日流量統計(GROUP BY sid)
$today = date('Y-m-d');
$statStmt = $pdo->prepare("
    SELECT sid, COUNT(*) AS come_total 
    FROM site_stat 
    WHERE add_time >= ? AND add_time < ? 
    GROUP BY sid
");
$statStmt->execute([$today . ' 00:00:00', $today . ' 23:59:59']);
$stats = fetchAllKeyed($statStmt, 'sid');
// 結果:[200 => ['sid'=>200, 'come_total'=>3], 202 => ['sid'=>202, 'come_total'=>3], ...]

// 3. 產出報表(O(1) 查找統計資料)
echo "日期,站點ID,站點網址,流量數\n";
foreach ($sites as $sid => $site) {
    $comeTotal = $stats[$sid]['come_total'] ?? 0;
    echo "{$today},{$sid},{$site['site_url']},{$comeTotal}\n";
}

/*
輸出:
2024-01-15,200,baidu.com,3
2024-01-15,201,google.com,0
2024-01-15,202,cnblogs.com,3
2024-01-15,203,codejia.net,1
*/

進階用法:多欄位合併鍵、分組陣列

<?php
/**
 * 進階重組:支援多欄位組合鍵、分組模式
 */
function fetchAllKeyedAdvanced($result, $keyFields, $group = false) {
    $data = [];
    $isMultiple = is_array($keyFields);
    
    $rows = is_array($result) ? $result : iterator_to_array($result);
    
    foreach ($rows as $row) {
        // 組合鍵名
        if ($isMultiple) {
            $keyParts = [];
            foreach ($keyFields as $f) $keyParts[] = $row[$f] ?? '';
            $key = implode('|', $keyParts);
        } else {
            $key = $row[$keyFields] ?? null;
        }
        if ($key === null) continue;

        if ($group) {
            $data[$key][] = $row;  // 分組:同鍵值收集為陣列
        } else {
            $data[$key] = $row;    // 唯一:後者覆蓋前者
        }
    }
    return $data;
}

// 使用:以 user_id + date 為鍵分組
$logs = $pdo->query("SELECT user_id, DATE(created_at) as log_date, action FROM user_logs")->fetchAll();
$grouped = fetchAllKeyedAdvanced($logs, ['user_id', 'log_date'], true);
/*
[
    '1001|2024-01-15' => [
        ['user_id'=>1001, 'log_date'=>'2024-01-15', 'action'=>'login'],
        ['user_id'=>1001, 'log_date'=>'2024-01-15', 'action'=>'view'],
    ],
    ...
]
*/

效能優化建議

場景建議
大量資料(>10萬筆)SQL 層面用 GROUP BY、INDEX,減少 PHP 端處理
頻繁查找重組為鍵值陣列,享受 O(1) 查找
記憶體敏感使用 Generator 逐行處理,或 MYSQLI_USE_RESULT 非緩衝查詢
多表關聯優先考慮 SQL JOIN,或分別查詢後 PHP 端合併(如本例)

常見陷阱

  • ❌ 鍵名欄位有重複值 → 後者覆蓋前者(需用分組模式 $group=true)
  • ❌ 鍵名為 NULL 或空字串 → 導致資料遺失或覆蓋
  • ❌ 欄位名稱大小寫不匹配 → MySQL 預設不區分,但 PHP 陣列鍵嚴格區分大小寫
  • ✅ 建議:SQL 用 AS 統一別名、PHP 端統一 strtolower() 鍵名
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved