1.首先數據庫安裝,部署時需配置大小寫不敏感
2.安裝PHP達夢擴展,一定要是對應版本(兼容操作系統)的擴展,否則會出現各種報錯。參考官方文檔:https://eco.dameng.com/document/dm/zh-cn/app-dev/php_php_new.html,注意拷貝擴展時根據安裝的php版本,拷貝對應的線程安全或非安全版本擴展。
3.進入框架兼容改造部分,Config/database.php配置:
'type' ? ? ? ? ? ?=> 'dm',?
'quote' ? ? ? ? ? => '"', //庫名(模式名)引用符號" 或 ` 等
4.添加對應驅動文件:thinkphp/library/think/db/connector/Dm.php 和 thinkphp/library/think/db/builder/Dm.php,具體代碼看項目
thinkphp/library/think/db/connector/Dm.php:
<?php
// +----------------------------------------------------------------------
// | Modified for Dameng Database
// +----------------------------------------------------------------------namespace think\db\connector;use PDO;
use think\Container;
use think\db\Connection;
use think\db\Query;/*** 達夢數據庫驅動*/
class Dm extends Connection
{protected $builder = '\\think\\db\\builder\\Dm'; // 使用達夢的 SQL 構造器/*** 初始化* @access protected* @return void*/protected function initialize(){// 可以在這里擴展達夢數據庫特有的功能}/*** 解析pdo連接的dsn信息* @access protected* @param array $config 連接信息* @return string*/protected function parseDsn($config){$dsn = 'dm:host=' . $config['hostname'] . ';port=' . $config['hostport'] . ';schema="' . $config['database'] . '"';if (!empty($config['charset'])) {$dsn .= ';charset=' . $config['charset'];}return $dsn;}/*** 取得數據表的字段信息* @access public* @param string $tableName* @return array*/public function getFields($tableName){list($tableName) = explode(' ', $tableName);if (strpos($tableName, '.')) {list($schema, $tableName) = explode('.', $tableName);}// 達夢數據庫獲取表結構$sql = "SELECT COLUMN_NAME, DATA_TYPE,DATA_DEFAULT, DATA_LENGTH, NULLABLEFROM DBA_TAB_COLUMNSWHERE TABLE_NAME = '{$tableName}'";$pdo = $this->query($sql, [], false, true);$result = $pdo->fetchAll(PDO::FETCH_ASSOC);$info = [];if ($result) {foreach ($result as $key => $val) {$val = array_change_key_case($val);$info[$val['column_name']] = ['name' => $val['column_name'],'type' => $val['data_type'],'notnull' => 'N' == $val['nullable'],'default' => $val['data_default'],'primary' => $val['column_name'] == 'id', //表主鍵基本都是id,為方便獲取信息省略查詢'autoinc' => $val['column_name'] == 'id', //表自增字段基本都是id,為方便獲取信息省略查詢];}}/** 查詢主鍵及自增示例* -- 獲取主鍵字段SELECTc.COLUMN_NAME AS pkFROMDBA_CONSTRAINTS conJOINDBA_CONS_COLUMNS c ON con.CONSTRAINT_NAME = c.CONSTRAINT_NAMEWHEREcon.TABLE_NAME = 'ss_logs_select'AND con.OWNER = 'self-service'AND con.CONSTRAINT_TYPE = 'P';-- 檢查字段是否為自增字段select b.table_name,a.name COL_NAME from SYS.SYSCOLUMNS a,all_tables b,sys.sysobjects c where a.INFO2 & 0x01 = 0x01and a.id=c.id and c.name= b.table_name AND b.table_name='ss_logs'* */return $this->fieldCase($info);}/*** 取得數據庫的表信息* @access public* @param string $dbName* @return array*/public function getTables($dbName = ''){// 達夢數據庫使用 SHOW TABLES 語句獲取表信息$sql = !empty($dbName) ? "SELECT TABLE_NAME FROM DBA_TABLES WHERE OWNER = '" . $dbName . "'" : 'SELECT TABLE_NAME FROM DBA_TABLES';$pdo = $this->query($sql, [], false, true);$result = $pdo->fetchAll(PDO::FETCH_ASSOC);$info = [];foreach ($result as $key => $val) {$info[$key] = current($val);}return $info;}/*** 獲取數據表信息* @access public* @param mixed $tableName 數據表名 留空自動獲取* @param string $fetch 獲取信息類型 包括 fields type bind pk* @return mixed*/public function getTableInfo($tableName, $fetch = ''){if (is_array($tableName)) {$tableName = key($tableName) ?: current($tableName);}if (strpos($tableName, ',')) {// 多表不獲取字段信息return false;} else {$tableName = $this->parseSqlTable($tableName);}// 修正子查詢作為表名的問題if (strpos($tableName, ')')) {return [];}list($tableName) = explode(' ', $tableName);if (false === strpos($tableName, '.')) {$schema = $this->getConfig('database') . '.' . $tableName;} else {$schema = $tableName;}if (!isset(self::$info[$schema])) {// 讀取緩存$cacheFile = Container::get('app')->getRuntimePath() . 'schema' . DIRECTORY_SEPARATOR . $schema . '.php';if (!$this->config['debug'] && is_file($cacheFile)) {$info = include $cacheFile;} else {$info = $this->getFields($tableName);}$fields = array_keys($info);$bind = $type = [];foreach ($info as $key => $val) {// 記錄字段類型$type[$key] = $val['type'];$bind[$key] = $this->getFieldBindType($val['type']);if (!empty($val['primary'])) {$pk[] = $key;}}if (isset($pk)) {// 設置主鍵$pk = count($pk) > 1 ? $pk : $pk[0];} else {$pk = null;}self::$info[$schema] = ['fields' => $fields, 'type' => $type, 'bind' => $bind, 'pk' => $pk];}//針對性處理字段名是desc這個關鍵詞的情況if($fetch == "bind" && isset(self::$info[$schema][$fetch]['desc'])){self::$info[$schema][$fetch]['"desc"'] = self::$info[$schema][$fetch]['desc'];unset(self::$info[$schema][$fetch]['desc']);}return $fetch ? self::$info[$schema][$fetch] : self::$info[$schema];}/*** 查找單條記錄* @access public* @param Query $query 查詢對象* @return array|null|\PDOStatement|string* @throws DbException* @throws ModelNotFoundException* @throws DataNotFoundException*/public function find(Query $query){// 分析查詢表達式$options = $query->getOptions();$pk = $query->getPk($options);$data = $options['data'];$query->setOption('limit', 1);if (empty($options['fetch_sql']) && !empty($options['cache'])) {// 判斷查詢緩存$cache = $options['cache'];if (is_string($cache['key'])) {$key = $cache['key'];} else {$key = $this->getCacheKey($query, $data);}$result = Container::get('cache')->get($key);if (false !== $result) {return $result;}}if (is_string($pk) && !is_array($data)) {if (isset($key) && strpos($key, '|')) {list($a, $val) = explode('|', $key);$item[$pk] = $val;} else {$item[$pk] = $data;}$data = $item;}$query->setOption('data', $data);// 生成查詢SQL$sql = $this->builder->select($query);$query->removeOption('limit');$bind = $query->getBind();if (!empty($options['fetch_sql'])) {// 獲取實際執行的SQL語句return $this->getRealSql($sql, $bind);}// 事件回調$result = $query->trigger('before_find');if (!$result) {// 執行查詢$resultSet = $this->query($sql, $bind, $options['master'], $options['fetch_pdo']);if ($resultSet instanceof \PDOStatement) {// 返回PDOStatement對象return $resultSet;}$result = isset($resultSet[0]) ? $resultSet[0] : null;}if (isset($cache) && $result) {// 緩存數據$this->cacheData($key, $result, $cache);}return $result;}/*** SQL性能分析* @access protected* @param string $sql* @return array*/protected function getExplain($sql){// 達夢數據庫可能不支持 EXPLAIN,需要根據實際情況調整return [];}protected function supportSavepoint(){return true;}/*** 啟動 XA 事務* @access public* @param string $xid XA 事務 ID* @return void*/public function startTransXa($xid){$this->initConnect(true);if (!$this->linkID) {return false;}// 達夢數據庫使用 DBMS_XA 包啟動 XA 事務$this->linkID->exec("CALL DBMS_XA.START('{$xid}')");}/*** 預編譯 XA 事務* @access public* @param string $xid XA 事務 ID* @return void*/public function prepareXa($xid){$this->initConnect(true);// 達夢數據庫使用 DBMS_XA 包結束并準備 XA 事務$this->linkID->exec("CALL DBMS_XA.END('{$xid}')");$this->linkID->exec("CALL DBMS_XA.PREPARE('{$xid}')");}/*** 提交 XA 事務* @access public* @param string $xid XA 事務 ID* @return void*/public function commitXa($xid){$this->initConnect(true);// 達夢數據庫使用 DBMS_XA 包提交 XA 事務$this->linkID->exec("CALL DBMS_XA.COMMIT('{$xid}')");}/*** 回滾 XA 事務* @access public* @param string $xid XA 事務 ID* @return void*/public function rollbackXa($xid){$this->initConnect(true);// 達夢數據庫使用 DBMS_XA 包回滾 XA 事務$this->linkID->exec("CALL DBMS_XA.ROLLBACK('{$xid}')");}
}
thinkphp/library/think/db/builder/Dm.php:
<?php
// +----------------------------------------------------------------------
// | Adapted for Dameng Database
// +----------------------------------------------------------------------namespace think\db\builder;use think\db\Builder;
use think\db\Expression;
use think\db\Query;
use think\Exception;/*** 達夢數據庫驅動*/
class Dm extends Builder
{// 查詢表達式解析protected $parser = ['parseCompare' => ['=', '<>', '>', '>=', '<', '<='],'parseLike' => ['LIKE', 'NOT LIKE'],'parseBetween' => ['NOT BETWEEN', 'BETWEEN'],'parseIn' => ['NOT IN', 'IN'],'parseExp' => ['EXP'],'parseRegexp' => ['REGEXP', 'NOT REGEXP'],'parseNull' => ['NOT NULL', 'NULL'],'parseBetweenTime' => ['BETWEEN TIME', 'NOT BETWEEN TIME'],'parseTime' => ['< TIME', '> TIME', '<= TIME', '>= TIME'],'parseExists' => ['NOT EXISTS', 'EXISTS'],'parseColumn' => ['COLUMN'],];protected $insertAllSql = '%INSERT% INTO %TABLE% (%FIELD%) VALUES %DATA% %COMMENT%';protected $updateSql = 'UPDATE %TABLE% %JOIN% SET %SET% %WHERE% %ORDER%%LIMIT% %LOCK%%COMMENT%';/*** 生成insertall SQL* @access public* @param Query $query 查詢對象* @param array $dataSet 數據集* @param bool $replace 是否replace* @return string*/public function insertAll(Query $query, $dataSet, $replace = false){$options = $query->getOptions();// 獲取合法的字段if ('*' == $options['field']) {$allowFields = $this->connection->getTableFields($options['table']);} else {$allowFields = $options['field'];}// 獲取綁定信息$bind = $this->connection->getFieldsBind($options['table']);foreach ($dataSet as $k => $data) {$data = $this->parseData($query, $data, $allowFields, $bind);$values[] = '( ' . implode(',', array_values($data)) . ' )';if (!isset($insertFields)) {$insertFields = array_keys($data);}}$fields = [];foreach ($insertFields as $field) {$fields[] = $this->parseKey($query, $field);}return str_replace(['%INSERT%', '%TABLE%', '%FIELD%', '%DATA%', '%COMMENT%'],[$replace ? 'REPLACE' : 'INSERT',$this->parseTable($query, $options['table']),implode(' , ', $fields),implode(' , ', $values),$this->parseComment($query, $options['comment']),],$this->insertAllSql);}/*** 正則查詢* @access protected* @param Query $query 查詢對象* @param string $key* @param string $exp* @param mixed $value* @param string $field* @return string*/protected function parseRegexp(Query $query, $key, $exp, $value, $field){if ($value instanceof Expression) {$value = $value->getValue();}// REGEXP約等于達夢數據庫parseRegexp(),若行不通也可以使用 LIKE 或其他方式替代return "parseRegexp({$key}, {$value})";}/*** 字段和表名處理* @access public* @param Query $query 查詢對象* @param mixed $key 字段名* @param bool $strict 嚴格檢測* @return string*/public function parseKey(Query $query, $key, $strict = false){if (is_numeric($key)) {return $key;} elseif ($key instanceof Expression) {return $key->getValue();}$key = trim($key);// 達夢數據庫不支持 JSON 字段的 `->>` 或 `->` 語法if (strpos($key, '->') || strpos($key, '->>')) {throw new Exception('JSON field syntax is not supported in Dameng Database.');}if (strpos($key, '.') && !preg_match('/[,\'\"\(\)`\s]/', $key)) {list($table, $key) = explode('.', $key, 2);$alias = $query->getOptions('alias');if ('__TABLE__' == $table) {$table = $query->getOptions('table');$table = is_array($table) ? array_shift($table) : $table;}if (isset($alias[$table])) {$table = $alias[$table];}}/*if ($strict && !preg_match('/^[\w\.\*]+$/', $key)) { //desc這種關鍵詞形式需加""的字段會導致匹配失敗throw new Exception('not support data:' . $key);}*//*if ('*' != $key && !preg_match('/[,\'\"\*\(\)`.\s]/', $key)) {$key = '"' . $key . '"'; // 達夢數據庫使用雙引號}*/if (isset($table)) {$key = $table . '.' . $key;}return $key;}/*** 生成查詢SQL* @access public* @param Query $query 查詢對象* @return string*/public function select(Query $query){$options = $query->getOptions();return str_replace(['%TABLE%', '%DISTINCT%', '%FIELD%', '%JOIN%', '%WHERE%', '%GROUP%', '%HAVING%', '%ORDER%', '%LIMIT%', '%UNION%', '%LOCK%', '%COMMENT%', '%FORCE%'],[$this->parseTable($query, $options['table']),$this->parseDistinct($query, $options['distinct']),$this->parseField($query, $options['field']),$this->parseJoin($query, $options['join']),$this->parseWhere($query, $options['where']),$this->parseGroup($query, $options['group']),$this->parseHaving($query, $options['having']),$this->parseOrder($query, $options['order']),$this->parseLimit($query, $options['limit']),$this->parseUnion($query, $options['union']),$this->parseLock($query, $options['lock']),$this->parseComment($query, $options['comment']),$this->parseForce($query, $options['force']),],$this->selectSql);}/*** table分析* @access protected* @param Query $query 查詢對象* @param mixed $tables 表名* @return string*/protected function parseTable(Query $query, $tables){$item = [];$options = $query->getOptions();foreach ((array) $tables as $key => $table) {if (!is_numeric($key)) {$key = $this->connection->parseSqlTable($key);$item[] = $this->parseKey($query, $key) . ' ' . $this->parseKey($query, $table);} else {$table = $this->connection->parseSqlTable($table);if (isset($options['alias'][$table])) {$item[] = $this->parseKey($query, $table) . ' ' . $this->parseKey($query, $options['alias'][$table]);} else {$item[] = $this->parseKey($query, $table);}}}return implode(',', $item);}/*** 隨機排序* @access protected* @param Query $query 查詢對象* @return string*/protected function parseRand(Query $query){// 達夢數據庫不支持 MySQL 的 rand() 函數,可以使用其他方式替代return 'DBMS_RANDOM.VALUE';}
}
5.Application/common.php添加方法:
//判斷是否使用的達夢數據庫
function isDMBase()
{return config('database.type') == 'dm';
}
6.相關group 分組sql語句與南大通用類似,group by的字段必須和查詢字段相對應,不能出現字段分組模棱兩可的情況。
7.Sql條件中存在邏輯或使用||的,更換成OR。
8.sql語句中使用了date_format()的地方全部更換成公共方法getSqlDateFormat()
格式符參考”DM8 SQL.pdf”文件里格式符:
/*** 根據提供的參數返回計算日期時間的sql語句* 為兼容達夢等數據庫語法差異* 支持達夢group by* @param $intval 日期時間偏差,必須嚴格按照格式傳輸例如: -1 day* @param $time 日期時間* @param $format 日期時間格式* @return string*/
function getSqlDateFormat($intval, $format = '', $time = '')
{if($time == ''){$time = 'NOW()';}elseif(substr($time, 0, 5) == 'left('){ //兼容sql語句left(date_field_name, len),如left(create_time,10)$time = $time;}else{$time = "'{$time}'"; //具體日期字符串}$format = $format ?: '%Y-%m-%d';if(isDMBase()){//達夢語法$time = $time == 'NOW()' ? 'SYSDATE' : $time;$intvalArr = explode(' ', $intval);if(count($intvalArr) != 2){ //必須嚴格按照格式傳輸例如: -1 dayreturn '';}$offset = $intvalArr[0]; //偏移量$unit = strtolower($intvalArr[1]); //取最后一個單位(day,hour,month等)$unitFormat = '';switch ($unit){case 'day':$unitFormat = 'DD';break;case 'month':$unitFormat = 'MM';break;case 'hour':$unitFormat = 'HH';break;case 'week':$unitFormat = 'WW';break;case 'year':$unitFormat = 'YYYY';break;}$format = str_replace(['%Y', '%m', '%d', '%H', '%u', '%i', '%s'], ['YYYY', 'MM', 'DD', 'HH24', 'WW', 'MI', 'SS'], $format);$sql = "TO_CHAR(DATEADD({$unitFormat}, {$offset}, {$time}), '{$format}')";}elseif(isKBASEDataBase()){//人大金倉語法if(substr($time, 0, 5) == 'left('){$leftParams = explode(',', substr($time, 5, -1));$time = 'to_timestamp(substring(' . $leftParams[0] . ' FROM 1 FOR ' . $leftParams[1] . '))';}else{$time = $time == 'NOW()' ? 'CURRENT_DATE' : 'TIMESTAMP ' . $time;}$intvalArr = explode(' ', $intval);if(count($intvalArr) != 2){ //必須嚴格按照格式傳輸例如: -1 dayreturn '';}$offset = intval($intvalArr[0]); //偏移量$sign = $offset < 0 ? '-' : '+'; //符號位$offset = abs($offset);$unit = strtolower($intvalArr[1]); //取最后一個單位(day,hour,month等)$intvalstr = $offset == 0 ? '' : " {$sign} INTERVAL '{$offset} {$unit}'";$format = str_replace(['%Y', '%m', '%d', '%H', '%u', '%i', '%s'], ['YYYY', 'MM', 'DD', 'HH24', 'IW', 'MI', 'SS'], $format);$sql = "TO_CHAR({$time}{$intvalstr},'{$format}')";}else{$sql = "DATE_FORMAT(DATE_ADD({$time}, INTERVAL {$intval}), '{$format}')";}return $sql;
}/*** 根據提供的參數返回計算日期時間的sql語句* 為兼容達夢等數據庫語法差異* 注:達夢不支持group by DATE_FORMAT()* @param $intval 日期時間偏差,必須嚴格按照格式傳輸例如: -1 day* @param $time 日期時間* @param $format 日期時間格式* @return string*/
/*function getSqlDateFormat($intval, $format = '', $time = '')
{if($time == ''){$time = 'NOW()';}elseif(substr($time, 0, 5) == 'left('){ //兼容sql語句left(date_field_name, len),如left(create_time,10)$time = $time;}else{$time = "'{$time}'"; //具體日期字符串}$format = $format ?: '%Y-%m-%d';if(isDMBase()){//達夢語法$intvalArr = explode(' ', $intval);if(count($intvalArr) != 2){ //必須嚴格按照格式傳輸例如: -1 dayreturn '';}$offset = $intvalArr[0]; //偏移量$unit = strtolower($intvalArr[1]); //取最后一個單位(day,hour,month等)if($unit == 'week'){$offset = intval($offset) * 7;$unit = 'day';}$sql = "DATE_FORMAT(DATE_ADD({$time}, INTERVAL '{$offset}' {$unit}), '{$format}')";}else{$sql = "DATE_FORMAT(DATE_ADD({$time}, INTERVAL {$intval}), '{$format}')";}return $sql;
}*/
相關測試代碼:
SELECT TO_CHAR(ADD_DAYS('2021-08-16', -1), 'YYYY-MM-DD');SELECT TO_CHAR(DATEADD(HH, -8, SYSDATE), 'YYYY-MM-DD HH24');SELECT TO_CHAR(DATEADD(DD, -1, SYSDATE), 'YYYY-MM-DD HH24');SELECT TO_CHAR(DATEADD(WW, -1, SYSDATE), 'YYYY-MM-DD HH24');SELECT TO_CHAR(SYSDATE, 'YYYY-MM-WW HH24');SELECT TO_CHAR(DATEADD(MM, -1, SYSDATE), 'YYYY-MM-DD HH24 MI SS');SELECT TO_CHAR(DATEADD(MM, -1, '2016-08-27'), 'YYYY-MM-DD HH24');SELECT TO_CHAR(DATEADD(DD, -0, left(create_time,18)), 'YYYY-WW') FROM ss_payment ORDER BY create_time desc LIMIT 1;SELECT TO_CHAR(DATEADD(WW, -1, SYSDATE), 'YYYY-MM-DD HH24') AS date;
后又查看達夢網站相關支持函數文檔:函數 | 達夢技術文檔
將TO_CHAR切換成了DATE_FORMAT(DATE_ADD(datetime, interval), format)
,同時要注意函數DATE_ADD在達夢和mysql中的差異,達夢中interval的數值必須用’’包含,且數值和符號(-+)之間不能存在空格,且達夢不支持“周”需手動進行轉化
但后續因為DATE_FORMAT()不支持group by,又切換回TO_CHAR()了。
9.達夢數據庫要求 SELECT?列表中的所有非聚合字段(如 dname、dcode、ddate)必須出現在 GROUP BY 子句中,同時字段不能是別名,支持函數分組如left(),修改所有group by date,其中date是函數計算結果的別名,將別名替換成計算表達式,同時后面要加上所有非聚合字段。如:
group by left(create_time,10),非聚合字段...
10.sql錯誤:試圖修改自增列[id],將更新數據中的自增字段過濾掉
11.字段賦值時使用單引號,看似簡單實則很多地方都容易出現。
12.更新數據時報錯:SQLSTATE[HY000]: General error: -2007 第 1 行, 第 447 列[desc]附近出現錯誤:
語法分析出錯
根據報錯極有可能是desc關鍵字相同字段名導致的,這種情況根據測試需要將字段名用雙引號引用,但又會觸發報錯:不支持的數據表達式:"desc",需要刪除think\db\builder\Dm
里面的parseKey方法里的嚴格模式判斷邏輯去掉。
13.插入數據時報錯:SQLSTATE[HY000]: General error: -2007 第 1 行, 第 155 列[desc]附近出現錯誤:語法分析出錯
以上錯誤同10情況類似,只不過換成了插入操作。將desc換成”desc”會觸發嚴格模式檢測到字段”desc”不存在,根據builder.php中報錯位置代碼,大概瀏覽db/Query.php相關方法,大概有幾種方式實現設置非嚴格模式:setOption()、option()、strict()以及配置config/database里的”fields_strict”=>false(影響全局),為降低影響范圍,選擇在調用insert()前調用->strict(false)
其實最好的辦法還是在數據表設計初期禁用以關鍵字作為字段名稱,
原以為設置上就可以解決問題,報錯確實是不報錯了,但是desc數據也確實是沒更新,經排查似乎是認為”desc”字段不存在給直接過濾掉了
跟蹤代碼,最后通過connector/Dm.php重寫getTableInfo()方法,針對desc字段解析單獨處理,實現了插入成功。
14.達夢不支持group by date_format()表達式,更換application/common.php中getSqlDateFormat()切換成TO_CHAR()形式,上面已提過。
15.將DATE_SUB(now(), INTERVAL 60?second)更改成DATE_SUB(now(), INTERVAL '60'?second), 數值加單引號
16.檢查表是否存在使用新添加的公共方法checkTableExists()
/*** @param $table 表名* @param $isFullName 是否為全名* @return mixed*/
function checkTableExists($table = '', $isFullName = 0)
{$tableName = $isFullName ? $table : Config('database.prefix') . $table;$sql = "SHOW TABLES LIKE '{$tableName}'";if(isDMBase()){$database = Config('database.database');$sql = "SELECT TABLE_NAMEFROM DBA_TABLESWHERE OWNER = '{$database}' AND TABLE_NAME LIKE '{$tableName}'";}elseif(isGBASEDataBase()){ //這里是南大通用的,沒有的情況下可以忽略或刪除$sql = "SELECT tablenameFROM pg_catalog.pg_tablesWHERE schemaname != 'pg_catalog' AND schemaname != 'information_schema'AND tablename LIKE '{$tableName}'";}$exists = \think\Db::query($sql);return $exists;
}
17.CONCAT()不支持單參數,所以單個參數需要把concat去掉
18.SHOW TABLES;查看所有表更換成SELECT TABLE_NAME FROM DBA_TABLES WHERE OWNER = '模式名';
綜上,只是本項目遇到的兼容問題,遇到新問題還是推薦參考官網文檔尤其是函數方面,進行測試修改。