首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> MySQL 資料庫 >> MySQL 綜合教學

Zend_Db_Table_Abstract 中使用 Zend_Db_Select 實現 Join 與 Join Left 查詢

     編輯:MySQL 綜合教學

在 Zend Framework 1.x 專案中,Zend_Db_Table_Abstract 是資料表模型的基類。本文示範如何在其內部(或關聯的 Mapper 類)使用 Zend_Db_Select 物件構建複雜查詢,特別是多表 JOIN 與 JOIN LEFT 操作,並輸出關聯資料陣列。

資料表結構範例

-- 充值記錄主表
CREATE TABLE `user_charges` (
    `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
    `user_id` INT(10) NOT NULL,
    `charge_type` INT(3) NOT NULL,
    `charge_subtype` INT(3) NOT NULL,
    `charge_credits` INT(3) NOT NULL,
    `buy_date` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
    `valid_to` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
    `next_charge_date` TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00',
    `payment` VARCHAR(50) NOT NULL,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- 充值日誌詳情表
CREATE TABLE `charge_logs` (
    `id` INT(11) NOT NULL AUTO_INCREMENT,
    `charge_id` INT(11) NOT NULL,
    `title` TEXT NOT NULL,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- 測試資料
INSERT INTO `user_charges` VALUES (1, 1, 1, 1, 100, '2011-09-09', '2011-09-16', '0000-00-00', 'paypal');
INSERT INTO `charge_logs` VALUES (1, 1, 'XXXXXXX');

在 Model (extends Zend_Db_Table_Abstract) 中查詢

class UserCharges extends Zend_Db_Table_Abstract
{
    protected $_name = 'user_charges';

    public function getChargesWithLogs($userId)
    {
        // 取得 Select 物件
        $select = $this->getAdapter()->select();
        
        // 建構查詢:FROM user_charges uc LEFT JOIN charge_logs cl
        $select->from(array('uc' => 'user_charges'))
               ->joinLeft(
                   array('cl' => 'charge_logs'),  // 表別名 => 表名
                   'uc.id = cl.charge_id',        // JOIN 條件
                   array('title')                 // 只選取需要的欄位
               )
               ->where('uc.user_id = ?', $userId); // 參數綁定,防 SQL 注入

        // 執行並返回陣列
        return $this->getAdapter()->fetchAll($select);
    }
}

在 Mapper 類中使用(推薦分層架構)

class UserChargesMapper
{
    protected $_dbTable;

    public function setDbTable($dbTable)
    {
        if (is_string($dbTable)) {
            $dbTable = new $dbTable();
        }
        if (!$dbTable instanceof Zend_Db_Table_Abstract) {
            throw new Exception('Invalid table data gateway provided');
        }
        $this->_dbTable = $dbTable;
        return $this;
    }

    public function getDbTable()
    {
        if (null === $this->_dbTable) {
            $this->setDbTable('UserCharges');
        }
        return $this->_dbTable;
    }

    public function fetchChargesWithLogs($userId)
    {
        $select = $this->getDbTable()->getAdapter()->select();
        $select->from(array('uc' => 'user_charges'))
               ->joinLeft(array('cl' => 'charge_logs'), 'uc.id = cl.charge_id', array('title'))
               ->where('uc.user_id = ?', $userId);
        
        return $this->getDbTable()->getAdapter()->fetchAll($select);
    }
}

輸出結果範例

array(1) {
  [0] => array(10) {
    ["id"] => string(1) "1"
    ["user_id"] => string(1) "1"
    ["charge_type"] => string(1) "1"
    ["charge_subtype"] => string(1) "1"
    ["charge_credits"] => string(3) "100"
    ["buy_date"] => string(19) "2011-09-09 00:00:00"
    ["valid_to"] => string(19) "2011-09-16 00:00:00"
    ["next_charge_date"] => string(19) "0000-00-00 00:00:00"
    ["payment"] => string(6) "paypal"
    ["title"] => string(7) "XXXXXXX"
  }
}

Zend_Db_Select Join 方法速查

方法SQL 對應用途
join($name, $cond, $cols)INNER JOIN只返回兩表都匹配的行
joinLeft($name, $cond, $cols)LEFT JOIN返回左表所有行,右表無匹配則為 NULL
joinRight($name, $cond, $cols)RIGHT JOIN返回右表所有行,左表無匹配則為 NULL
joinFull($name, $cond, $cols)FULL JOIN返回兩表所有行(MySQL 不支援,會模擬)
joinCross($name, $cond, $cols)CROSS JOIN笛卡爾積
joinNatural($name, $cols)NATURAL JOIN自動按同名欄位連接

關鍵參數說明

  • $name:array('別名' => '表名') 或直接 '表名'(不建議,無別名易衝突)
  • $cond:JOIN 條件字串,如 'uc.id = cl.charge_id',建議使用別名
  • $cols:要選取的欄位陣列,如 array('title', 'created_at');* 表所有欄位;false/空陣列表不選取任何欄位

進階技巧

1. 避免欄位名衝突

$select->from(array('uc' => 'user_charges'), array('id', 'user_id', 'charge_type'))
       ->joinLeft(array('cl' => 'charge_logs'), 'uc.id = cl.charge_id', array('log_title' => 'title'));
-- 將 cl.title 別名為 log_title,避免與 uc.title 衝突(若存在)

2. 多表連接鏈

$select->from(array('a' => 'table_a'))
       ->joinLeft(array('b' => 'table_b'), 'a.id = b.a_id', array('b_name'))
       ->joinLeft(array('c' => 'table_c'), 'b.id = c.b_id', array('c_name'))
       ->where('a.status = ?', 1);

3. 使用 Zend_Db_Expr 處理複雜表達式

$select->from(array('u' => 'users'), array(
    'id', 
    'full_name' => new Zend_Db_Expr("CONCAT(first_name, ' ', last_name)")
));

常見錯誤與避坑

錯誤原因修正
欄位名衝突導致資料覆蓋兩表有同名欄位且未指定別名明確指定 $cols 並使用別名
JOIN 條件寫錯表別名未使用 from() 定義的別名統一使用陣列別名 array('uc' => 'table')
SQL 注入風險直接拼接變數到條件字串使用參數綁定 where('col = ?', $val)
效能差未加索引 / 選取過多欄位 / 大表無 LIMIT加索引、只選需要欄位、分頁

總結

Zend_Db_Select 提供了流暢介面構建複雜 SQL,配合 Zend_Db_Table_Abstract 的 getAdapter() 可靈活實現各類 JOIN 查詢。關鍵點:使用陣列別名、明確指定選取欄位、善用參數綁定、理解各 JOIN 類型語義。Mapper 模式進一步分離關注點,讓模型專注業務邏輯,資料存取邏輯集中維護,是中大型 Zend 專案的推薦架構。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved