你会如何在PHP中编写一个准备好的MySQL语句,每次都需要不同数量的参数?这种查询的一个例子是:
SELECT `age`, `name` FROM `people` WHERE id IN (12, 45, 65, 33)
Run Code Online (Sandbox Code Playgroud)
该IN子句id每次运行时都会有不同数量的s.
我脑子里有两种可能的解决方案,但想看看是否有更好的方法.
可能的解决方案1使语句接受100个变量,并使用保证不在表中的虚拟值填充其余变量; 多次调用超过100个值.
可能的解决方案2不要使用准备好的声明; 构建并运行查询严格检查可能的注入攻击.
我理解MySQL中预准备语句的安全性好处.这里不需要涉及这个主题.我想知道它们的性能方面.
现在,我知道当使用预准备语句的查询在单个PHP脚本中执行两次时,它更快,因为查询只被解析一次,每次查询一次.客户端进行一次准备,然后使用二进制协议发送数据两次.二进制协议速度更快,而且您不必再次进行解析.
但是,我只想在一个PHP脚本中执行一次查询的情况呢?看起来使用准备好的声明会更糟糕,因为你要两次前往服务器,一次准备,一次发送数据.只需要解析一次的好处就丢失了,你第二次旅行就受到了惩罚.如果数据的二进制格式不够小,那么使用准备好的语句会丢失,对吗?
但是,我已经阅读了一些关于PHP的mysqli或PDO库做什么的相互矛盾的报道?它们中的任何一个是否跨脚本执行缓存预准备语句?服务器是否必须在后续页面加载时再次解析预准备语句?如果答案是否定的,那么语句不必在第二个页面加载上进行解析,那么即使您每页面加载仅执行一次查询,看起来准备好的语句也会更好.
请考虑MySQL版本之间是否有任何相关变化.您可以放心地假设我使用的是PHP 5.2
编辑:只是为了说清楚,我想要专门针对MySQL和PHP的答案,指定MySQL版本,如果这是不同的,并且只考虑性能,而不是易用性或安全性.
更新:我接受了我的答案,因为后续评论有一些好主意.我仍然有点失望,似乎没有人能够回答我提出的实际问题的症结.我猜有时答案真的是"这取决于".
何时关闭PHP中的预处理语句?
例:
$query = "insert into web_reviews (title,added_date,reviewer_home_url,read_more_link,summary) values(?,?,?,?,?)";
$stmt = $this->db->prepare($query);
$stmt->bind_params($this->title,$this->added_date,$this->reviewer_home_url,$this->read_more,$this->summary);
$stmt->execute() or die("Cannot add the date to the database, please try again.");
$stmt->close();
$stmt = $this->db->prepare("select id from web_reviews where title = ? and read_more = ?");
$stmt->bind_params($this->title,$this->read_more);
$stmt->execute();
$stmt->bind_results($web_review_id);
$stmt->close();
Run Code Online (Sandbox Code Playgroud)
我应该$stmt->close();在这里使用吗?
编辑:
PHP手册上写的内容以及手册中的一条评论说:
结束准备好的声明.mysqli_stmt_close()也释放语句句柄.如果当前语句具有挂起或未读结果,则此函数将取消它们,以便可以执行下一个查询.
评论:
如果你在循环中使用bind_param重复一个语句,依此类推,以便进行更大的操作.我认为用stmt-> close来清理它会很好.但它在aprox后总是出现错误.250次操作.当我尝试使用stmt-> reset时,它对我有用.
我已经找到了很多方法来使用exec语句进行PDO,但我不确定它对我有什么帮助.我的理解是我必须对预准备语句使用execute()函数.我正在用来自用户输入的数据更新一行,所以我想使用预准备语句而不是query()调用.
我的代码如下:
$dbh = buildDBConnector();
$sql = "UPDATE tb_users
SET authState=1
WHERE id = ? AND authPass = ?";
$q = $dbh->prepare($sql);
$f = $q->execute(array($id,$authPass));
if($f){
echo '<br />Success<br />';
}else{
echo '<br />Failure<br />';
}
Run Code Online (Sandbox Code Playgroud)
问题是查询本身没有错误并且执行正常,因此没有失败存储在$ f中.但是,我需要知道它是否确实找到要更新的行,然后成功更新它.换句话说,我需要受影响的行.当谷歌搜索这样的时候,它会继续写入exec语句,但根据我的理解,exec不是用于准备语句吗?有什么建议?
我正在使用bit(1)字段来存储布尔值并使用PDO预处理语句写入表中.
这是测试表:
CREATE TABLE IF NOT EXISTS `test` (
`SomeText` varchar(255) NOT NULL,
`TestBool` bit(1) NOT NULL DEFAULT b'0'
) ENGINE=MEMORY DEFAULT CHARSET=latin1;
Run Code Online (Sandbox Code Playgroud)
这是测试代码:
$pdo = new PDO("connection string etc") ;
$statement = $pdo->prepare('INSERT INTO `test` (SomeText,TestBool) VALUES (?,?)') ;
$statement->execute(array("TEST",0)) ;
Run Code Online (Sandbox Code Playgroud)
运行该代码在TestBool下给我一行值为1的行.使用bindValue()和bindParm()也是一样的.我也尝试过具有相同结果的命名占位符(而不是?).
然后我尝试了:
$statement = $pdo->prepare('INSERT INTO `test` (SomeText,TestBool) VALUES ("TEST",0)') ;
$statement->execute() ;
Run Code Online (Sandbox Code Playgroud)
哪个工作正常(TestBool的值为0).将SQL直接打入MySQL也行之有效.
请注意,插入1始终有效.
那么为什么占位符无法插入值0?(我怎么做呢?)
有下一个分区表:
CREATE TABLE "ERMB_LOG_TEST_BF"."OUT_SMS"(
"TRX_ID" NUMBER(19,0) NOT NULL ENABLE,
"CREATE_TS" TIMESTAMP (3) DEFAULT systimestamp NOT NULL ENABLE,
/* other fields... */
) PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255
STORAGE(BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
TABLESPACE "ERMB_LOG_TEST_BF"
PARTITION BY RANGE ("TRX_ID") INTERVAL (281474976710656)
(PARTITION "SYS_P1358" VALUES LESS THAN (59109745109237760) SEGMENT CREATION IMMEDIATE
PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255
NOCOMPRESS LOGGING
STORAGE(INITIAL 8388608 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
BUFFER_POOL DEFAULT …Run Code Online (Sandbox Code Playgroud) sql oracle partitioning prepared-statement sql-execution-plan
想象一下,我们有一个查询:
SELECT * FROM somewhere WHERE `id` IN(1,5,18,25) ORDER BY `name`;
Run Code Online (Sandbox Code Playgroud)
以及要获取的ID数组: $ids = array(1,5,18,25)
准备好的陈述,建议准备一个陈述并多次调用:
$stmt = $mysqli->prepare('SELECT * FROM somewhere WHERE `id`=?;');
foreach ($ids as $id){
$stmt->bind_params('i', $id);
$stmt->exec();
}
Run Code Online (Sandbox Code Playgroud)
但现在我必须手动对结果进行排序.我有什么好的选择吗?
我有以下查询:
INSERT INTO users (user_id, date_created) VALUES (?,?)
Run Code Online (Sandbox Code Playgroud)
我有以下准备好的声明
PreparedStatement insertUser = dbConnection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
insertUser.setInt(1, 7);
java.util.Date now = new java.util.Date(System.currentTimeMillis());
insertUser.setDate(2, new java.sql.Date((new Date(System.currentTimeMillis())).getTime()));
insertUser.executeUpdate();
Run Code Online (Sandbox Code Playgroud)
如果我检查数据库,我发现它只插入今天的日期而不是时间,所以它将是: 2011-07-29 00:00:00
我还应该把setDate()时间花在什么上呢?
SELECT id, content, date
FROM comment
WHERE post = ?
ORDER BY date DESC
LIMIT ?, ?
Run Code Online (Sandbox Code Playgroud)
使用PDO(我正在使用具有Apache 2.2.21的MAMP 2.0.5,PHP高达5.3.6和MySQL 5.5.9)准备语句这不起作用,如果我更改查询
LIMIT 0, 10
Run Code Online (Sandbox Code Playgroud)
有用.
我在MySQL的错误中看到这是以前版本中的一个错误,但我无法理解这是否仍有待修复.
如果这仍然是个问题,有办法以另一种方式选择一系列行吗?
码:
$comments = $db->prepare($query);
/* where $db is the PDO object */
$comments->execute(array($post, $min, $max));
Run Code Online (Sandbox Code Playgroud)