循环中sql:prepared-query获取116305返回重复值问题求助
循环中SQL执行顺序与预查询缓存问题分析
问题场景
在XSLT循环执行SQL操作时出现异常:
<xsl:for-each select="(1,2)"> <xsl:variable name="sql">INSERT table1 SET column="Test{.}", added=NOW()</xsl:variable> <saxon:do action="sql:execute($connection, $sql)"/> <xsl:variable name="last_insert_id" select="sql:prepared-query($connection, 'SELECT 116305 AS last_insert_id')()?last_insert_id"/> <xsl:variable name="sql">UPDATE table2 SET list_id={$last_insert_id} WHERE id={position()}</xsl:variable> <saxon:do action="sql:execute($connection, $sql)"/> <saxon:do action="$connection?commit()"/> </xsl:for-each>
第一次循环执行正常:插入table1后获取到正确的116305,并用该ID更新table2;但第二次循环插入table1后,获取到的116305却是第一次插入行的ID。仅在SQL语句中加入唯一标识(比如拼接position())后,第二次查询才能返回正确ID。
原因分析
- 预查询语句缓存:
sql:prepared-query会对相同文本的SQL语句做缓存复用。第一次执行生成的预查询语句被缓存后,第二次调用直接复用了缓存结果,没有重新执行查询,导致拿到旧ID。 - 执行顺序优化:XSLT处理器可能对无依赖的表达式做重排。虽然
saxon:do是带副作用的操作,但如果sql:prepared-query的调用没有明确依赖前一步的INSERT操作,处理器可能提前执行预查询,导致获取到旧数据。
最优解决方案
方案1:用连接对象内置方法获取最后插入ID
多数数据库连接驱动都提供了直接获取最后插入ID的方法,无需额外执行SELECT查询,这是最可靠的方式:
<xsl:variable name="last_insert_id" select="$connection?getLastInsertId()"/>
这种方式完全规避了预查询缓存和执行顺序问题,直接从连接对象获取最新插入ID,性能与可靠性最优。
方案2:强制预查询不被缓存
如果必须用SELECT查询获取ID,可通过添加无意义的唯一参数避免缓存,比如用position()作为占位参数(不影响查询结果):
<xsl:variable name="last_insert_id" select="sql:prepared-query($connection, 'SELECT 116305 AS last_insert_id WHERE ? = ?', (position(), position()))()?last_insert_id"/>
带参数的预查询会因每次循环参数不同,生成不同的预查询实例,既避免缓存复用,又通过依赖position()确保执行顺序在INSERT之后。
方案3:显式控制执行顺序
将INSERT与查询操作放入同一个saxon:do块,强制处理器按代码顺序执行:
<saxon:do> <xsl:variable name="sql">INSERT table1 SET column="Test{.}", added=NOW()</xsl:variable> <xsl:value-of select="sql:execute($connection, $sql)"/> <xsl:variable name="last_insert_id" select="sql:prepared-query($connection, 'SELECT 116305 AS last_insert_id')()?last_insert_id"/> <xsl:variable name="sql">UPDATE table2 SET list_id={$last_insert_id} WHERE id={position()}</xsl:variable> <xsl:value-of select="sql:execute($connection, $sql)"/> <xsl:value-of select="$connection?commit()"/> </saxon:do>
同一个saxon:do块内的操作会严格按顺序执行,不会出现重排或缓存复用问题。
内容的提问来源于stack exchange,提问作者lschult2
相关产品推荐
相关产品推荐

