包 | system.db.schema.mssql |
---|---|
继承 | class CMssqlCommandBuilder » CDbCommandBuilder » CComponent |
版本 | $Id: CMssqlCommandBuilder.php 3515 2011-12-28 12:29:24Z mdomba $ |
源码 |
公共属性
属性 | 类型 | 描述 | 定义在 |
---|---|---|---|
dbConnection | CDbConnection | database connection. | CDbCommandBuilder |
schema | CDbSchema | the schema for this command builder. | CDbCommandBuilder |
公共方法
方法 | 描述 | 定义在 |
---|---|---|
__call() | 如果类中没有调的方法名,则调用这个方法。 | CComponent |
__construct() | CDbCommandBuilder | |
__get() | 返回一个属性值、一个事件处理程序列表或一个行为名称。 | CComponent |
__isset() | 检查一个属性是否为null。 | CComponent |
__set() | 设置一个组件的属性值。 | CComponent |
__unset() | 设置一个组件的属性为null。 | CComponent |
applyCondition() | Alters the SQL to apply WHERE clause. | CDbCommandBuilder |
applyGroup() | Alters the SQL to apply GROUP BY. | CDbCommandBuilder |
applyHaving() | Alters the SQL to apply HAVING. | CDbCommandBuilder |
applyJoin() | Alters the SQL to apply JOIN clause. | CDbCommandBuilder |
applyLimit() | This is a port from Prado Framework. | CMssqlCommandBuilder |
applyOrder() | Alters the SQL to apply ORDER BY. | CDbCommandBuilder |
asa() | 返回这个名字的行为对象。 | CComponent |
attachBehavior() | 附加一个行为到组件。 | CComponent |
attachBehaviors() | 附加一个行为列表到组件。 | CComponent |
attachEventHandler() | 为事件附加一个事件处理程序。 | CComponent |
bindValues() | Binds parameter values for an SQL command. | CDbCommandBuilder |
canGetProperty() | 确定属性是否可读。 | CComponent |
canSetProperty() | 确定属性是否可写。 | CComponent |
createColumnCriteria() | Creates a query criteria with the specified column values. | CDbCommandBuilder |
createCountCommand() | 为一个表创建COUNT(*)语句。 | CMssqlCommandBuilder |
createCriteria() | Creates a query criteria. | CDbCommandBuilder |
createDeleteCommand() | 创建DELETE语句。 | CMssqlCommandBuilder |
createFindCommand() | 为一个表创建SELECT语句。 | CMssqlCommandBuilder |
createInCondition() | Generates the expression for selecting rows of specified primary key values. | CDbCommandBuilder |
createInsertCommand() | Creates an INSERT command. | CDbCommandBuilder |
createPkCondition() | Generates the expression for selecting rows of specified primary key values. | CDbCommandBuilder |
createPkCriteria() | Creates a query criteria with the specified primary key. | CDbCommandBuilder |
createSearchCondition() | Generates the expression for searching the specified keywords within a list of columns. | CDbCommandBuilder |
createSqlCommand() | Creates a command based on a given SQL statement. | CDbCommandBuilder |
createUpdateCommand() | 创建UPDATE语句。 | CMssqlCommandBuilder |
createUpdateCounterCommand() | 创建递增或递减特定列的UPDATE语句。 | CMssqlCommandBuilder |
detachBehavior() | 从组件中分离一个行为。 | CComponent |
detachBehaviors() | 从组件中分离所有行为。 | CComponent |
detachEventHandler() | 分离一个存在的事件处理程序。 | CComponent |
disableBehavior() | 禁用一个附加行为。 | CComponent |
disableBehaviors() | 禁用组件附加的所有行为。 | CComponent |
enableBehavior() | 启用一个附加行为。 | CComponent |
enableBehaviors() | 启用组件附加的所有行为。 | CComponent |
evaluateExpression() | 计算一个PHP表达式,或根据组件上下文执行回调。 | CComponent |
getDbConnection() | 返回database connection. | CDbCommandBuilder |
getEventHandlers() | 返回一个事件的附加处理程序列表。 | CComponent |
getLastInsertID() | Returns the last insertion ID for the specified table. | CDbCommandBuilder |
getSchema() | 返回the schema for this command builder. | CDbCommandBuilder |
hasEvent() | 确定一个事件是否定义。 | CComponent |
hasEventHandler() | 检查事件是否有附加的处理程序。 | CComponent |
hasProperty() | 确定属性是否被定义。 | CComponent |
raiseEvent() | 发起一个事件。 | CComponent |
受保护方法
方法 | 描述 | 定义在 |
---|---|---|
checkCriteria() | 检查使用offset或limit的查询条件是否有order by子句。 | CMssqlCommandBuilder |
createCompositeInCondition() | 生成根据指定符合键值选择行的表达式。 | CMssqlCommandBuilder |
ensureTable() | Checks if the parameter is a valid table schema. | CDbCommandBuilder |
findOrdering() | 基于简化语法:http://msdn2.microsoft.com/en-us/library/aa259187(SQL.80).aspx | CMssqlCommandBuilder |
joinOrdering() | CMssqlCommandBuilder | |
reverseDirection() | CMssqlCommandBuilder | |
rewriteLimitOffsetSql() | 为MSSQL数据库重写SQL以应用$limit > 0和$offset > 0。 | CMssqlCommandBuilder |
方法详细
public string applyLimit(string $sql, integer $limit, integer $offset)
| ||
$sql | string | SQL查询字符串 |
$limit | integer | 最大行数,若为-1则忽略limit。 |
$offset | integer | 行偏移,若为-1则忽略offset。 |
{return} | string | 带有limit和offset的SQL。 |
public function applyLimit($sql, $limit, $offset)
{
$limit = $limit!==null ? intval($limit) : -1;
$offset = $offset!==null ? intval($offset) : -1;
if ($limit > 0 && $offset <= 0) //just limit
$sql = preg_replace('/^([\s(])*SELECT( DISTINCT)?(?!\s*TOP\s*\()/i',"\\1SELECT\\2 TOP $limit", $sql);
else if($limit > 0 && $offset > 0)
$sql = $this->rewriteLimitOffsetSql($sql, $limit,$offset);
return $sql;
}
This is a port from Prado Framework.
重写父类的实现。改变SQL以应用$limit和$offset。
应用带有offset的limit是根据SQL语句结构的
许多假设通过动态修改SQL实现的。
根据下面地址的附注完成修改:
http://troels.arvin.dk/db/rdbms/#select-limit-offset
SELECT * FROM (
SELECT TOP n * FROM (
SELECT TOP z columns -- (z=n+skip)
FROM tablename
ORDER BY key ASC
) AS FOO ORDER BY key DESC -- ('FOO' may be anything)
) AS BAR ORDER BY key ASC -- ('BAR' may be anything)
Regular expressions are used to alter the SQL query. The resulting SQL query
may be malformed for complex queries. The following restrictions apply
- In particular, commas should NOT be used as part of the ordering expression or identifier. Commas must only be used for separating the ordering clauses.
- In the ORDER BY clause, the column name should NOT be be qualified with a table name or view name. Alias the column names or use column index.
- No clauses should follow the ORDER BY clause, e.g. no COMPUTE or FOR clauses.
protected CDbCrireria checkCriteria(CMssqlTableSchema $table, CDbCriteria $criteria)
| ||
$table | CMssqlTableSchema | 表schema |
$criteria | CDbCriteria | 查询条件 |
{return} | CDbCrireria | 修改后的查询条件 |
protected function checkCriteria($table, $criteria)
{
if ($criteria->offset > 0 && $criteria->order==='')
{
$criteria->order=is_array($table->primaryKey)?implode(',',$table->primaryKey):$table->primaryKey;
}
return $criteria;
}
检查使用offset或limit的查询条件是否有order by子句。 重写父类的实现以检查带有offset的查询中order by子句是否已指定。 若未指定order by子句,则按主键排序。
protected string createCompositeInCondition(CDbTableSchema $table, array $values, string $prefix)
| ||
$table | CDbTableSchema | 表schema |
$values | array | 要选择的主键值列表 |
$prefix | string | 列前缀(以.结束) |
{return} | string | 选择的表达式。 |
protected function createCompositeInCondition($table,$values,$prefix)
{
$vs=array();
foreach($values as $value)
{
$c=array();
foreach($value as $k=>$v)
$c[]=$prefix.$table->columns[$k]->rawName.'='.$v;
$vs[]='('.implode(' AND ',$c).')';
}
return '('.implode(' OR ',$vs).')';
}
生成根据指定符合键值选择行的表达式。
public CDbCommand createCountCommand(CDbTableSchema $table, CDbCriteria $criteria, string $alias='t')
| ||
$table | CDbTableSchema | 表元数据。 |
$criteria | CDbCriteria | 查询条件。 |
$alias | string | 主表的别名。默认为‘t’。 |
{return} | CDbCommand | 查询语句。 |
public function createCountCommand($table,$criteria,$alias='t')
{
$criteria->order='';
return parent::createCountCommand($table, $criteria,$alias);
}
为一个表创建COUNT(*)语句。 重写父类的实现以移除条件里的order子句(如果它存在)。
$table | CDbTableSchema | 表元数据 |
$criteria | CDbCriteria | 查询条件 |
{return} | CDbCommand | delete语句。 |
public function createDeleteCommand($table,$criteria)
{
$criteria=$this->checkCriteria($table, $criteria);
return parent::createDeleteCommand($table, $criteria);
}
创建DELETE语句。 重写父类的实现以检查带有offset的查询中order by子句是否已指定。
public CDbCommand createFindCommand(CDbTableSchema $table, CDbCriteria $criteria, string $alias='t')
| ||
$table | CDbTableSchema | 表元数据。 |
$criteria | CDbCriteria | 查询条件。 |
$alias | string | 主表的别名。默认为‘t’。 |
{return} | CDbCommand | 查询语句。 |
public function createFindCommand($table,$criteria,$alias='t')
{
$criteria=$this->checkCriteria($table,$criteria);
return parent::createFindCommand($table,$criteria,$alias);
}
为一个表创建SELECT语句。 重写父类的实现以检查带有offset的查询中order by子句是否已指定。
$table | CDbTableSchema | 表元数据。 |
$data | array | 要更新的列(名=>值) |
$criteria | CDbCriteria | 查询条件 |
{return} | CDbCommand | update语句。 |
public function createUpdateCommand($table,$data,$criteria)
{
$criteria=$this->checkCriteria($table,$criteria);
$fields=array();
$values=array();
$bindByPosition=isset($criteria->params[0]);
$i=0;
foreach($data as $name=>$value)
{
if(($column=$table->getColumn($name))!==null)
{
if ($table->sequenceName !== null && $column->isPrimaryKey === true) continue;
if ($column->dbType === 'timestamp') continue;
if($value instanceof CDbExpression)
{
$fields[]=$column->rawName.'='.$value->expression;
foreach($value->params as $n=>$v)
$values[$n]=$v;
}
else if($bindByPosition)
{
$fields[]=$column->rawName.'=?';
$values[]=$column->typecast($value);
}
else
{
$fields[]=$column->rawName.'='.self::PARAM_PREFIX.$i;
$values[self::PARAM_PREFIX.$i]=$column->typecast($value);
$i++;
}
}
}
if($fields===array())
throw new CDbException(Yii::t('yii','No columns are being updated for table "{table}".',
array('{table}'=>$table->name)));
$sql="UPDATE {$table->rawName} SET ".implode(', ',$fields);
$sql=$this->applyJoin($sql,$criteria->join);
$sql=$this->applyCondition($sql,$criteria->condition);
$sql=$this->applyOrder($sql,$criteria->order);
$sql=$this->applyLimit($sql,$criteria->limit,$criteria->offset);
$command=$this->getDbConnection()->createCommand($sql);
$this->bindValues($command,array_merge($values,$criteria->params));
return $command;
}
创建UPDATE语句。 重写父类的实现因为MSSQL不希望更新标识列。
public CDbCommand createUpdateCounterCommand(CDbTableSchema $table, CDbCriteria $counters, array $criteria)
| ||
$table | CDbTableSchema | 表元数据 |
$counters | CDbCriteria | 查询条件 |
$criteria | array | 要更新的计数器(列名为索引的递增或递减计数器)。 |
{return} | CDbCommand | update语句。 |
public function createUpdateCounterCommand($table,$counters,$criteria)
{
$criteria=$this->checkCriteria($table, $criteria);
return parent::createUpdateCounterCommand($table, $counters, $criteria);
}
创建递增或递减特定列的UPDATE语句。 重写父类的实现以检查带有offset的查询中order by子句是否已指定。
protected array findOrdering(string $sql)
| ||
$sql | string | $sql |
{return} | array | 排序表达式作为键及排序方向作为值。 |
protected function findOrdering($sql)
{
if(!preg_match('/ORDER BY/i', $sql))
return array();
$matches=array();
$ordering=array();
preg_match_all('/(ORDER BY)[\s"\[](.*)(ASC|DESC)?(?:[\s"\[]|$|COMPUTE|FOR)/i', $sql, $matches);
if(count($matches)>1 && count($matches[2]) > 0)
{
$parts = explode(',', $matches[2][0]);
foreach($parts as $part)
{
$subs=array();
if(preg_match_all('/(.*)[\s"\]](ASC|DESC)$/i', trim($part), $subs))
{
if(count($subs) > 1 && count($subs[2]) > 0)
{
$name='';
foreach(explode('.', $subs[1][0]) as $p)
{
if($name!=='')
$name.='.';
$name.='[' . trim($p, '[]') . ']';
}
$ordering[$name] = $subs[2][0];
}
//else what?
}
else
$ordering[trim($part)] = 'ASC';
}
}
// replacing column names with their alias names
foreach($ordering as $name => $direction)
{
$matches = array();
$pattern = '/\s+'.str_replace(array('[',']'), array('\[','\]'), $name).'\s+AS\s+(\[[^\]]+\])/i';
preg_match($pattern, $sql, $matches);
if(isset($matches[1]))
{
$ordering[$matches[1]] = $ordering[$name];
unset($ordering[$name]);
}
}
return $ordering;
}
基于简化语法:http://msdn2.microsoft.com/en-us/library/aa259187(SQL.80).aspx
protected string joinOrdering(array $orders, string $newPrefix)
| ||
$orders | array | 从findOrdering获得的order信息 |
$newPrefix | string | 加在排序列的新表前缀。 |
{return} | string | 连接后的order表达式。 |
protected function joinOrdering($orders, $newPrefix)
{
if(count($orders)>0)
{
$str=array();
foreach($orders as $column => $direction)
$str[] = $column.' '.$direction;
$orderBy = 'ORDER BY '.implode(', ', $str);
return preg_replace('/\s+\[[^\]]+\]\.(\[[^\]]+\])/i', ' '.$newPrefix.'.\1', $orderBy);
}
}
protected array reverseDirection(array $orders)
| ||
$orders | array | 原始order信息 |
{return} | array | 反转方向的order信息。 |
protected function reverseDirection($orders)
{
foreach($orders as $column => $direction)
$orders[$column] = strtolower(trim($direction))==='desc' ? 'ASC' : 'DESC';
return $orders;
}
protected sql rewriteLimitOffsetSql(string $sql, integer $limit, integer $offset)
| ||
$sql | string | SQL查询 |
$limit | integer | $limit > 0 |
$offset | integer | $offset > 0 |
{return} | sql | 应用limit和offset的修改后SQL查询 |
protected function rewriteLimitOffsetSql($sql, $limit, $offset)
{
$fetch = $limit+$offset;
$sql = preg_replace('/^([\s(])*SELECT( DISTINCT)?(?!\s*TOP\s*\()/i',"\\1SELECT\\2 TOP $fetch", $sql);
$ordering = $this->findOrdering($sql);
$orginalOrdering = $this->joinOrdering($ordering, '[__outer__]');
$reverseOrdering = $this->joinOrdering($this->reverseDirection($ordering), '[__inner__]');
$sql = "SELECT * FROM (SELECT TOP {$limit} * FROM ($sql) as [__inner__] {$reverseOrdering}) as [__outer__] {$orginalOrdering}";
return $sql;
}
为MSSQL数据库重写SQL以应用$limit > 0和$offset > 0。 参见http://troels.arvin.dk/db/rdbms/#select-limit-offset