`

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

相关推荐

    ormsql使用示例

    超级简单的android 数据库 ormsql使用示例,易懂易学

    ORM一键还原系统 v4.1.25.1 官方版.zip

    今天小编要给大家介绍一款功能强大的Windows系统备份和还原工具——ORM一键还原系统。ORM一键还原系统备份速度比ghost还快,压缩率更出色,可以全自动备份系统数据到最大分区,ORM一键还原系统同时支持所有主流...

    一款轻量级高性能的ORM,可以不用写SQL语句.zip

    一款轻量级高性能的ORM,可以不用写SQL语句,分页,事务,多结果集,模型映射,Include,导航模型属性,导航值对象属性,支持多租户,不同租户不同数据库等,支持各种复杂SQL,如:Insert Select From,Update From ...

    Dos.ORM Demo

    选择Dos.ORM的理由:  1.上手非常简单,0学习成本。使用方便,按照sql书写习惯编写C#代码。功能强大。  2.轻量级,只有一个dll文件(不到200KB),相比于EF,NHibernate这些重量级的ORM框架,实在是太小。  3....

    python使用ORM之如何调用多对多关系.docx

    python使用ORM之如何调用多对多关系.docx

    Doc.ORM的演示Demo

    说明:本Demo致力于介绍Doc.ORM的基本功能,增、删、改、查,一看就会,轻松入门。 注:内含Demo必须的《实体生成工具》,要使用Doc.ORM的功能必须用这个工具来生成实体。 Doc.ORM特点: 1)上手简单,0学习成本。...

    ORM ServiceStack.OrmLite.SqlServer 无限制破解版

    OrmLite's goal is to provide a convenient, DRY, config-free, RDBMS-agnostic typed wrapper that retains a high affinity with SQL, exposing intuitive APIs that ...ORM用开发用到的4个DLL都在资源里面了!

    java orm 框架 hibernate的最新api说明文档.非中文,但介绍很详细.chm版.

    java orm 框架 hibernate的最新api说明文档.非中文,但介绍很详细.chm版. java orm 框架 hibernate的最新api说明文档.非中文,但介绍很详细.chm版.

    org.springframework.orm-3.0.0.M1.jar

    org.springframework.orm-3.0.0.M1.jar

    pring对JDBC和orm的支持共10页.pdf.zip

    pring对JDBC和orm的支持共10页.pdf.zip

    org.springframework.orm.hibernate4.HibernateOptimisticLockingFailureException

    NULL 博文链接:https://hw1287789687.iteye.com/blog/1967701

    好用的ORM组件,DB的orm功能集合.zip

    最好用的ORM组件,DB的orm功能集合,将数据库的复杂造作进行处理,支持自定义查询条件编写以及todata智能函数,支持mysql mariadb sqlserver postgres opengauss sqllite oracle

    金蝶BOSV6.1_业务组件API参考手册

    Packages ...com.kingdee.bos.sql.shell.trace.client com.kingdee.bos.sql.shell.trace.server com.kingdee.bos.sql.shell.xa com.kingdee.bos.sql.util com.kingdee.bos.stconvert ...

    自己写的orm辅助类

    自己写的orm辅助类 .定义实体的特性(如对应的表名 主键名 属性对应的列名 实体和数据库对象的相互转换 简单实体的 CRUD操作 QQ交流群60168829 欢迎C# ASP.NET 和SQL新手 老手 高手加入 谢谢

    自己动手写轻量级ORM(C#)

    我的目标就是在数据库中建个表,在项目中写个模型,然后用上这个ORM工具,就能实现数据库的基本增删改查。 上面的代码实现了最基本的单条纪录的增删改查,其中最麻烦的是查,最简单的是删。 其中的db变量是数据库...

    Dikeko.ORM.Core.zip

    Dikeko.ORM.Core是一个简单的.NET轻量级的ORM,目前仅支持SqlServer数据库。 ## 源码 .NET 版:https://github.com/cnsmallant/Dikeko.ORM .NET CORE版:https://github.com/cnsmallant/Dikeko.ORM.Core PM&gt;...

    stream-node-orm, NodeJS客户端生成活动使用 GetStream.io 提供&流.zip

    stream-node-orm, NodeJS客户端生成活动使用 GetStream.io 提供&流 流 Node.js stream-node-orm 是用于流管理的node.js ( Mongoose &水线) 客户端。你可以在 https://getstream.io/get_started 注册一个流

Global site tag (gtag.js) - Google Analytics