`

orm 用多了,忽然发觉SQL不会写了... 整个基本的功能重新学习SQL,改自fleaphp

阅读更多
<?php
/**
 * 数据库封装对象,对Mysql的一个操作封装
 *
 */
class CoreDB {
	
	/**
	 * 数据库操作实用工具对象
	 *
	 * @var CoreDbUtils
	 */
	protected $_dbUtils = NULL;
	
	/**
	 * 链接资源标志
	 * 
	 * @var resource
	 */
	protected $_link_identifier = NULL ;
	
	protected $_dsn = null;	
		
    /**
     * 执行的查询计数
     *
     * @var int
     */
    protected $querycount = 0;
    
    /**
     * 最后一次数据库操作的错误信息
     *
     * @var mixed
     */
    public $lasterr = null;

    /**
     * 最后一次数据库操作的错误代码
     *
     * @var mixed
     */
    public $lasterrcode = null;
    
    /**
     * 最近一次插入操作或者 lastInsertId() 操作返回的插入 ID
     *
     * @var mixed
     */
    protected $_lastInsertId = null;
	
    /**
     * 指示数据库是否支持事务
     *
     * @var boolean
     */
    protected $_has_transaction = false;
        
    /**
     * 指示数据库是否支持事务中的 SAVEPOINT 功能
     *
     * @var boolean
     */
    protected $_has_savepoint = false;
	    
    /**
     * 指示事务启动次数
     *
     * @var int
     */
    protected $_trans_count = 0;

    /**
     * 指示事务执行期间是否发生了错误
     *
     * @var boolean
     */
    protected $_has_failed_query = false;

    /**
     * SAVEPOINT 堆栈
     *
     * @var array
     */
    protected $_savepoint_stack = array();
    
	/**
	 * 数据库程序版本号
	 */
	private $_version = NULL;
	
	function __construct(array $dsn,$doParse=true){		
		$this->_dsn = $doParse ? self::parseDSN($dsn) : $dsn;		
	}
	
	/**
     * 连接数据库
     * 
     * @return bool
     * @throw SqlQueryException
     */
	function connect(){
		if (is_resource($this->_link_identifier)) return;
		$host = $this->_dsn['host'] . ( empty($this->_dsn['port']) ? '' : ":{$this->_dsn['port']}" );
		
		if (!empty($this->_dsn['options'])) {
			$this->_link_identifier = @mysql_connect($host, $dsn['login'], $dsn['password'], false, $this->_dsn['options']);
		} else {
			$this->_link_identifier = @mysql_connect($host,$this->_dsn['login'] ,$this->_dsn['password']);
		}
//		$this->_log_($this->_link_identifier);
		
		if (!$this->_link_identifier){
			$this->lasterr = mysql_error();
			$this->lasterrcode = mysql_errno();
			throw new SqlQueryException("connect('{$host}','{$this->_dsn['login']}') failed!", $this->lasterr, $this->lasterrcode);
		}
			
		if (!empty($this->_dsn['database'])) {
			$this->selectDb($this->_dsn['database']);
		}
		
		$this->_version = $this->getOne('SELECT VERSION()');
		if ($this->_version >= '5.0') {
            $this->_has_transaction = true;
        }
		
		// 设置字符集
		if (!empty($this->_dsn['charset'])) {
			$this->execute("SET NAMES '{$this->_dsn['charset']}'");
		}
		
		return true;
	}
	
	/**
     * 关闭数据库连接
     */
	function close(){
		if($this->_link_identifier){
			mysql_close($this->_link_identifier);			
		}
		$this->_link_identifier = NULL ;
        $this->_lastInsertId = null;
        $this->_trans_count = 0;
	}
	
	/**
     * 选择要操作的数据库
     *
     * @param string $database
     *
     * @return boolean
     */
	function selectDb($database)
	{
		if (!mysql_select_db($database, $this->_link_identifier)) {
			$this->lasterr = mysql_error($this->_link_identifier);
			$this->lasterrcode = mysql_errno($this->_link_identifier);
			throw new SqlQueryException("SELECT DATABASE: '{$database}' FAILED!", $this->lasterr,$this->lasterrcode);
		}
		return true;
	}
	
	/**
     * 启动事务
     */
    function startTrans()
    {
    	if ($this->_has_transaction){
	    	
    		if ($this->_trans_count == 0) {
	            $this->execute('START TRANSACTION');
	            $this->_has_failed_query = false;
	        }
	        $this->_trans_count++;
	    	
	        if ($this->_trans_count > 1 && $this->_has_savepoint) {
	            $savepoint = 'savepoint_' . $this->_trans_count;
	            $this->execute("SAVEPOINT {$savepoint}");
	            array_push($this->_savepoint_stack, $savepoint);
	        }
    	}
    }

    /**
     * 完成事务,根据查询是否出错决定是提交事务还是回滚事务
     *
     * 如果 $commitOnNoErrors 参数为 true,当事务中所有查询都成功完成时,则提交事务,否则回滚事务
     * 如果 $commitOnNoErrors 参数为 false,则强制回滚事务
     *
     * @param $commitOnNoErrors 指示在没有错误时是否提交事务
     */
    function completeTrans($commitOnNoErrors = true)
    {
    	if ($this->_has_transaction && $this->_trans_count > 0){
    		$this->_trans_count--;
    		if ($this->_trans_count > 0 && $this->_has_savepoint) {
    			$savepoint = array_pop($this->_savepoint_stack);
    			if ($this->_has_failed_query || $commitOnNoErrors == false) {
    				$this->execute("ROLLBACK TO SAVEPOINT {$savepoint}");
    			}
    		} else {
    			if ($this->_has_failed_query == false && $commitOnNoErrors) {
    				$this->execute('COMMIT');
    			} else {
    				$this->execute('ROLLBACK');
    			}
    		}
    	}
    }
	
    /**
     * 强制指示在调用 completeTrans() 时回滚事务
     */
    function failTrans()
    {
        $this->_has_failed_query = true;
    }

    /**
     * 返回事务是否失败的状态
     */
    function hasFailedTrans()
    {
        return $this->_has_failed_query;
    }
    
    /**
     * 执行一个查询,返回一个 resource 或者 boolean 值
     *
     * @param string $sql
     * @param array $inputarr
     * @param boolean $throw 指示查询出错时是否抛出异常
     *
     * @return resource|boolean
     */
	function execute($sql, $inputarr = null, $throw = true)
	{
		if (is_array($inputarr)) {
			$sql = $this->bind($sql, $inputarr);
		}
		
		$this->_log_($sql);

		$this->querycount++;
		$result = mysql_query($sql, $this->_link_identifier);
		if ($result !== false) {			
			return $result;
		}
		$this->lasterr = mysql_error($this->_link_identifier);
		$this->lasterrcode = mysql_errno($this->_link_identifier);

		if ($throw) {
			throw new SqlQueryException($sql, $this->lasterr, $this->lasterrcode);
		}
		return false;
	}
	
	/**
     * 返回最近一次数据库操作受到影响的记录数
     *
     * @return int
     */
	function lastQueryAffectedRows(){
		return mysql_affected_rows($this->_link_identifier);
	}
	
	/**
     * 获取最后一次 nextId 操作获得的值
     *
     * @return int
     */
	function lastInsertId(){
		return mysql_insert_id($this->_link_identifier);
	}
	
	/**
     * 执行一个查询,返回查询结果记录集
     *
     * @param string|resource $sql
     *
     * @return array
     */
    function getAll($sql)
    {
        $res = is_resource($sql) ? $sql : $this->execute($sql);
        $rowset = array();
        while ($row = mysql_fetch_assoc($res)) {
            $rowset[] = $row;
        }
        mysql_free_result($res);
        return $rowset;
    }
	
    /**
     * 执行查询,返回第一条记录的第一个字段
     *
     * @param string|resource $sql
     *
     * @return mixed
     */
    function getOne($sql)
    {
        $res = is_resource($sql) ? $sql : $this->execute($sql);        
        $row = mysql_fetch_row($res);
        mysql_free_result($res);
        return isset($row[0]) ? $row[0] : null;
    }
    
    /**
     * 执行查询,返回第一条记录
     *
     * @param string|resource $sql
     *
     * @return mixed
     */
    function getRow($sql)
    {
        $res = is_resource($sql) ? $sql : $this->execute($sql);        
        $row = mysql_fetch_assoc($res);
        mysql_free_result($res);
        return $row;
    }

    /**
     * 执行查询,返回结果集的指定列
     *
     * @param string|resource $sql
     * @param int $col 要返回的列,0 为第一列
     *
     * @return mixed
     */
    function getCol($sql, $col = 0)
    {
        $res = is_resource($sql) ? $sql : $this->execute($sql);
        $data = array();
        while ($row = mysql_fetch_row($res)) {
            $data[] = $row[$col];
        }
        mysql_free_result($res);
        return $data;
    }    
    
    /**
     * 从记录集中返回一行数据
     *
     * @param resouce $res
     *
     * @return array
     */
	function fetchRow($res)
	{
		return mysql_fetch_row($res);
	}
	
	/**
     * 从记录集中返回一行数据,字段名作为键名
     *
     * @param resouce $res
     *
     * @return array
     */
	function fetchAssoc($res)
	{
		return mysql_fetch_assoc($res);
	}
	
	/**
     * 释放查询句柄
     *
     * @param resource $res
     *
     * @return boolean
     */
	function freeRes($res)
	{
		return mysql_free_result($res);
	}
	
	/**
     * 转义字符串
     *
     * @param string $value
     *
     * @return mixed
     */
	function qstr($value)
	{
		if (is_int($value) || is_float($value)) { return $value; }
		if (is_bool($value)) { return $value ? 1 : 0; }
		if (is_null($value)) { return 'NULL'; }
		return "'" . mysql_real_escape_string($value, $this->_link_identifier) . "'";
	}	
	
    /**
     * 执行一个查询,返回分组后的查询结果记录集
     *
     * $groupBy 参数如果为字符串或整数,表示结果集根据 $groupBy 参数指定的字段进行分组。
     * 如果 $groupBy 参数为 true,则表示根据每行记录的第一个字段进行分组。
     *
     * @param string|resource $sql
     * @param string $groupBy
     *
     * @return array
     */
    function getAllGroupBy($sql, & $groupBy)
    {
    	$res = is_resource($sql) ? $sql : $this->execute($sql);
        $data = array();
        $row = mysql_fetch_assoc($res);
        if ($row != false) {
            if (empty($groupBy)) {
                $groupBy = key($row);
            }
            do {
                $rkv = $row[$groupBy];
                unset($row[$groupBy]);
                $data[$rkv][] = $row;
            } while ($row = mysql_fetch_assoc($res));
        }
        mysql_free_result($res);
        return $data;
    }    
        
	/**
     * 分析 DSN 数组,返回包含 DSN 连接信息的数组,失败返回 false
     *
     * @param array $dsn
     *
     * @return array
     */
    static function parseDSN(array $dsn)
    {
    	if (empty($dsn)) return NULL;	
    	
        $dsn['host'] = isset($dsn['host']) ? $dsn['host'] : '';
        $dsn['port'] = isset($dsn['port']) ? $dsn['port'] : '';
        $dsn['login'] = isset($dsn['login']) ? $dsn['login'] : '';
        $dsn['password'] = isset($dsn['password']) ? $dsn['password'] : '';
        $dsn['database'] = isset($dsn['database']) ? $dsn['database'] : '';
        $dsn['charset'] = isset($dsn['charset']) ? $dsn['charset'] : 'utf8';
        $dsn['options'] = isset($dsn['options']) ? $dsn['options'] : '';
        $dsn['prefix'] = isset($dsn['prefix']) ? $dsn['prefix'] : '';
        $dsn['schema'] = isset($dsn['schema']) ? $dsn['schema'] : '';
        
        $dsnid = "mysql://{$dsn['login']}:{$dsn['password']}@{$dsn['host']}_{$dsn['prefix']}/{$dsn['database']}/{$dsn['schema']}/{$dsn['options']}";
        $dsn['id'] = $dsnid;
        return $dsn;
    }
    
	/**
     * 返回数据库访问对象实例
     *
     * 必须提供 $dsn 参数
     *
     * DSN 是 Database Source Name 的缩写,可以理解为数据源名字。
     * DSN 是一个数组,包含了连接数据库需要的各种信息,例如主机、用户名、密码等。
     *
     * DSN 的正确写法:
     *
     * example:
     * <code>
     * $dsn = array(
     *      'host'     => 'localhost',
     *      'login'    => 'username',
     *      'password' => 'password',
     *      'database' => 'test_db',
     *      'charset'  => 'utf8',
     * );
     *
     * $dbo = CoreDB::instance($dsn);
     * </code>
     *
     * @param array $dsn
     *
     * @return CoreDB
     */
    static function instance(array $dsn)
    {	
        $dsn = self::parseDSN($dsn);
        
        $dsnid = "core-db/[{$dsn['id']}]";
        if (App::isRegistered($dsnid)) return App::registry($dsnid);
        
        return App::register(new self($dsn,false),$dsnid);
    }
	
    /**
     * 返回 数据库操作工具对象
     * 
     * @return CoreDbUtils
     */
    function getDbUtils(){
    	if (!$this->_dbUtils){    		
    		$this->_dbUtils = new CoreDbUtils($this);
    	}
    	return $this->_dbUtils;
    }
    
    private function _log_($statement)
    {
        App::$_logWriter_->append($statement,'sql','debug');
    }
	
}

/**
 * InvalidDSNException 异常指示没有提供有效的 DSN 设置
 *
 */
class InvalidDSNException extends Exception
{
	public $dsn;
	
    /**
     * 构造函数
     *
     * @param array $dsn
     */
    function __construct(array $dsn)
    {
    	unset($dsn['password']);
    	$this->dsn = $dsn; 	
        parent::__construct('无效的数据源名称“Data-Source-Name (DSN)”.');
    }
}

/**
 * SqlQueryException 异常指示一个 SQL 语句执行错误
 * 
 */
class SqlQueryException extends Exception
{
    public $sql;    
    public $error;    
    public $errno;
	
    /**
     * 构造函数
     *
     * @param string $sql sql语句
     * @param string $error 错误文本
     * @param int $errno 错误号
     */
    function __construct($sql, $error ,$errno)
    {
    	$this->sql = $sql;
    	$this->error = $error;
    	$this->errno = $errno;
    	
    	$msg = sprintf('SQL: "%s"\nError: "%s"\nErrno: "%s"', $sql, $error ,$errno);
        parent::__construct($msg);
    }
}

require_once 'dbutils.php';
 
<?php
/**
 * CoreDb 工具
 *
 */
final class CoreDbUtils {
	
	/**
	 * @var CoreDb
	 */
	private $_dbo = NULL;	
	
	function __construct(CoreDb $dbo){
		$this->_dbo = $dbo;
	}

	/**
	 *  C CHAR 或 VARCHAR 类型字段
	 *  X TEXT 或 CLOB 类型字段
	 *  B 二进制数据(BLOB)
	 *  N 数值或者浮点数
	 *  D 日期
	 *  T TimeStamp
	 *  L 逻辑布尔值
	 *  I 整数
	 *  R 自动增量或计数器
	 */
	private static $typeMap = array(
            'BIT'           => 'I',
            'TINYINT'       => 'I',
            'BOOL'          => 'L',
            'BOOLEAN'       => 'L',
            'SMALLINT'      => 'I',
            'MEDIUMINT'     => 'I',
            'INT'           => 'I',
            'INTEGER'       => 'I',
            'BIGINT'        => 'I',
            'FLOAT'         => 'N',
            'DOUBLE'        => 'N',
            'DOUBLEPRECISION' => 'N',
            'FLOAT'         => 'N',
            'DECIMAL'       => 'N',
            'DEC'           => 'N',

            'DATE'          => 'D',
            'DATETIME'      => 'T',
            'TIMESTAMP'     => 'T',
            'TIME'          => 'T',
            'YEAR'          => 'I',

            'CHAR'          => 'C',
            'NCHAR'         => 'C',
            'VARCHAR'       => 'C',
            'NVARCHAR'      => 'C',
            'BINARY'        => 'B',
            'VARBINARY'     => 'B',
            'TINYBLOB'      => 'X',
            'TINYTEXT'      => 'X',
            'BLOB'          => 'X',
            'TEXT'          => 'X',
            'MEDIUMBLOB'    => 'X',
            'MEDIUMTEXT'    => 'X',
            'LONGBLOB'      => 'X',
            'LONGTEXT'      => 'X',
            'ENUM'          => 'C',
            'SET'           => 'C',
	);
	
	/**
     * 按照指定的类型,返回值
     *
     * @param mixed $value
     * @param string $type
     *
     * @return mixed
     */
    static function setValueByType($value, $type)
    {
        /**
         *  C CHAR 或 VARCHAR 类型字段
         *  X TEXT 或 CLOB 类型字段
         *  B 二进制数据(BLOB)
         *  N 数值或者浮点数
         *  D 日期
         *  T TimeStamp
         *  L 逻辑布尔值
         *  I 整数
         *  R 自动增量或计数器
         */
        switch (strtoupper($type)) {
	        case 'I':
	            return (int)$value;
	        case 'N':
	            return (float)$value;
	        case 'L':
	            return (bool)$value;
	        default:
	            return $value;
        }
    }
	
    /**
     * 返回指定表(或者视图)的元数据
     *
     * 部分代码参考 ADOdb 实现。
     *
     * 每个字段包含下列属性:
     *
     * name:            字段名
     * scale:           小数位数
     * type:            字段类型
     * simpleType:      简单字段类型(与数据库无关)
     * maxLength:       最大长度
     * notNull:         是否不允许保存 NULL 值
     * primaryKey:      是否是主键
     * autoIncrement:   是否是自动增量字段
     * binary:          是否是二进制数据
     * unsigned:        是否是无符号数值
     * hasDefault:      是否有默认值
     * defaultValue:    默认值
     *
     * @param string $table
     *
     * @return array
     */
	function metaColumns($table)
	{
		$rs = $this->_dbo->execute(sprintf('SHOW FULL COLUMNS FROM %s', $table));
		if (!$rs) { return false; }
		$retarr = array();
		while (($row = $this->_dbo->fetchAssoc($rs))) {
			$field = array();
			$field['name'] = $row['Field'];
			$type = $row['Type'];

			$field['scale'] = null;
			$queryArray = false;
			if (preg_match('/^(.+)\((\d+),(\d+)/', $type, $queryArray)) {
				$field['type'] = $queryArray[1];
				$field['maxLength'] = is_numeric($queryArray[2]) ? $queryArray[2] : -1;
				$field['scale'] = is_numeric($queryArray[3]) ? $queryArray[3] : -1;
			} elseif (preg_match('/^(.+)\((\d+)/', $type, $queryArray)) {
				$field['type'] = $queryArray[1];
				$field['maxLength'] = is_numeric($queryArray[2]) ? $queryArray[2] : -1;
			} elseif (preg_match('/^(enum)\((.*)\)$/i', $type, $queryArray)) {
				$field['type'] = $queryArray[1];
				$arr = explode(",",$queryArray[2]);
				$field['enums'] = $arr;
				$zlen = max(array_map("strlen",$arr)) - 2; // PHP >= 4.0.6
				$field['maxLength'] = ($zlen > 0) ? $zlen : 1;
			} else {
				$field['type'] = $type;
				$field['maxLength'] = -1;
			}
			$field['simpleType'] = self::$typeMap[strtoupper($field['type'])];
			// if ($field['simpleType'] == 'C' && $field['maxLength'] > 250) {
			// $field['simpleType'] = 'X';
			// }
			$field['notNull'] = ($row['Null'] != 'YES');
			$field['primaryKey'] = ($row['Key'] == 'PRI');
			$field['autoIncrement'] = (strpos($row['Extra'], 'auto_increment') !== false);
			if ($field['autoIncrement']) { $field['simpleType'] = 'R'; }
			$field['binary'] = (strpos($type,'blob') !== false);
			$field['unsigned'] = (strpos($type,'unsigned') !== false);

			if ($field['type'] == 'tinyint' && $field['maxLength'] == 1) {
				$field['simpleType'] = 'L';
			}

			if (!$field['binary']) {
				$d = $row['Default'];
				if ($d != '' && $d != 'NULL') {
					$field['hasDefault'] = true;
					$field['defaultValue'] = self::setValueByType($d, $field['simpleType']);
				} else {
					$field['hasDefault'] = false;
				}
			}

			$field['description'] = isset($row['Comment']) ? $row['Comment'] : '';

			$retarr[strtoupper($field['name'])] = $field;
		}
		$this->_dbo->freeRes($rs);
		return $retarr;
	}
	
	/**
     * 获得所有数据表的名称
     *
     * @param string $pattern
     * @param string $schema
     *
     * @return array
     */
	function metaTables($pattern = null, $schema = null)
	{
		$sql = 'SHOW TABLES';
		if (!empty($schema)) {
			$sql .= " FROM {$schema}";
		}
		if (!empty($pattern)) {
			$sql .= ' LIKE ' . $this->_dbo->qstr($schema);
		}
		$res = $this->_dbo->execute($sql, null, false);
		$tables = array();
		while (($row = $this->_dbo->fetchRow($res))) {
			$tables[] = reset($row);
		}
		$this->_dbo->freeRes($res);
		return $tables;
	}
	
    /**
     * 为数据表产生下一个序列值
     *
     * @param string $seqName
     * @param string $startValue
     *
     * @return int
     */
    function nextId($seqName = 'coredb_seq', $startValue = 1)
    {
        $getNextIdSql = sprintf('UPDATE %s SET id = LAST_INSERT_ID(id + 1)', $seqName);
        $result = $this->_dbo->execute($getNextIdSql, null, false);
        if (!$result) {
            if (!$this->createSeq($seqName, $startValue)) { return false; }
            $result = $this->_dbo->execute($getNextIdSql);
            if (!$result) { return false; }
        }		
        return $this->_dbo->lastInsertId();
    }

    /**
     * 创建一个新的序列,成功返回 true,失败返回 false
     *
     * @param string $seqName
     * @param int $startValue
     *
     * @return boolean
     */
    function createSeq($seqName = 'coredb_seq', $startValue = 1)
    {
        if ($this->_dbo->execute(sprintf('CREATE TABLE %s (id INT NOT NULL)', $seqName))) {
            return $this->_dbo->execute(sprintf('INSERT INTO %s VALUES (%s)', $seqName, $startValue - 1));
        } else {
            return false;
        }
    }

    /**
     * 删除一个序列
     *
     * @param string $seqName
     */
    function dropSeq($seqName = 'coredb_seq')
    {
        return $this->_dbo->execute(sprintf('DROP TABLE %s', $seqName));
    }
    
/**
     * 执行一个查询,返回查询结果记录集、指定字段的值集合以及以该字段值分组后的记录集
     *
     * @param string|resource $sql
     * @param string $field
     * @param array $fieldValues
     * @param array $reference
     *
     * @return array
     */
    function getAllWithFieldRefs($sql, $field, & $fieldValues, & $reference)
    {
        $res = is_resource($sql) ? $sql : $this->_dbo->execute($sql);
        $fieldValues = array();
        $reference = array();
        $offset = 0;
        $data = array();

        while ($row = $this->_dbo->fetchAssoc($res)) {
            $fieldValue = $row[$field];
            unset($row[$field]);
            $data[$offset] = $row;
            $fieldValues[$offset] = $fieldValue;
            $reference[$fieldValue] =& $data[$offset];
            $offset++;
        }
        $this->_dbo->freeRes($res);
        return $data;
    }

    /**
     * 执行一个查询,并将数据按照指定字段分组后与 $assocRowset 记录集组装在一起
     *
     * @param string|resource $sql
     * @param array $assocRowset
     * @param string $mappingName
     * @param boolean $oneToOne
     * @param string $refKeyName
     * @param mixed $limit
     */
    function assemble($sql, & $assocRowset, $mappingName, $oneToOne, $refKeyName, $limit = null)
    {
        if (is_resource($sql)) {
            $res = $sql;
        } else {
            if (!is_null($limit)) {
                if (is_array($limit)) {
                    list($length, $offset) = $limit;
                } else {
                    $length = $limit;
                    $offset = 0;
                }
                $res = $this->_dbo->selectLimit($sql, $length, $offset);
            } else {
                $res = $this->_dbo->execute($sql);
            }
        }

        if ($oneToOne) {
            // 一对一组装数据
            while ($row = $this->_dbo->fetchAssoc($res)) {
                $rkv = $row[$refKeyName];
                unset($row[$refKeyName]);
                $assocRowset[$rkv][$mappingName] = $row;
            }
        } else {
            // 一对多组装数据
            while ($row = $this->_dbo->fetchAssoc($res)) {
                $rkv = $row[$refKeyName];
                unset($row[$refKeyName]);
                $assocRowset[$rkv][$mappingName][] = $row;
            }
        }
        $this->_dbo->freeRes($res);
    }
}

/**
 * SqlHelper 类提供了各种生成 SQL 语句的辅助方法
 *
 */
final class CoreDbSqlHelper {
	
	/**
	 * 分析查询条件
	 *
	 * @param CoreDb $dbo
	 * @param mixed $conditions
	 *
	 * @return string
	 */
	static function parseConditions(CoreDb $dbo, $conditions)
	{
		// 对于 NULL,直接返回 NULL
        if (is_null($conditions)) { return null; }
 		
		// 如果是字符串,则假定为自定义条件
        if (is_string($conditions)) {
            return $conditions;
        }
	
        // 如果不是数组,说明提供的查询条件有误
        if (!is_array($conditions)) {
            return null;
        }
 		$where = '';
 		$expr = '';
 		
 		/**
         * 不过何种条件形式,一律为  字段名 => (值, 操作, 连接运算符, 值是否是SQL命令) 的形式
         */
 		foreach ($conditions as $field => $cond) {
 			
 			$expr = 'AND';
            
 			if (!is_string($field)) {
 				continue;
 			}
 			if (!is_array($cond)) {
                // 字段名 => 值
            	$cond = array($cond);
            }
            reset($cond);
            // 第一个元素是值
 			if (!isset($cond[1])) { $cond[1] = '='; }
            if (!isset($cond[2])) { $cond[2] = $expr; }
            if (!isset($cond[3])) { $cond[3] = false; }
			
            list($value, $op, $expr, $isCommand) = $cond;
            
 			if (is_array($value)){
 				$value = '(' . implode(',',array_map(array($dbo, 'qstr'),$value)) . ')' ;
 				$op = 'IN';
 				$isCommand = true;
 			}
 			
 			if (!$isCommand) {
				$value = $dbo->qstr($value);		
			}
			
			$where .= "{$field} {$op} {$value} {$expr} ";
 		}
 		
        $where = substr($where, 0, - (strlen($expr) + 2));        
        return $where;
	}
	
	/**
	 * 返回 limit sql字串
	 *
	 * @param int|array $limit
	 * @return string
	 */
	static function getLimitSql($limit){
		if (is_null($limit)) return '';
		
		if (is_array($limit)) {
            list($length, $offset) = $limit;
        } else {
            $length = $limit;
            $offset = null;
        }
        
		if (!is_null($offset)) {
			$sql = " LIMIT " . (int)$offset;
			if (!is_null($length)) {
				$sql .= ', ' . (int)$length;
			} else {
				$sql .= ', 4294967294';
			}
		} elseif (!is_null($length)) {
			$sql = " LIMIT " . (int)$length;
		}
		return $sql;
	}
	
	/**
     * 返回数据库可以接受的日期格式
     *
     * @param int $timestamp
     * @return string
     */
    static function dbTimeStamp($timestamp)
    {
        return date('Y-m-d H:i:s', $timestamp);
    }

	/**
     * 将数据表名字转换为完全限定名
     *
     * @param string $tableName
     * @param string $schema
     *
     * @return string
     */
	static function qtable($tableName, $schema = null)
	{
		return $schema != '' ? "`{$schema}`.`{$tableName}`" : "`{$tableName}`";
	}
	
	/**
     * 将字段名转换为完全限定名,避免因为字段名和数据库关键词相同导致的错误
     *
     * @param string $fieldName
     * @param string $tableName
     * @param string $schema
     *
     * @return string
     */
	static function qfield($fieldName, $tableName = null, $schema = null)
	{
		$fieldName = ($fieldName == '*') ? '*' : "`{$fieldName}`";
		return $tableName != '' ? self::qtable($tableName, $schema) . '.' . $fieldName : $fieldName;
	}
		
    /**
     * 一次性将多个字段名转换为完全限定名
     *
     * @param string|array $fields
     * @param string $tableName
     * @param string $schema
     * @param boolean $returnArray
     *
     * @return string
     */
    static function qfields($fields, $tableName = null, $schema = null, $returnArray = false)
    {
        if (!is_array($fields)) {
            $fields = explode(',', $fields);
            $fields = array_map('trim', $fields);
        }
        $return = array();
        foreach ($fields as $fieldName) {
            $return[] = self::qfield($fieldName, $tableName, $schema);
        }
        return $returnArray ? $return : implode(', ', $return);
    }
	
	/**
     * 根据 SQL 语句和提供的参数数组,生成最终的 SQL 语句
     *
     * @param CoreDb $dbo
     * @param string $sql
     * @param array $inputarr
     *
     * @return string
     */
    static function bind(CoreDb $dbo, $sql, & $inputarr)
    {
        $arr = explode('?', $sql);
        $sql = array_shift($arr);
        foreach ($inputarr as $value) {
            if (isset($arr[0])) {
                $sql .= $dbo->qstr($value) . array_shift($arr);
            }
        }
        return $sql;
    }

    /**
     * 根据包含记录内容的数组返回一条有效的 SQL 插入记录语句
     *
     * @param array $row
     * @param string $table 要插入的数据表
     * @param string $schema
     *
     * @return string
     */
    static function getInsertSQL(& $row, $table, $schema = null)
    {
        list($holders, $values) = self::getPlaceholder($row);
        $holders = implode(',', $holders);
        $fields = self::qfields(array_keys($values));
        $table = self::qtable($table, $schema);
        $sql = "INSERT INTO {$table} ({$fields}) VALUES ({$holders})";
        return $sql;
    }
	
    /**
     * 根据包含记录内容的数组返回一条有效的 SQL 更新记录语句
     *
     * @param CoreDb $dbo
     * @param array $row
     * @param array $pk 表主键字段
     * @param string $table 要插入的数据表
     * @param string $schema
     *
     * @return string
     */
    static function getUpdateSQL(CoreDb $dbo, & $row, $pk, $table, $schema = null)
    {
        $pkv = $row[$pk];
        unset($row[$pk]);
        list($pairs, $values) = self::getPlaceholderPair($dbo,$row);
        $row[$pk] = $pkv;
        $pairs = implode(',', $pairs);
        $table = self::qtable($table, $schema);
        $pk = self::qfield($pk);
        $sql = "UPDATE {$table} SET {$pairs} WHERE {$pk} = " . $dbo->qstr($pkv);
        return $sql;
    }

    /**
     * 根据参数占位符样式,返回包含参数占位符及有效数据的数组
     *
     * @param array $inputarr
     * @param array $fields
     *
     * @return array
     */
    static function getPlaceholder(& $inputarr, $fields = null)
    {
        $holders = array();
        $values = array();
        if (is_array($fields)) {
            $fields = array_change_key_case(array_flip($fields), CASE_LOWER);
            foreach (array_keys($inputarr) as $key) {
                if (!isset($fields[strtolower($key)])) { continue; }
                $holders[] = '?';
                $values[$key] =& $inputarr[$key];
            }
        } else {
            foreach (array_keys($inputarr) as $key) {
                $holders[] = '?';
                $values[$key] =& $inputarr[$key];
            }
        }
        return array($holders, $values);
    }
    
    /**
     * 根据驱动的参数占位符样式,返回包含参数及占位符字符串对、有效数据的数组
     *
     * @param CoreDb $dbo
     * @param array $inputarr
     * @param array $fields
     *
     * @return array
     */
    static function getPlaceholderPair(CoreDb $dbo, & $inputarr, $fields = null)
    {
        $pairs = array();
        $values = array();
        if (is_array($fields)) {
            $fields = array_change_key_case(array_flip($fields), CASE_LOWER);
            foreach (array_keys($inputarr) as $key) {
                if (!isset($fields[strtolower($key)])) { continue; }
                $qkey = $this->qfield($key);
                $pairs[] = "{$qkey}=?";
                $values[$key] =& $inputarr[$key];
            }
        } else {
            foreach (array_keys($inputarr) as $key) {
                $qkey = $this->qfield($key);
                $pairs[] = "{$qkey}=?";
                $values[$key] =& $inputarr[$key];
            }
        }
        return array($pairs, $values);
    }
}

 代码例子:

 

		$dbo = CoreDB::instance(App::ini('_dsn/default'));
		$dbo->connect();
//		
//		$sqlCond = SqlHelper::parseConditions(array(
//			'name' => 'asfdfds',
//			'id' => array(array(123),'IN','OR'),
//			'role.name' => array('%ha%','Like')
//		),$dbo);
		
		$row = array(
			'name' => 'asfdfds',
			'id' => 112.5 
		);
		$sqlCond = CoreDbSqlHelper::getInsertSQL($row,'users');
		$sqlCond = CoreDbSqlHelper::bind($dbo,$sqlCond,$row);
		
		dump($dbo->getCol('show tables'),$sqlCond);
		dump($dbo->getDbUtils()->metaTables(),'metaTables');
分享到:
评论
1 楼 vb2005xu 2011-10-07  
字符串为null 返回0 mysql
mysql>select ifnull(null,0)

select if(isnull(col),0,1) as col ....

引用
1、如果为空返回0
select ifnull(null,0)
2、如果为空返回0,否则返回1
select if(isnull(col),0,1) as col.
MYSQL 中的IFNULL函数
IFNULL(expr1,expr2)
如果expr1不是NULL,IFNULL()返回expr1,否则它返回expr2。IFNULL()返回一个数字或字符串值,取决于它被使用的上下文环境。
mysql> select IFNULL(1,0);
        -> 1
mysql> select IFNULL(0,10);
        -> 0
mysql> select IFNULL(1/0,10);
        -> 10
mysql> select IFNULL(1/0,'yes');
        -> 'yes'

IF(expr1,expr2,expr3)
如果expr1是TRUE(expr1<>0且expr1<>NULL),那么IF()返回expr2,否则它返回expr3。IF()返回一个数字或字符串值,取决于它被使用的上下文。
mysql> select IF(1>2,2,3);
        -> 3
mysql> select IF(1<2,'yes','no');
        -> 'yes'
mysql> select IF(strcmp('test','test1'),'yes','no');
        -> 'no'
expr1作为整数值被计算,它意味着如果你正在测试浮点或字符串值,你应该使用一个比较操作来做。
mysql> select IF(0.1,1,0);
        -> 0
mysql> select IF(0.1<>0,1,0);
        -> 1
在上面的第一种情况中,IF(0.1)返回0,因为0.1被变换到整数值, 导致测试IF(0)。这可能不是你期望的。在第二种情况中,比较测试原来的浮点值看它是否是非零,比较的结果被用作一个整数。
CASE value WHEN [compare-value] THEN result [WHEN [compare-value] THEN result ...] [ELSE result] END
 
CASE WHEN [condition] THEN result [WHEN [condition] THEN result ...] [ELSE result] END
第一个版本返回result,其中value=compare-value。第二个版本中如果第一个条件为真,返回result。如果没有匹配的result值,那么结果在ELSE后的result被返回。如果没有ELSE部分,那么NULL被返回。
mysql> SELECT CASE 1 WHEN 1 THEN "one" WHEN 2 THEN "two" ELSE "more" END;
       -> "one"
mysql> SELECT CASE WHEN 1>0 THEN "true" ELSE "false" END;
       -> "true"
mysql> SELECT CASE BINARY "B" when "a" then 1 when "b" then 2 END;
-> NULL

相关推荐

    fleaphp核心完整代码

    9. **安全特性**:Fleaphp关注应用的安全性,内置了防止SQL注入、XSS攻击等的安全措施,同时提供输入验证和输出过滤功能,保护网站免受恶意攻击。 10. **扩展性**:Fleaphp采用模块化设计,开发者可以轻松地添加...

    fleaphp框架

    FleaPHP框架是一款轻量级的PHP开发框架,旨在提高开发效率并降低项目的维护成本。...如果你正准备使用FleaPHP进行开发,建议先熟悉其基本概念和核心组件,再结合官方文档和社区资源进行实践,以充分发挥框架的优势。

    Fleaphp开发指南

    书中还会详细介绍FleaPHP的数据库操作,包括ORM(对象关系映射)的使用,如何进行SQL查询,以及事务处理。对于初学者来说,这部分内容尤为重要,因为数据库操作是Web开发中的关键部分。 在MVC模式下,FleaPHP提供了...

    fleaphp-full-beta(开发框架).

    使用ORM(对象关系映射)技术,开发者可以通过面向对象的方式操作数据库,降低了数据访问的复杂性。 模板引擎是Fleaphp的另一大亮点,它允许开发者使用简单的语法编写视图模板,将数据和视图分离,提高代码的可复用...

    养老院管理系统:SpringBoot与Vue前后端不分离架构的设计与实现

    内容概要:本文详细介绍了基于SpringBoot和Vue开发的养老院管理系统的具体实现细节。该系统采用前后端不分离的架构,旨在快速迭代并满足中小项目的开发需求。文中涵盖了多个关键技术点,如数据库设计(组合唯一约束、触发器)、定时任务(@Scheduled、@Async)、前端数据绑定(Vue的条件渲染和动态class绑定)、权限控制(RBAC模型、自定义注解)以及报表导出(SXSSFWorkbook流式导出)。此外,还讨论了开发过程中遇到的一些常见问题及其解决方案,如CSRF防护、静态资源配置、表单提交冲突等。 适合人群:具备一定Java和前端开发经验的研发人员,尤其是对SpringBoot和Vue有一定了解的开发者。 使用场景及目标:适用于需要快速开发中小型管理系统的团队,帮助他们理解如何利用SpringBoot和Vue进行全栈开发,掌握前后端不分离架构的优势和注意事项。 其他说明:文章不仅提供了详细的代码示例和技术要点,还分享了许多实用的小技巧和避坑指南,有助于提高开发效率和系统稳定性。

    家族企业如何应对人才流失问题?.doc

    家族企业如何应对人才流失问题?

    员工关怀制度.doc

    员工关怀制度.doc

    路径规划领域中基于排序搜索的蚁群算法优化及其应用

    内容概要:本文详细探讨了对传统蚁群算法进行改进的方法,特别是在路径规划领域的应用。主要改进措施包括:采用排序搜索机制,即在每轮迭代后对所有路径按长度排序并只强化前20%的优质路径;调整信息素更新规则,如引入动态蒸发系数和分级强化策略;优化路径选择策略,增加排序权重因子;以及实现动态地图调整,使算法能够快速适应环境变化。实验结果显示,改进后的算法在收敛速度上有显著提升,在复杂地形中的表现更加稳健。 适合人群:从事路径规划研究的技术人员、算法工程师、科研工作者。 使用场景及目标:适用于需要高效路径规划的应用场景,如物流配送、机器人导航、自动驾驶等领域。目标是提高路径规划的效率和准确性,减少不必要的迂回路径,确保在动态环境中快速响应变化。 其他说明:改进后的蚁群算法不仅提高了收敛速度,还增强了对复杂环境的适应能力。建议在实际应用中结合可视化工具进行调参,以便更好地观察和优化蚂蚁的探索轨迹。此外,还需注意避免过度依赖排序机制而导致的过拟合问题。

    基于PSO算法的配电网分布式光伏选址定容优化及其Matlab实现

    内容概要:本文详细介绍了利用粒子群优化(PSO)算法解决配电网中分布式光伏系统的选址与定容问题的方法。首先阐述了问题背景,即在复杂的配电网环境中选择合适的光伏安装位置和确定合理的装机容量,以降低网损、减小电压偏差并提高光伏消纳效率。接着展示了具体的PSO算法实现流程,包括粒子初始化、适应度函数构建、粒子位置更新规则以及越界处理机制等关键技术细节。文中还讨论了目标函数的设计思路,将多个相互制约的目标如网损、电压偏差和光伏消纳通过加权方式整合为单一评价标准。此外,作者分享了一些实践经验,例如采用前推回代法进行快速潮流计算,针对特定应用场景调整权重系数,以及引入随机波动模型模拟光伏出力特性。最终实验结果显示,经过优化后的方案能够显著提升系统的整体性能。 适用人群:从事电力系统规划与设计的专业人士,尤其是那些需要处理分布式能源集成问题的研究人员和技术人员。 使用场景及目标:适用于希望深入了解如何运用智能优化算法解决实际工程难题的人士;旨在帮助读者掌握PSO算法的具体应用方法,从而更好地应对配电网中分布式光伏系统的选址定容挑战。 其他说明:文中提供了完整的Matlab源代码片段,便于读者理解和复现研究结果;同时也提到了一些潜在改进方向,鼓励进一步探索和创新。

    Prius2004永磁同步电机设计:从Excel到MotorCAD的全流程解析与实战技巧

    内容概要:本文详细介绍了丰田Prius2004永磁同步电机的设计流程,涵盖从初始参数计算到最终温升仿真的各个环节。首先利用Excel进行基本参数计算,如铁芯叠厚、定子外径等,确保设计符合预期性能。接着使用Maxwell进行参数化仿真,通过Python脚本自动化调整磁钢尺寸和其他关键参数,优化电机性能并减少齿槽转矩。随后借助橡树岭实验室提供的实测数据验证仿真结果,确保模型准确性。最后采用MotorCAD进行温升仿真,优化冷却系统设计,确保电机运行安全可靠。文中还分享了许多实用技巧,如如何正确设置材料参数、避免常见的仿真错误等。 适合人群:从事电机设计的专业工程师和技术人员,尤其是对永磁同步电机设计感兴趣的读者。 使用场景及目标:适用于希望深入了解永磁同步电机设计全过程的技术人员,帮助他们在实际工作中提高设计效率和精度,解决常见问题,优化设计方案。 其他说明:文章提供了丰富的实战经验和具体的操作步骤,强调了理论与实践相结合的重要性。同时提醒读者注意一些容易忽视的细节,如材料参数的选择和仿真模型的准确性。

    基于DSP28335的单相逆变器设计方案与实现:涵盖ADC采样、PWM控制、锁相环及保护机制

    内容概要:本文详细介绍了基于DSP28335的单相逆变器的设计与实现,涵盖了多个关键技术模块。首先,ADC采样模块用于获取输入电压和电流的数据,确保后续控制的准确性。接着,PWM控制模块负责生成精确的脉宽调制信号,控制逆变器的工作状态。液晶显示模块则用于实时展示电压、电流等重要参数。单相锁相环电路实现了电网电压的频率和相位同步,确保逆变器输出的稳定性。最后,电路保护程序提供了过流保护等功能,保障系统的安全性。每个模块都有详细的代码示例和技术要点解析。 适合人群:具备一定嵌入式系统和电力电子基础知识的研发人员,尤其是对DSP28335感兴趣的工程师。 使用场景及目标:适用于单相逆变器项目的开发,帮助开发者理解和掌握各个模块的具体实现方法,提高系统的可靠性和性能。 其他说明:文中不仅提供了具体的代码实现,还分享了许多调试经验和常见问题的解决方案,有助于读者更好地理解和应用相关技术。

    SecureCRT安装包

    SecureCRT安装包

    C# WPF MVVM架构下的大屏看板3D可视化开发指南

    内容概要:本文详细介绍了如何利用C#、WPF和MVVM模式构建一个大屏看板3D可视化系统。主要内容涵盖WPF编程设计、自定义工业控件、数据库设计、MVVM架构应用以及典型的三层架构设计。文中不仅提供了具体的代码实例,还讨论了数据库连接配置、3D模型绑定、依赖属性注册等关键技术细节。此外,文章强调了项目开发过程中需要注意的问题,如3D坐标系换算、MVVM中命令传递、数据库连接字符串加密等。 适合人群:具备一定C#编程基础,对WPF和MVVM模式有一定了解的研发人员。 使用场景及目标:适用于希望深入了解WPF和MVVM模式在实际项目中应用的开发者,特别是那些从事工业控制系统、数据可视化平台开发的专业人士。通过学习本文,读者可以掌握如何构建高效、稳定的大屏看板3D可视化系统。 其他说明:本文提供的设计方案和技术实现方式,可以帮助开发者更好地理解和应用WPF和MVVM模式,同时也能为相关领域的项目开发提供有价值的参考。

    基于java SSM 框架的酒店管理系统.zip

    基于ssm的系统设计,包含sql文件(Spring+SpringMVC+MyBatis)

    非厄米超表面双参数传感器的COMSOL建模与应用

    内容概要:本文详细介绍了利用COMSOL进行非厄米超表面双参数传感器的设计与实现。首先,通过构建超表面单元并引入虚部折射率,实现了PT对称系统的增益-损耗交替分布。接着,通过频域扫描和参数化扫描,捕捉到了复频率空间中的能级劈裂现象,并找到了奇异点(Exceptional Point),从而显著提高了传感器对微小扰动的敏感度。此外,文章探讨了双参数检测的独特优势,如解耦温度和折射率变化的能力,并展示了其在病毒检测、工业流程监控等领域的潜在应用。 适合人群:从事光学传感器研究的专业人士,尤其是对非厄米系统和COMSOL仿真感兴趣的科研人员。 使用场景及目标:适用于需要高精度、多参数检测的应用场合,如生物医学检测、环境监测等。目标是提高传感器的灵敏度和分辨率,解决传统传感器中存在的参数交叉敏感问题。 其他说明:文中提供了详细的建模步骤和代码片段,帮助读者理解和重现实验结果。同时,强调了在建模过程中需要注意的关键技术和常见问题,如网格划分、参数设置等。

    怎样健全员工福利体系.docx

    怎样健全员工福利体系.docx

    离职证明范本.doc

    离职证明范本.doc

    6538b79724855900a9c930904a302920.part6

    6538b79724855900a9c930904a302920.part6

    员工离职单.doc

    员工离职单.doc

    COMSOL中超材料异常折射仿真的关键技术与实现

    内容概要:本文详细介绍了在COMSOL中进行超材料异常折射仿真的关键技术。首先解释了异常折射现象及其产生的原因,接着通过具体代码展示了如何利用相位梯度和结构色散精确计算折射角。文中还讨论了边界条件的设置、网格划分的优化以及参数化扫描的应用。此外,提供了多个实用脚本和技巧,帮助提高仿真的精度和效率。最后强调了验证结果的重要性和一些常见的注意事项。 适合人群:从事电磁仿真研究的专业人士,尤其是对超材料和异常折射感兴趣的科研人员和技术开发者。 使用场景及目标:适用于需要深入理解和解决超材料中异常折射问题的研究项目。主要目标是掌握COMSOL中异常折射仿真的完整流程,确保仿真结果的准确性并优化计算性能。 其他说明:文章不仅提供了详细的代码示例和技术细节,还分享了许多实践经验,有助于读者更好地应对实际仿真过程中可能出现的问题。

Global site tag (gtag.js) - Google Analytics