复杂查询,重复参数在PHP PDO中不起作用

时间:2013-12-02 18:51:43

标签: php mysql pdo subquery

我对选举数据库有一些复杂的查询(使用子查询),我试图获得某个职位的每个特定候选人的投票数。

votes表的标题是: id(PRIMARY KEY),时间戳,pos1,pos2,...,pos6 其中 pos1-pos6 是职位名称。投票选票成为此表中的新行,其中所选候选人的成员编号(候选人链接到数据库中“成员资格”表中的个人资料)存储为每个职位的值。因此,例如,数据库中的一行可能如下所示(除了实际的6个位置名称):

id    timestamp    pos1   pos2   pos3 (and so on)
=================================================
6     1386009129   345    162    207

我希望使用PHP PDO获取每个职位的结果,列出候选人姓名的每个职位以及他们为此职位收到的投票数。因此原始数据库结果应显示为(例如“pos1”):

name         votecount
======================
Joe Smith    27
Jane Doe     45

我有一个原始的SQL查询,我可以成功地使用它来获取这些结果,查询是(使pos1成为实际的列/位置名称President):

SELECT (SELECT fullname FROM membership memb WHERE member_id=`President`) name, count(`President`) votecount FROM `election_votes` votes GROUP BY `President`

正如您所看到的,位置名称(President,此处)在查询中重复了3次。这似乎导致PHP PDO代码中的问题。我的代码如下:

$position = "President";   // set in earlier code as part of a loop

$query = "SELECT (SELECT fullname FROM membership memb WHERE member_id=:pos1) name, count(:pos2) votecount FROM `election_votes` votes GROUP BY :pos3";
$query2 = "SELECT (SELECT fullname FROM membership memb WHERE member_id=$position) name, count($position) votecount FROM `election_votes` votes GROUP BY $position";  // NOT SAFE!

$STH = $DBH->prepare($query);
$STH->bindParam(':pos1', $position);
$STH->bindParam(':pos2', $position);
$STH->bindParam(':pos3', $position);
$STH->execute();

while($row = $STH->fetch(PDO::FETCH_ASSOC)) {
  print_r($row);
  // I'd like to do other things with these results
}

当我使用查询$query运行此操作时,我不会按需要获得每人的结果。我的输出是:

Array
(
    [name] => 
    [votecount] => 47
)

其中 47 是投票的总投票数而不是每个候选人的数组,包括他们的姓名和投票数(超出总数)。但是,如果我使用明显不安全的$query2,它只是将$position的值插入查询字符串三次,我会得到我想要的结果。

上面的PHP代码有问题吗?我在PDO做的事情是不可能的(我希望不是!)?我的底层数据库是MySQL 5.5.32。我甚至尝试用?未命名的参数替换三个命名参数,并将数组array($position, $position, $position)传递给$STH->execute()方法,但没有取得更大的成功。

1 个答案:

答案 0 :(得分:1)

您的查询并不复杂。我认为部分困惑在于您没有正确构建基本SQL。您试图将“总统”视为值和列。最终的SQL看起来应该是这样的:

SELECT 
    `fullname` AS `name`, 
    COUNT(`id`) AS `votecount` 
FROM 
    `election_votes` AS `votes` 
LEFT JOIN 
    `membership` AS `memb` ON `member_id` = `president` 
GROUP BY 
    `pos1`

您将election_votes表加入membership表,其中pos1列中的值等于列member_id中的值。

注意:您不能在PDO(Can PHP PDO Statements accept the table or column name as parameter?)中使用带有表名和列名的参数,因此您必须手动转义这些参数。

/**
 *  Map positions/columns manually.  Never use user-submitted data for table/column names
 */
$positions = array('President' => 'pos1', 'Vice-president' => 'pos2');

$column = $positions['President'];

您应该能够在PDO中重写您的查询:

/**
 *  Get election results for all candidates
 */
$sql = "SELECT 
            `fullname` AS `name`, 
            COUNT(`id`) AS `votecount` 
        FROM 
            `election_votes` AS `votes` 
        LEFT JOIN 
            `membership` AS `memb` ON `member_id` = `".$column."` 
        GROUP BY 
            `".$column."`";

正如您所看到的,PDO中没有任何内容可以与此特定查询绑定。您使用BindParam来过滤WHERE子句中出现的值(同样:不是表/列名称)。例如:

/**
 *  Get election results for person named 'Homer Simpson'
 */
$sql = "SELECT 
            `fullname` AS `name`, 
            COUNT(`id`) AS `votecount` 
        FROM 
            `election_votes` AS `votes` 
        LEFT JOIN 
            `membership` AS `memb` ON `member_id` = `".$column."` 
        WHERE 
            `fullname` = :fullname:
        GROUP BY 
            `".$column."`";
$STH = $DBH->prepare($sql);
$STH->bindParam(':fullname', 'Homer Simpson');