`

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使用示例

    ORMSQL就是实现了这种技术,允许开发者用Java对象的方式处理数据库操作,而无需编写大量的SQL语句。 ### 安装ORMSQL 在Android项目中使用ORMSQL,首先需要在`build.gradle`文件中添加依赖。通常,你需要在...

    手写orm

    在自写ORM的过程中,通常会涉及以下关键知识点: 1. **数据模型设计**:首先,需要定义数据模型类,这些类代表数据库中的表,类的属性对应表的字段。通常会用到注解或者配置文件来定义映射关系。 2. **数据库连接...

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

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

    ORM思想的深入学习ORM.zip

    1. **ORM思想**:ORM的核心思想是通过在应用程序中的对象与数据库表之间建立映射关系,使得开发者可以使用面向对象的方式来操作数据库,而无需直接编写SQL语句。这种映射关系通常通过XML配置文件或者注解来定义,...

    Dos.ORM Demo

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

    sql注入工具.rar

    6. 防护措施:避免SQL注入的最有效方法是采用预编译语句(如参数化查询)、存储过程,或者使用ORM(对象关系映射)框架。同时,应进行输入验证,限制数据库用户的权限,并定期进行安全审计。 7. 安全最佳实践:除了...

    PyPI 官网下载 | sql-orm-1.1.7.tar.gz

    《Python SQL ORM库详解——基于sql-orm-1.1.7.tar.gz》 Python SQL Object-Relational Mapping(ORM)是一种技术,它允许开发者使用面向对象的方式来操作数据库,而无需关心底层SQL语言的细节。在Python的世界里,...

    Doc.ORM的演示Demo

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

    Moon.Orm下载

    Moon.Orm是一个专门为.NET开发者设计的轻量级ORM(对象关系映射)框架,它具有强大的功能和良好的可扩展性,能够支持多种不同的数据库系统,包括但不限于MySQL、SQL Server、Oracle、SQLite等。ORM框架的主要目标是...

    sql优化工具.zip

    SQL优化是数据库管理中的关键环节,它涉及到查询性能的提升,进而影响整个应用程序的响应速度。SQL Server作为一款广泛使用的数据库管理系统,其性能优化尤为重要。"SQL Optimizer for SQL Server"是一款专门针对SQL...

    Dos.ORM.Demo调用测试案例.zip

    7. **代码示例**:压缩包内的代码示例可能包含如何初始化数据库连接、创建DbContext实例、执行SQL语句(如使用Query、Insert、Update、Delete等方法)等内容,这些都是学习和使用Dos.ORM的关键。 通过学习这个Demo...

    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都在资源里面了!

    针对自己的框架 写的 orm自动工具

    【标题】:“针对自己的框架 写的 ORM 自动工具” ORM(Object-Relational Mapping,对象关系映射)是一种编程技术,用于将关系数据库的数据映射到对象上,使得开发者可以使用面向对象的方式来操作数据库,而无需...

    SqlSugar ORM工具箱2.2.7z

    SqlSugar是一个在中国广泛使用的对象关系映射(ORM)框架,它极大地简化了.NET开发者与数据库交互的工作。ORM工具箱如SqlSugar,通过提供高级抽象,允许程序员以面向对象的方式处理数据库操作,而无需直接编写SQL...

    C#不写SQL语句的数据库操作

    本主题将探讨如何在C#中进行不写SQL语句的数据库操作,实现对数据的增删改查功能。 首先,我们可以利用ORM(Object-Relational Mapping)框架来避免直接编写SQL。ORM框架允许开发者用面向对象的方式来操作数据库,...

    AndroidInject增加sqlite3数据库映射注解(ORM)-IT计算机-毕业设计.zip

    - 数据库操作:通过ORM提供的API,如insert()、update()、delete()、query()等方法,进行数据的增删改查,无需关心底层SQL语句。 在这个项目中,"AndroidInject"可能是一个注入框架,比如Dagger或Koin,用于依赖...

    java的sql解析器jsqlparser

    Java的SQL解析器JSQLPaser是一个强大的开源库,专门设计用于处理SQL语句的解析工作。这个库允许开发者分析SQL语句的结构,提取出其中的关键元素,如列名、表名、别名以及查询条件,从而在Java应用程序中实现对SQL的...

    Syngress.SQL.Injection.Attacks.and.Defense.2nd.Edition.1597499633.zip

    4. **防御策略**:介绍最佳实践,如参数化查询、输入验证、安全编码原则,以及使用ORM(对象关系映射)框架来减少直接SQL操作的风险。 5. **检测与响应**:讲解如何通过日志分析、入侵检测系统和渗透测试发现潜在的...

    仿orm自动生成分页SQL分享

    【ORM与分页SQL】 ORM(Object-Relational Mapping)是一种编程技术,用于将关系数据库的数据映射到对象上,使得开发人员可以使用面向对象的方式来操作数据库。在处理大量数据时,分页查询是非常常见的需求,它能...

Global site tag (gtag.js) - Google Analytics