如何使用准备好的 PDO 语句设置 ORDER BY 参数?
Posted
技术标签:
【中文标题】如何使用准备好的 PDO 语句设置 ORDER BY 参数?【英文标题】:How do I set ORDER BY params using prepared PDO statement? 【发布时间】:2011-02-02 07:11:39 【问题描述】:我在使用 SQL 的 ORDER BY
部分中的参数时遇到问题。它不会发出任何警告,但不会打印任何内容。
$order = 'columnName';
$direction = 'ASC';
$stmt = $db->prepare("SELECT field from table WHERE column = :my_param ORDER BY :order :direction");
$stmt->bindParam(':my_param', $is_live, PDO::PARAM_STR);
$stmt->bindParam(':order', $order, PDO::PARAM_STR);
$stmt->bindParam(':direction', $direction, PDO::PARAM_STR);
$stmt->execute();
:my_param
有效,但 :order
或 :direction
无效。它没有在内部正确转义吗?我是否将其直接插入 SQL 中?像这样:
$order = 'columnName';
$direction = 'ASC';
$stmt = $db->prepare("SELECT * from table WHERE column = :my_param ORDER BY $order $direction");
是否有 PDO::PARAM_COLUMN_NAME
常量或等价物?
谢谢!
【问题讨论】:
另见Can php PDO Statements accept the table name as parameter? 【参考方案1】:是的,您无法将其直接插入 SQL 中。当然,有一些预防措施。 每个运算符/标识符都必须在您的脚本中进行硬编码,如下所示:
$orders=array("name","price","qty");
$key=array_search($_GET['sort'],$orders);
$order=$orders[$key];
$query="SELECT * from table WHERE is_live = :is_live ORDER BY $order";
方向相同。
我写了一个whitelisting helper function用于这种情况,它大大减少了需要编写的代码量:
$order = white_list($order, ["name","price","qty"], "Invalid field name");
$direction = white_list($direction, ["ASC","DESC"], "Invalid ORDER BY direction");
$sql = "SELECT field from table WHERE column = ? ORDER BY $order $direction";
$stmt = $db->prepare($sql);
$stmt->execute([$is_live]);
这里的想法是检查值并在不正确的情况下引发错误。
【讨论】:
如果你有一个有效的字符串数组并重新输入你所拥有的$input = (string)$input;
到 (string)
你可以简单地检查 if( in_array($input, $valid_orderbys_array) )
它基本上应该做一样的。【参考方案2】:
我认为你不能:
在order by
子句中使用占位符
绑定列名:您只能绑定值或变量,并将它们的值注入到准备好的语句中。
【讨论】:
老实说 - IS 可以在ORDER BY
子句中绑定 值(不是标识符)
这是部分正确的。您只能使用绑定占位符来提供 values 。 永远可以使用绑定占位符来提供 标识符(如表名、列名)、SQL 关键字或value 以外的 SQL 语法。 (基本上,绑定占位符可以代替字符串文字、日期文字或数字文字。句号。但正如 zerkms 指出的那样,由于可以在 ORDER BY
子句中使用文字作为表达式的一部分,所以它是事实上可以使用绑定占位符作为ORDER BY
子句中表达式的一部分。【参考方案3】:
有可能在ORDER BY
子句中使用准备好的语句,不幸的是,您需要传递名称的列顺序,并且需要设置PDO_PARAM_INT
和类型。
在 mysql 中,您可以使用此查询获取列的顺序:
SELECT column_name, ordinal_position FROM information_schema.columns
WHERE table_name = 'table' and table_schema = 'database'
PHP 代码:
$order = 2;
$stmt = $db->prepare("SELECT field from table WHERE column = :param ORDER BY :order DESC");
$stmt->bindParam(':param', $is_live, PDO::PARAM_STR);
$stmt->bindParam(':order', $order, PDO::PARAM_INT);
$stmt->execute();
【讨论】:
聪明的解决方法。不过就个人而言,我认为首先必须弄清楚列号是太多了。$direction
仍然存在问题,您刚刚将其硬编码为 DESC
。【参考方案4】:
我认为您不能将 ASC/DESC 作为准备好的语句的一部分,但是如果您在 sql 查询中将它们全部列出,则可以像这样:
// Validate between 2 possible values:
$sortDir = isset($_GET['sortDir']) && $_GET['sortDir'] === 'ASC' ? 'ASC' : 'DESC';
$sql = "
...
order
by
case :orderByCol
when 'email' then email
when 'age' then age
else surname
end
$sortDir
";
$stmt = $db->prepare($sql);
$stmt->bindParam(':orderByCol', $someColumn);
$stmt->execute();
由于 ASC/DESC 只有两个可能的值,您可以使用 php 代码轻松验证并在它们之间选择硬编码值。
您也可以为此使用 ELT(FIELD(,,,,,),,,,,) 函数,但是即使列是数字数据类型,排序也将始终作为字符串完成应该使用数字语义/排序规则进行排序。
【讨论】:
【参考方案5】:不幸的是,我想您无法使用准备好的语句来实现它。 这将使其不可缓存,因为不同的列可能具有可以使用特殊排序策略进行排序的值。
使用标准转义创建查询并直接执行。
【讨论】:
【参考方案6】:可能。您可以在“order by”子句中使用数字而不是字段名称。这是一个从 1 开始的数字,按照查询中的字段名称顺序排列。您可以为 ASC 或 DESC 连接一个字符串。例如 “从 tab1 顺序中选择 col1,col2,col3?” + strDesc + “限制 10,5”。 strDesc="ASC" / "DESC"。
【讨论】:
【参考方案7】:如果我没记错的话,帕斯卡是对的。 PDO 中唯一可能的绑定是值的绑定,就像您对 ':my_param' 参数所做的那样。 但是,这并没有造成任何伤害:
$stmt = $db->prepare("SELECT field from table WHERE column = :my_param ORDER BY ".$order ." ".$direction);
$stmt->bindParam(':my_param', $is_live, PDO::PARAM_STR);
$stmt->execute();
唯一需要注意的是 $order
和 $direction
的正确转义,但由于您手动设置它们并且没有通过用户输入设置它们,我想您已经设置好了。
【讨论】:
如果他不打算更改 orderby 列和方向,那么我认为让它们成为变量根本没有任何意义。以上是关于如何使用准备好的 PDO 语句设置 ORDER BY 参数?的主要内容,如果未能解决你的问题,请参考以下文章