1

我一直在努力解决这个问题,但我似乎无法靠自己解决这个问题。我遇到了 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。我用

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();

当我继续以这种方式编写它们时,我不禁认为我让自己的一切变得更加困难。我仍在学习,这是迄今为止我提出的最佳解决方案。任何人都知道如何让这个功能正常运行?

任何帮助表示赞赏。

谢谢!

4

2 回答 2

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);

这是粗略和笼统的,但你明白了。除非你真的致力于参数化我更喜欢第一个片段的所有内容。

于 2012-12-05T22:08:57.270 回答
0

我会在这里为其他寻找答案的人发布这个。Sammitch 的想法非常好,但我无法让它与存储过程一起使用。我最终将变量传递给 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 ;
于 2012-12-07T17:38:45.637 回答