可以在PDO准备语句中参数化哪些令牌?

时间:2009-10-25 23:52:26

标签: php pdo prepared-statement

我正在使用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

那么,准备好的陈述中哪些内容应该有效?我可以'参数化'字段名称,表名等吗?

3 个答案:

答案 0 :(得分:10)

您无法参数化IN子句中的表名,列名或任何内容(感谢pointing out the IN clause restriction的c0r0ner)。

请参阅this question,然后this comment in the PHP manual

答案 1 :(得分:1)

@Josh Leitzel

这种想法非常严格(在我看来,这只是一个懒得实现强大解决方案的借口),特别是对于在数据库中表达的动态树结构。

考虑以下示例:

我的项目有一个逻辑结构:

公司层级以实体表示。在一般情况下,每个实体都可以作为层次结构的成员或作为层次结构的特定级别的成员来处理。层次结构本身在表中定义为单个树分支,如下所示:

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
)

在分区级别的实体将division_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);
}

导致代码更清晰,只有一个预备语句。

整个示例不使用任何用户输入。

需要考虑的事情。

答案 2 :(得分:1)

您也无法在IN子句中对任何内容进行参数化。