使用PDO进行动态列表查询

时间:2012-12-05 21:17:58

标签: php mysql pdo

我一直在打这个,但我似乎无法自己解决这个问题。我已经遇到了PDO和MYSQL的已知问题/功能,它们引导我使用下面的代码。

我在数据库中搜索符合多个条件的标签ID。我遇到了一个MYSQL错误,似乎迫使我在第二次处理之前使用临时表来保存我的一些结果。 PDO不会让我在第一次执行后引用临时表(我可能做错了)或让我准备多个语句,因此它引导我为这个调用创建一个存储过程。在最近的一些测试之后,我发现了PDO的另一个问题/特性,它不允许我以我正在寻找的方式调用存储过程。

这是SP:

DELIMITER $$
CREATE PROCEDURE sp_searchPrevArticles(IN tagList VARCHAR(255), IN firstArticle INT(10))
BEGIN

CREATE TEMPORARY TABLE at_results (
id INTEGER(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
article_id INTEGER(10) NOT NULL,
datetime DATETIME NOT NULL,
common_tags INTEGER NOT NULL)
SELECT at.article_id, art.datetime, Count(at.article_id) AS common_tags
FROM article_tags AS at 
INNER JOIN articles AS art ON at.article_id = art.article_id
WHERE at.tag_id IN (tagList)
GROUP BY at.article_id
ORDER BY common_tags DESC, art.datetime DESC;

CREATE TEMPORARY TABLE at_article
SELECT id
FROM at_results
WHERE article_id = firstArticle;

SELECT article_id
FROM at_results, at_article
WHERE at_results.id < at_article.id
ORDER BY at_results.id DESC;

END $$
DELIMITER ;

无法正常工作的参数是tagList。我用

创建tagList
foreach ($tag_ids as $tag) {
    $tag_list = ($tag_list . $tag . ",");
}
$tag_list = rtrim($tag_list, ',');

这给了我一个动态的标签ID列表。为此,我们可以说“4,3,2,1”。当我在没有PDO的情况下运行此SP时,它返回正确的数据集。任何时候我用PDO运行它只会给我列表中的第一个标签。在这种情况下,它只返回“4”的结果。

我的PDO电话是:

$sql = "CALL sp_searchPrevArticles(:tag_list, :first_article)";
$tag_sth = $this->db->prepare($sql);
$tag_sth->execute(array(':tag_list' => $tag_list, ':first_article' => $first_article));
$tag_sth->setFetchMode(PDO::FETCH_ASSOC);
$data = $tag_sth->fetchAll();

当我继续以这种方式写作时,我不禁想到我为自己制造的一切都更加困难。我还在学习,这是迄今为止我提出的最佳解决方案。任何人都知道如何正常运作?

感谢任何帮助。

谢谢!

2 个答案:

答案 0 :(得分:3)

PDO完全不能正确处理列表,除非它们是静态长度并且定义为IN(?,?,?,?)。我绕过它:

<?php
$list = '1,2,3,4';
$params = array('a', 'b');

$query = "SELECT * FROM TABLE WHERE thing = ? AND other = ? AND more IN (%s)";
$query = sprintf($query, $list);

$rs = $dbh->doQuery($query, $params);

这是kludge-y,但我还没有看到PHP关于动态长度列表的真正解决方案。

我只是想到了一个更丑陋,但更符合要求的方式来写这个:

<?php
$list = array(1,2,3,4); //pretend this is dynamic, and always has at least one member
$params = array('a','b');
$query = "SELECT * FROM TABLE WHERE thing = ? AND other = ? AND more IN (%s)";

$placeholders = '?';
for($i=0; $i<count($list); $i++) {
    $placeholders .= ',?';
}
$query = sprintf($query, $placeholders);
$params = array_merge($params, $list);

$rs = $dbh->doQuery($query, $params);

这是粗略和一般的,但你明白了。除非你真的承诺绝对参数化所有我喜欢的第一个片段。

答案 1 :(得分:0)

我会在这里发布这个给其他寻找答案的人。 Sammitch的想法非常好,但我无法让它与Stored Procedures一起工作。我最终将变量传递给SQL并在存储过程中构建完整查询。我不确定这是否是处理此问题的最佳方法,但它目前正在运作。我可以担心以后会调整它。

DROP PROCEDURE IF EXISTS sp_searchPrevArticles;

DELIMITER $$
CREATE PROCEDURE sp_searchPrevArticles(IN tagList VARCHAR(255), IN firstArticle INT(10))
BEGIN

SET @sql1 = CONCAT('
CREATE TEMPORARY TABLE at_results (
id INTEGER(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
article_id INTEGER(10) NOT NULL,
datetime DATETIME NOT NULL,
common_tags INTEGER NOT NULL)
SELECT at.article_id, art.datetime, Count(at.article_id) AS common_tags
FROM article_tags AS at 
INNER JOIN articles AS art ON at.article_id = art.article_id
WHERE at.tag_id IN (', tagList ,')
GROUP BY at.article_id
ORDER BY common_tags DESC, art.datetime DESC;');

SET @sql2 = CONCAT('
CREATE TEMPORARY TABLE at_article
SELECT id
FROM at_results
WHERE article_id = ', firstArticle, ';');

SET @sql3 = '
SELECT article_id
FROM at_results, at_article
WHERE at_results.id < at_article.id
ORDER BY at_results.id DESC;';

PREPARE stmt FROM @sql1;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

PREPARE stmt FROM @sql2;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

PREPARE stmt FROM @sql3;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

END $$
DELIMITER ;