我在玩PHP / PDO中准备好的语句。基本查询工作正常,将值传递给WHERE子句:
$stmt = $db->prepare( 'SELECT title FROM episode WHERE id=:id' ); $stmt->bindParam( ':id', $id, PDO::PARAM_INT ); $id = 5; $stmt->execute();
但是我有一种情况,我需要为字段名称传递变量。此查询(具有适当的绑定)可以正常工作:
SELECT :field FROM episode WHERE id=:id
这给出了一个错误:
SELECT title FROM :field WHERE id=:id
这个不会给出错误,但是不会返回任何行:
SELECT title FROM episode WHERE :field=:id
那么,哪些事情应该在准备好的语句中起作用?我可以“参数化”字段名称,表名称等吗?
表名和列名不能用PDO中的参数替换。
在这种情况下,您只需要手动过滤和清理数据。一种实现方法是将简写参数传递给将动态执行查询的函数,然后使用一条switch()语句创建要用于表名或列名的有效值白名单。这样,就不会有用户输入直接进入查询。因此,例如:
switch()
function buildQuery( $get_var ) { switch($get_var) { case 1: $tbl = 'users'; break; } $sql = "SELECT * FROM $tbl"; }
通过不保留默认大小写或使用返回错误消息的默认大小写,可以确保仅使用要使用的值。
这种想法非常严格(在我看来,这只是太懒而无法实施可靠的解决方案的借口),尤其是对于数据库中表示的动态树结构。
考虑以下示例:
我的项目具有逻辑结构:
公司层次结构以实体表示。在一般情况下,每个实体都可以被视为层次结构的成员或特定层次结构的成员。层次结构本身在表中定义为单个树枝,如下所示:
entity_structure ( id name parent_entity_structure_id );
实体本身表示为:
entities ( id name entity_structure_id parent_id );
为了易于使用,我构建了一种算法,可创建树的平面视图。以下具体示例说明了我的意思:
SELECT * FROM entity_structure; id | name | entity_structure_parent_id ----------------------------------------------------------- 1 | Company | null (special one that always exists) 2 | Division | 1 3 | Area | 2 4 | Store | 3
这将导致生成以下平面表示形式:
entity_tree ( entity_id division_id area_id store_id )
处于划分级别的实体的divide_id,area_id和store_id为NULL,区域area_id和store_id为NULL,等等。
这样做的好处是,您可以使用类似于以下内容的语句来查询部门的所有子级:
SELECT * FROM entity_tree WHERE division_id = :division_id;
但是,这假定我知道我要查询的实体的结构级别。这样做很好:
SELECT * FROM entity_tree WHERE :structure = :entity_id;
我知道弄清单个实体的结构级别并不难,但是假设我正在遍历可能并非都处于同一级别的实体集合。现在,我必须为层次结构的每个级别构建一个单独的查询,但是如果可以参数化字段,则可以执行以下操作:
$children = array(); $stmt = $pdo->prepare('SELECT entity_id FROM entity_tree WHERE :structure = :entityId'); foreach ($entities AS $entity) { $stmt->execute(array( ':structure' = $entity->getEntityStructureId(), ':entityId' = $entity->getId() )); $children[$entity->getId()] = $stmt->fetchAll(PDO::FETCH_COLUMN); }
导致代码更简洁,只有一个准备好的语句。
整个示例不使用任何用户输入。
只是要考虑的事情。