TP5兼容达梦国产数据库

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 = '模式名';

综上,只是本项目遇到的兼容问题,遇到新问题还是推荐参考官网文档尤其是函数方面,进行测试修改。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/79027.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

《解锁图像“高清密码”:超分辨率重建之路》

在图像的世界里&#xff0c;高分辨率意味着更多细节、更清晰的画面&#xff0c;就像用高清望远镜眺望远方&#xff0c;一切都纤毫毕现。可现实中&#xff0c;我们常被低分辨率图像困扰&#xff0c;模糊的监控画面、老旧照片里难以辨认的面容……不过别担心&#xff0c;图像超分…

整合 CountVectorizer 和 TfidfVectorizer 绘制词云图

本文分别整合 CountVectorizer 和 TfidfVectorizer 绘制词云图 ✨ CountVectorizer CountVectorizer 是 scikit-learn 中用于 文本特征提取 的一个工具&#xff0c;它的主要作用是将一组文本&#xff08;文本集合&#xff09;转换为词频向量&#xff08;Bag-of-Words&#xf…

Linux 用户管理

用户管理是 Linux 系统管理中的重要组成部分&#xff0c;它涉及到用户和用户组的创建、删除、修改以及权限分配等操作。以下是关于用户和用户组管理的详细说明&#xff1a; 一、用户和用户组的概念 &#xff08;一&#xff09;用户&#xff08;User&#xff09; 用户是系统中…

【HTTP/2和HTTP/3的应用现状:看不见的革命】

HTTP/2和HTTP/3的应用现状&#xff1a;看不见的革命 实际上&#xff0c;HTTP/2和HTTP/3已经被众多著名网站广泛采用&#xff0c;只是这场革命对普通用户来说是"无形"的。让我们揭开这个技术变革的真相。 著名网站的HTTP/2和HTTP/3采用情况 #mermaid-svg-MtfrNDo5DG…

青少年编程与数学 02-018 C++数据结构与算法 16课题、贪心算法

青少年编程与数学 02-018 C数据结构与算法 16课题、贪心算法 一、贪心算法的基本概念定义组成部分 二、贪心算法的工作原理三、贪心算法的优点四、贪心算法的缺点五、贪心算法的应用实例&#xff08;一&#xff09;找零问题问题描述&#xff1a;贪心策略&#xff1a;示例代码&a…

UE5 Set actor Location和 Set World Location 和 Set Relative Location 的区别

在 Unreal Engine 的蓝图里&#xff0c;SetRelativeLocation、SetWorldLocation 和 SetActorLocation 三个节点虽然都能改变物体位置&#xff0c;但作用对象和坐标空间&#xff08;Coordinate Space&#xff09;不同&#xff1a; 1. SetActorLocation 作用对象&#xff1a;整个…

VINS-FUSION:跑通手机录制数据

文章目录 &#x1f4da;简介&#x1f680;手机录制数据&#x1f680;跑通数据&#x1f527;启动rviz&#x1f527;启动配置&#x1f527;播放rosbag&#x1f3af;跑通结果 &#x1f4da;简介 利用智能手机的 摄像头IMU 采集数据&#xff0c;并在 VINS-Fusion&#xff08;视觉惯…

Spring AI在大模型领域的趋势场景题深度解析

Spring AI在大模型领域的趋势场景题深度解析 在互联网大厂Java求职者的面试中&#xff0c;经常会被问到关于Spring AI在大模型领域的趋势场景的相关问题。本文通过一个故事场景来展示这些问题的实际解决方案。 第一轮提问 面试官&#xff1a;马架构&#xff0c;欢迎来到我们…

MySQL数据库全面详解:从基础到高级应用

一、数据存储概述 在计算机系统中&#xff0c;数据可以存储在多种形式中&#xff1a; 变量&#xff1a;程序中最基本的数据存储单元 元组&#xff1a;不可变的序列类型&#xff0c;常用于函数返回多个值 列表&#xff1a;有序可变集合&#xff0c;可存储不同类型元素 字典&…

Redux和MobX有什么区别

Redux 和 MobX 都是用于 React 应用的全局状态管理库&#xff0c;但它们在设计理念、使用方式和适用场景等方面存在明显的区别&#xff0c;下面为你详细分析&#xff1a; 1. 设计理念 Redux&#xff1a;基于 Flux 架构&#xff0c;遵循单向数据流和纯函数式编程的理念。状态是…

WPF实现类似Microsoft Visual Studio2022界面效果及动态生成界面技术

WPF实现类似VS2022界面效果及动态生成界面技术 一、实现类似VS2022界面效果 1. 主窗口布局与主题 <!-- MainWindow.xaml --> <Window x:Class"VsStyleApp.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x…

备份服务器,备份服务器数据有哪些方法可以实现?

服务器承载着企业核心业务数据与关键应用&#xff0c;数据丢失或业务中断可能带来灾难性后果。因此&#xff0c;构建一套科学、可靠的服务器数据备份体系至关重要。当前&#xff0c;服务器数据备份方法可根据技术架构、存储介质及恢复需求进行多维划分。根据不同场景、预算和技…

前端基础——5、CSS border属性与渐变色(详解与实战)

前端基础——5、CSS border属性与渐变色详解 CSS border属性与渐变色&#xff08;详解与实战&#xff09;一、border属性全面解析1. 基础三属性2. 复合写法3. 高级特性附加.border-style详解使用示例效果&#xff1a; CSS 渐变终极指南&#xff1a;线性渐变与径向渐变的深度解析…

企业出海降本:如何将应用从 AWS EC2 快速无缝迁移至DigitalOcean Droplet

企业出海已经成为目前最热门的趋势。然而不论你是做跨境电商&#xff0c;还是短剧出海&#xff0c;或处于最热门的AI 赛道&#xff0c;你都需要使用海外的云主机或GPU云服务。海外一线的云服务平台尽管覆盖区域广泛&#xff0c;但是往往费用成本较高。所以降本始终是企业出海关…

解决Spring Boot多模块自动配置失效问题

前言 在Spring Boot多模块项目中&#xff0c;模块间配置不生效是一个复杂但可解决的问题&#xff0c;尤其涉及自动配置类、依赖冲突、条件注解以及IDE配置。 一、问题背景与场景 1.1 场景描述 假设存在两个模块&#xff1a; 模块A&#xff1a;提供通用配置&#xff08;如跨…

WEBSTORM前端 —— 第2章:CSS —— 第4节:盒子模型

目录 1.画盒子 2.Pxcook软件 3.盒子模型——组成 4.盒子模型 ——边框线 5.盒子模型——内外边距 6.盒子模型——尺寸计算 7.清除默认样式 8.盒子模型——元素溢出 9.外边距问题 ①合并现象 ②塌陷问题 10.行内元素——内外边距问题 11.盒子模型——圆角 12.盒子…

Kafka和flume整合

需求1&#xff1a;利用flume监控某目录中新生成的文件&#xff0c;将监控到的变更数据发送给kafka&#xff0c;kafka将收到的数据打印到控制台&#xff1a; 在flume/conf下添加.conf文件&#xff0c; vi flume-kafka.conf # 定义 Agent 组件 a1.sourcesr1 a1.sinksk1 a1.c…

Idea 如何配合 grep console过滤并分析文件

这里写自定义目录标题 [grep console插件]()右击打开文件目录&#xff0c;选择 tail in console 同时可以添加自己的快捷键。 ![新的改变](https://i-blog.csdnimg.cn/direct/03423e27cf6c40c5abd2d53982547b61.png) 随后会在idea的菜单栏中出现tail菜单。这里&#xff0c;接下…

怎样学习Electron

学习 Electron 是一个很好的选择&#xff0c;特别是如果你想构建跨平台的桌面应用程序&#xff0c;并且已经有前端开发经验。以下是一个循序渐进的学习指南&#xff0c;帮助你从零开始掌握 Electron。 1. 基础知识 HTML/CSS/JavaScript 确保你对这些基础技术有扎实的理解&am…

MySQL 大数据量分页查询优化指南

问题分析 当对包含50万条记录的edu_test表进行分页查询时&#xff0c;发现随着分页越深入&#xff0c;查询时间越长&#xff1a; limit 0,10&#xff1a;0.05秒limit 200000,10&#xff1a;0.14秒limit 499000,10&#xff1a;0.21秒 通过EXPLAIN分析发现&#xff0c;limit o…