我有一个在CF任务中安排的ColdFusion文件,每30秒运行一次。此文件的目的是将响应表中的文本插入到订单表中。一切都工作得很酷,但现在有问题,即
随机1次中的6次,文件已执行两次,每行代码运行两次。所以订单被插入两次。
我已经检查过,如果存在响应ID为xxx的相同订单,请不要再次插入。但我仍然看到重复。任何帮助将不胜感激。
<cfquery name="getdupeordersinfile" datasource="#Application.ds#" dbtype="ODBC" username="#Application.UserName#" password="#Application.Password#">
SELECT TOP 1 * from Orders
Where ltrim(rtrim(smsresponseid)) = '#val(responseId)#'
</cfquery>
<cfif getdupeordersinfile.recordcount EQ 0>
<cfquery name="getInsertInOrder" maxrows="1" datasource="#Application.ds#" dbtype="ODBC" username="#Application.UserName#" password="#Application.Password#" result="stResult">
INSERT INTO Orders
(....)
values (...)
</cfquery>
</cfif>
由于
答案 0 :(得分:2)
我会使用以下查询的一些变体,这些变体应该适用于任何现代SQL数据库:
INSERT INTO Orders
(column1, column2, column3)
SELECT column1, column2, column3
FROM Responses
WHERE 1 = 1
AND ...
AND NOT EXISTS (
SELECT 1
FROM Orders
WHERE ltrim(rtrim(smsresponseid)) = ltrim(rtrim(Responses.responseid))
)
如果您输入的静态值不是来自数据库,也可以使用相同的技术。
INSERT INTO Orders
(
color,
column1,
column2
)
SELECT 'red',
<cfqueryparam value="#value1# ...>,
<cfqueryparam value="#value2# ...>
WHERE 1 = 1
AND NOT EXISTS (
SELECT 1
FROM Orders
WHERE ltrim(rtrim(smsresponseid)) = ltrim(rtrim(Responses.responseid))
)
我正在使用“SELECT 1”而不是“SELECT TOP 1”,因为您根本不需要获取任何字段。 “SELECT 1”只会返回1的字面值。当然,这是无用的,但无论如何你都不需要EXISTS子句中的实际值。此外,无论如何,数据库都不会在EXISTS子句中检索多条记录。
SQL Server: JOIN vs IN vs EXISTS - the logical difference
这篇文章特定于SQL Server,但我的经验表明大多数SQL数据库都是如此。
EXISTS和NOT EXISTS是解决此类问题的最佳解决方案,因为它具有性能影响,并且语法正确且清晰地描述了您想要的内容。
为什么会这样?
正如我以前没有提到的那样,这就是为什么代码可以执行两次(只是推测,但我已经看过了)。
假设此代码位于order-response.cfm中。想象一下,order-response.cfm被调用(由用户通过CFSCHEDULE调用并不重要)。请拨打此请求#1。在此之后,它立即再次被调用。请拨打此请求#2。
在请求#1中,检查查询运行并且不返回任何记录。然后(在请求#1中完成插入查询之前),检查查询在请求#2中运行。现在,请求#2中的检查查询也不返回任何结果。然后插入查询在请求#1中运行。然后插入查询在请求#2中运行(因为检查查询在运行时没有返回任何结果)。
这些请求相互接近以达到此效果的程度取决于代码运行的速度,而这又取决于(某种程度上)服务器所处的负载。如果您有数据库服务器被锁定的时间段(或相关的表被锁定),那么您可能需要等待解锁的事情,这可能会使这些步骤花费比您预期的更长的时间。
鉴于您的任务每30秒运行一次,数据库或ColdFusion的轻微减速可能导致这些请求重叠,从而产生此问题。
将它全部放在一个SQL语句中,使得该操作在某种程度上是原子的,并且不太可能遇到这种情况(尽管在极少数情况下它仍然会发生)。
我应该提到(我认为其他人已经做过)你应该在你的表中添加约束来拒绝重复的条目,以防止人们通过该级别。
答案 1 :(得分:0)
从响应表到您说的订单表。我的方法类似于:
insert into orders
(field1, field2, etc)
select field1, field2, etc
from response
where whatever
and somefield in
(select somefield
from response
where -- here you specify the same conditions of the outer query
except
select somefield
from orders
)
确切的语法取决于您的rdbms。有些人使用减号而不是除外,其他人根本不支持这种语法。