在Coldfusion中没有循环的情况下将大量数据插入到查询中

时间:2015-08-20 16:01:36

标签: sql sql-server coldfusion

我们有一个页面,最近偶尔会暂停,这是一个非常古老的代码我从不打扰更新,但现在是我猜的最佳时间。

此代码的作用是跟踪谁从我们的电子邮件爆炸发送给他们的电子邮件。代码:

  1. 检查他们是否已收到电子邮件
  2. 发送电子邮件
  3. 插入电子邮件发送表,该成员收到此电子邮件。
  4. 问题是标记电子邮件的查询是在cfloop中。我在寻找更好的方法来获得建议而不将查询放入cfloop,也许是一些例子。

    它循环通过约6000(略低于)的联系人。代码如下。

    <cfloop query="MembersWhoGetEMail"> 
        <cfquery name="queryname">
            INSERT INTO EmailsSent
                (EmailID, MemID)
            VALUES
                (<cfqueryparam value="#EmailID#" cfsqltype="CF_SQL_INTEGER">, 
                 <cfqueryparam value="#MemID#" cfsqltype="CF_SQL_INTEGER">)
        </cfquery>
    </cfloop>
    

    我们的服务器正在运行MS SQL 2012

4 个答案:

答案 0 :(得分:2)

循环使用这样的数据会磨损应用程序服务器并导致您描述的超时。

试试这个。 创建存储过程并让数据库服务器完成所有工作,而不是从服务器获取数据将其拉入Cold Fusion应用程序服务器,然后将其发送回数据库

我会在我的存储过程中使用游标

set nocount on;

declare @email varchar(20);
declare @memid int;

declare insertCursor cursor fast_forward for
(your first select statement here)

open insertCursor
fetch next from insertCursor into @email, @memid

while @@fetch_status = 0
Begin
 INSERT INTO EmailsSent
            (EmailID, MemID)
        VALUES
            (@email, @memid)

fetch next from insertCursor into @email, memid
end
close insertCursor
deallocate insertCursor

然后在我的cfm页面

<cfstoredproc procedure="myProcedure" datasource="myDSN">
  <cfprocresult name="myResult"> 
  </cfprocresult>
</cfstoredproc>

这将允许您的数据库完成所有工作,而所有CF服务器正在等待它完成。

**编辑 - 在与Leigh讨论后,我的答案远离基础。各种各样。这可能确实需要尽可能在服务器上完成。如果您的服务器设置为发送电子邮件,则可以在服务器上完成。使用sp_send_dbmail存储过程

  

向指定的收件人发送电子邮件。消息可能   包括查询结果集,文件附件或两者。当邮件是   成功放入数据库邮件队列后,sp_send_dbmail返回   邮件的mailitem_id。此存储过程位于msdb中   数据库中。

然后您可以安排作业在需要时运行该过程。与CF任务相同。

答案 1 :(得分:1)

您可以创建一个大型选择列表并批量插入。您可能需要分批循环遍历一千个。 6000可能无法正常工作,因为you can only pass 2100 parameters in a query

<cfquery>
  INSERT INTO EmailsSent (EmailID, MemID)
  <cfloop query="MemberWhoGetEmail">
    <cfset rowNumber = totalRow + indRow>
    <cfif rowNumber LTE totalRows>
      SELECT <cfqueryparam value="#MemberWhoGetEmail.EmailID#" cfsqltype="CF_SQL_INTEGER">, 
      <cfqueryparam value="#MemberWhoGetEmail.MemID#" cfsqltype="CF_SQL_INTEGER">
      <cfif indRow NEQ rowsPerInsert AND rowNumber NEQ totalRows> UNION ALL</cfif>
    </cfif>
  </cfloop>
</cfquery>

或者Dan建议,您可以修改循环以消除if语句。

<cfquery>
  INSERT INTO EmailsSent (EmailID, MemID)
  <cfloop query="MemberWhoGetEmail">
    SELECT <cfqueryparam value="#MemberWhoGetEmail.EmailID#" cfsqltype="CF_SQL_INTEGER">, 
    <cfqueryparam value="#MemberWhoGetEmail.MemID#" cfsqltype="CF_SQL_INTEGER">
    UNION ALL
  </cfloop>
  SELECT null, null WHERE 1 = 3
</cfquery>

答案 2 :(得分:1)

如何使用<cffile>将字段写入csv文件,然后BULK INSERT

BULK 
INSERT Employee
FROM 'F:\\MyPublis\\CSVTest.csv' --location with filename
WITH
(
FIELDTERMINATOR = ',',
ROWTERMINATOR = '\n'
)

http://www.codeproject.com/Tips/775961/Import-CSV-or-txt-File-Into-SQL-Server-Using-Bulk

答案 3 :(得分:1)

最大的问题是MembersWhoGetEMail查询是否(或可以)编写为单个SQL查询。如果是这样,那么INSERT INTO SELECT查询就是您的答案。如果没有,那么写入CSV并执行BULK INSERT可能是您最好的。

INSERT INTO SELECT

我将为MembersWhoGetEMail组成一个简单的查询,但它可以是为INSERT部分正确返回数据的任何查询。

<cfquery>
 INSERT INTO EmailsSent
     (EmailID, MemID)
 SELECT
     EmailID,
     MemID
 FROM
     thisTable
 JOIN
     anotherTable
  ON
      thisTable.field = anotherTable.field
  WHERE
      1 = 1
</cfquery>

SELECT子句可以是任何返回正确列数的SQL,并且在匹配顺序中返回要插入的列。数据类型必须与插入的数据匹配,因此如果它们不是,那么您将需要在SELECT语句中的那些列上使用CAST()或CONVERT()。

这比循环查询和多次插入要快得多。

如果你无法将它变成SQL查询,那么你将不得不进行BULK INSERT。

BULK INSERT

在这种情况下,您将编写一个CSV文件并在其上运行BULK INSERT。

BULK
INSERT EmailsSent
FROM '#FullPathToFile#'
WITH
(
FIELDTERMINATOR = ',',
ROWTERMINATOR = '\n'
)

这将是一个文件,其中逗号分隔每个值和每行的换行符,第一行是表格中列的名称。

在您的示例中,您只是插入整数,但如果您要插入文本,那么您还需要注意您的文本不包含回车符或逗号,否则会弄乱CSV文件(如果是这样,您可能必须选择不同的分隔符。)

总而言之,如果您可以从单个SQL查询中获取数据,INSERT INTO SELECT是更好的解决方案。