Stored procedures PLSQL过程需要花费大量时间来执行

Stored procedures PLSQL过程需要花费大量时间来执行,stored-procedures,plsql,oracle11g,oracle10g,oracle-sqldeveloper,Stored Procedures,Plsql,Oracle11g,Oracle10g,Oracle Sqldeveloper,我有一个OraclePLSQL过程,它有一个父游标和两个子游标,在主体中参照父游标值创建 当我运行SQL语句direclt时,它会立即返回结果。然而,当我将它们放入PLSQL过程中时,它会无休止地运行 CREATE OR REPLACE PROCEDURE SENDTASKREMINDER AS l_html VARCHAR2(32767); r_html VARCHAR2(32767); l_exst NUMBER; ow

我有一个OraclePLSQL过程,它有一个父游标和两个子游标,在主体中参照父游标值创建

当我运行SQL语句direclt时,它会立即返回结果。然而,当我将它们放入PLSQL过程中时,它会无休止地运行

        CREATE OR REPLACE PROCEDURE SENDTASKREMINDER
    AS
      l_html VARCHAR2(32767);
      r_html VARCHAR2(32767);
      l_exst NUMBER;
      ownerId NUMBER;
      CURSOR OwnerCursor IS SELECT DISTINCT TASK_OWNER_ID FROM TASKS 
          WHERE to_char(TASK_START_DATE, 'DD-MON-YY') >= to_char(SYSDATE, 'DD-MON-YY') AND to_char(TASK_DUE_DATE, 'DD-MON-YY') <= to_char(SYSDATE, 'DD-MON-YY')
      UNION
      SELECT DISTINCT TASK_OWNER_ID FROM TASKS 
          WHERE to_char(TASK_START_DATE, 'DD-MON-YY') > to_char(SYSDATE, 'DD-MON-YY'); 
    BEGIN
      l_html := '<html>
        <head>
          <title>Task Reminder</title>
        </head>
        <body>';

      r_html := l_html;
      /* Get Pending Tasks */
      OPEN OwnerCursor;
      LOOP 
        FETCH OwnerCursor INTO ownerId;
        r_html := l_html;
        SELECT COUNT(*) INTO l_exst FROM TASKS 
            WHERE TASK_OWNER_ID = ownerId AND to_char(TASK_START_DATE, 'DD-MON-YY') >= to_char(SYSDATE, 'DD-MON-YY') AND to_char(TASK_DUE_DATE, 'DD-MON-YY') <= to_char(SYSDATE, 'DD-MON-YY');
        IF l_exst > 0
        THEN  
          r_html := r_html || '<h1>Your current pending tasks</h1><table><tr><th>Task Name</th><th>Description</th><th>Due Date</th></tr>';   
          FOR PendingCursor IN (SELECT PK_TASK_ID, TASK_NAME, TASK_DESCRIPTION, TASK_START_DATE, TASK_DUE_DATE FROM TASKS 
              WHERE TASK_OWNER_ID = ownerId AND to_char(TASK_START_DATE, 'DD-MON-YY') >= to_char(SYSDATE, 'DD-MON-YY') AND to_char(TASK_DUE_DATE, 'DD-MON-YY') <= to_char(SYSDATE, 'DD-MON-YY'))
          LOOP
            r_html := r_html || '<tr><td>' || PendingCursor.TASK_NAME || '</td><td>' || PendingCursor.TASK_DESCRIPTION || '</td><td>' || PendingCursor.TASK_DUE_DATE ||'</td></tr>';
          END LOOP;
          r_html := r_html || '</table>';
        END IF;

        SELECT COUNT(*) INTO l_exst FROM  TASKS 
           WHERE TASK_OWNER_ID = ownerId AND to_char(TASK_START_DATE, 'DD-MON-YY') > to_char(SYSDATE, 'DD-MON-YY')  ;  
        IF l_exst > 0
        THEN  
          r_html := r_html || '<h1>Your future tasks</h1><table><tr><th>Task Name</th><th>Description</th><th>Due Date</th></tr>';   
          FOR FutureCursor IN (SELECT PK_TASK_ID, TASK_NAME, TASK_DESCRIPTION, TASK_START_DATE, TASK_DUE_DATE   FROM  TASKS 
              WHERE TASK_OWNER_ID = ownerId AND to_char(TASK_START_DATE, 'DD-MON-YY') > to_char(SYSDATE, 'DD-MON-YY')  )
          LOOP
            r_html := r_html || '<tr><td>' || FutureCursor.TASK_NAME || '</td><td>' || FutureCursor.TASK_DESCRIPTION || '</td><td>' || FutureCursor.TASK_DUE_DATE ||'</td></tr>';
          END LOOP;
          r_html := r_html || '</table>';
        END IF;

r_html := r_html || '</body></html>';
        send_mail(p_to        => 'XXX.XXX@xxx.com',
                  p_from      => 'fromemail@xxx.com',
                  p_subject   => 'Your Pending and Future tasks for OwnerId' || ownerId,
                  p_text_msg  => 'text msg is not yet created',
                  p_html_msg  => r_html,
                  p_smtp_host => 'localhost');
      END LOOP;
      CLOSE OwnerCursor;
    END;
    /
这里是否创建了任何无休止的循环。PLSQL中没有错误。在粘贴到此处之前,我已编辑了此文件。所以,若发现编译错误,那个么这是由于我的编辑。实际过程没有任何编译错误

提前感谢您的帮助。

您忘记了提取后的出口:

FETCH Ownercursor into ownerId;
EXIT WHEN Ownercursor%NOTFOUND;
您忘记了提取后的出口:

FETCH Ownercursor into ownerId;
EXIT WHEN Ownercursor%NOTFOUND;
您忘记了提取后的出口:

FETCH Ownercursor into ownerId;
EXIT WHEN Ownercursor%NOTFOUND;
您忘记了提取后的出口:

FETCH Ownercursor into ownerId;
EXIT WHEN Ownercursor%NOTFOUND;

我没有看到任何无休止的循环。您是否在独立过程中测试了发送邮件的呼叫?它可能挂在那里

where子句中还有一个逻辑缺陷。您使用的TO_CHAR函数错误。这:


到charTASK开始日期,'DD-MON-YY'>=到charSYSDATE,'DD-MON-YY'和到charTASK到期日期,'DD-MON-YY'=系统日期和任务到期日期我没有看到任何无休止的循环。您是否在独立过程中测试了发送邮件的呼叫?它可能挂在那里

where子句中还有一个逻辑缺陷。您使用的TO_CHAR函数错误。这:


到charTASK开始日期,'DD-MON-YY'>=到charSYSDATE,'DD-MON-YY'和到charTASK到期日期,'DD-MON-YY'=系统日期和任务到期日期我没有看到任何无休止的循环。您是否在独立过程中测试了发送邮件的呼叫?它可能挂在那里

where子句中还有一个逻辑缺陷。您使用的TO_CHAR函数错误。这:


到charTASK开始日期,'DD-MON-YY'>=到charSYSDATE,'DD-MON-YY'和到charTASK到期日期,'DD-MON-YY'=系统日期和任务到期日期我没有看到任何无休止的循环。您是否在独立过程中测试了发送邮件的呼叫?它可能挂在那里

where子句中还有一个逻辑缺陷。您使用的TO_CHAR函数错误。这:


到charTASK开始日期,'DD-MON-YY'>=到charSYSDATE,'DD-MON-YY'和到charTASK到期日期,'DD-MON-YY'=系统日期和任务到期日期Wow。从哪里开始。Diederikh和Scott讨论了几个突出的问题,但仔细看代码,有很多是没有意义的

首先,当前任务的开始日期是过去还是现在,到期日期是将来还是现在?将来的任务是否在将来有开始日期?如果是这样的话,你的比较就全错了。根据您的逻辑,当前任务的开始日期为将来或现在,到期日期为过去或现在

第二,有五个!需要查询并访问数据库的位置。没有理由不能用初始光标读入您需要的所有内容。然后根据需要进行分析和处理

还有很多其他地方我会提出建议。我不想列出所有的代码,所以我只想包括代码。你可以接受我的小建议,也可以不接受,但一定要验证你的约会逻辑,不要频繁地访问数据库。在执行一次磁盘访问所需的时间内,可以执行数千行代码。它仍然没有我希望的生产代码那么完美,但已经足够让您开始了。而且,虽然我不能访问您的表,但它应该可以工作

create or replace PROCEDURE SENDTASKREMINDER AS
      l_html        VARCHAR2(100);
      r_html        VARCHAR2(32767);
      C_header      varchar2( 40 ) := '<h1>Your current pending tasks</h1>';
      F_header      varchar2( 32 ) := '<h1>Your future tasks</h1>';
      EndHeader     varchar2( 80 ) := '<table><tr><th>Task Name</th><th>Description</th><th>Due Date</th></tr>';
      EndFooter     varchar2( 32 ) := '</table></body></html>';
      PrevOwnerId   int         := -1; -- Must not be a valid OwnerID
      C_count       int         := 0;
      F_count       int         := 0;
      l_today       date        := Trunc( SysDate );
      CURSOR OwnerCursor IS 
        SELECT TASK_OWNER_ID, PK_TASK_ID, TASK_NAME, TASK_DESCRIPTION, TASK_START_DATE, TASK_DUE_DATE
        FROM TASKS
        WHERE (TASK_START_DATE <= l_today and TASK_DUE_DATE >= l_today)
            or TASK_START_DATE  > l_today
        order by TASK_OWNER_ID, TASK_START_DATE; -- So first the Current then Future tasks
BEGIN
    l_html := '<html><head><title>Task Reminder</title></head><body>';

    /* Get all Tasks */
    for OwnerRec in OwnerCursor LOOP
        if OwnerRec.TASK_OWNER_ID != PrevOwnerId then
            -- This is the first record for this owner. Unless it's also the first record of the loop,
            -- close the html and send the email.
            if C_count > 0 or F_count > 0 then
              r_html := r_html || EndFooter;
            end if;

            send_mail(p_to => 'XXX.XXX@xxx.com',
              p_from       => 'fromemail@xxx.com',
              p_subject    => 'Your Pending and Future tasks for OwnerId' || ownerId,
              p_text_msg   => 'text msg is not yet created',
              p_html_msg   => r_html,
              p_smtp_host  => 'localhost');

            r_html      := l_html;
            PrevOwnerId := OwnerRec.TASK_OWNER_ID;
            C_count     := 0;
            F_count     := 0;
        end if;

        if OwnerRec.TASK_START_DATE <= l_today then
            if C_count = 0 then
                -- This is the first Current entry, write the header
                r_html := r_html || C_header || EndHeader;
            end if;
            C_count := c_count + 1;
        else
            if F_count = 0 then
                -- This is the first Future entry. See if there were any Current entries
                if C_count > 0 then
                    -- There was, so terminate the Current table
                    r_html := r_html || '</table>';
                    C_count := 0;
                end if;
                -- Write the header
                r_html := r_html || F_header || EndHeader;
            end if;
            F_count := F_count + 1;
        end if;

        -- Whether Current or Future, write the task particulars
        r_html  := r_html || '<tr><td>' || OwnerRec.TASK_NAME || '</td><td>' || OwnerRec.TASK_DESCRIPTION || '</td><td>' || OwnerRec.TASK_DUE_DATE ||'</td></tr>';
    END LOOP;

    -- The last owner has not completed processing. But maybe there were no tasks at all...
    if C_count > 0 or F_count > 0 then
        r_html := r_html || EndFooter;

        send_mail(p_to => 'XXX.XXX@xxx.com',
          p_from       => 'fromemail@xxx.com',
          p_subject    => 'Your Pending and Future tasks for OwnerId' || ownerId,
          p_text_msg   => 'text msg is not yet created',
          p_html_msg   => r_html,
          p_smtp_host  => 'localhost');

        dbms_output.put_line( r_html );
    end if;
END;

哇。从哪里开始。Diederikh和Scott讨论了几个突出的问题,但仔细看代码,有很多是没有意义的

首先,当前任务的开始日期是过去还是现在,到期日期是将来还是现在?将来的任务是否在将来有开始日期?如果是这样的话,你的比较就全错了。根据您的逻辑,当前任务的开始日期为将来或现在,到期日期为过去或现在

第二,有五个!需要查询并访问数据库的位置。没有理由不能用初始光标读入您需要的所有内容。然后根据需要进行分析和处理

还有很多其他地方我会提出建议。我不想列出所有的代码,所以我只想包括代码。你可以接受我的小建议,也可以不接受,但一定要验证你的约会逻辑,不要频繁地访问数据库。在执行一次磁盘访问所需的时间内,可以执行数千行代码。它仍然没有我希望的生产代码那么完美,但已经足够让您开始了。而且,虽然我不能访问您的表,但它应该可以工作

create or replace PROCEDURE SENDTASKREMINDER AS
      l_html        VARCHAR2(100);
      r_html        VARCHAR2(32767);
      C_header      varchar2( 40 ) := '<h1>Your current pending tasks</h1>';
      F_header      varchar2( 32 ) := '<h1>Your future tasks</h1>';
      EndHeader     varchar2( 80 ) := '<table><tr><th>Task Name</th><th>Description</th><th>Due Date</th></tr>';
      EndFooter     varchar2( 32 ) := '</table></body></html>';
      PrevOwnerId   int         := -1; -- Must not be a valid OwnerID
      C_count       int         := 0;
      F_count       int         := 0;
      l_today       date        := Trunc( SysDate );
      CURSOR OwnerCursor IS 
        SELECT TASK_OWNER_ID, PK_TASK_ID, TASK_NAME, TASK_DESCRIPTION, TASK_START_DATE, TASK_DUE_DATE
        FROM TASKS
        WHERE (TASK_START_DATE <= l_today and TASK_DUE_DATE >= l_today)
            or TASK_START_DATE  > l_today
        order by TASK_OWNER_ID, TASK_START_DATE; -- So first the Current then Future tasks
BEGIN
    l_html := '<html><head><title>Task Reminder</title></head><body>';

    /* Get all Tasks */
    for OwnerRec in OwnerCursor LOOP
        if OwnerRec.TASK_OWNER_ID != PrevOwnerId then
            -- This is the first record for this owner. Unless it's also the first record of the loop,
            -- close the html and send the email.
            if C_count > 0 or F_count > 0 then
              r_html := r_html || EndFooter;
            end if;

            send_mail(p_to => 'XXX.XXX@xxx.com',
              p_from       => 'fromemail@xxx.com',
              p_subject    => 'Your Pending and Future tasks for OwnerId' || ownerId,
              p_text_msg   => 'text msg is not yet created',
              p_html_msg   => r_html,
              p_smtp_host  => 'localhost');

            r_html      := l_html;
            PrevOwnerId := OwnerRec.TASK_OWNER_ID;
            C_count     := 0;
            F_count     := 0;
        end if;

        if OwnerRec.TASK_START_DATE <= l_today then
            if C_count = 0 then
                -- This is the first Current entry, write the header
                r_html := r_html || C_header || EndHeader;
            end if;
            C_count := c_count + 1;
        else
            if F_count = 0 then
                -- This is the first Future entry. See if there were any Current entries
                if C_count > 0 then
                    -- There was, so terminate the Current table
                    r_html := r_html || '</table>';
                    C_count := 0;
                end if;
                -- Write the header
                r_html := r_html || F_header || EndHeader;
            end if;
            F_count := F_count + 1;
        end if;

        -- Whether Current or Future, write the task particulars
        r_html  := r_html || '<tr><td>' || OwnerRec.TASK_NAME || '</td><td>' || OwnerRec.TASK_DESCRIPTION || '</td><td>' || OwnerRec.TASK_DUE_DATE ||'</td></tr>';
    END LOOP;

    -- The last owner has not completed processing. But maybe there were no tasks at all...
    if C_count > 0 or F_count > 0 then
        r_html := r_html || EndFooter;

        send_mail(p_to => 'XXX.XXX@xxx.com',
          p_from       => 'fromemail@xxx.com',
          p_subject    => 'Your Pending and Future tasks for OwnerId' || ownerId,
          p_text_msg   => 'text msg is not yet created',
          p_html_msg   => r_html,
          p_smtp_host  => 'localhost');

        dbms_output.put_line( r_html );
    end if;
END;

哇。从哪里开始。Diederikh和Scott讨论了几个突出的问题,但仔细看代码,有很多是没有意义的

首先,当前任务是否为w 开始日期在过去或现在,到期日期在未来或现在?将来的任务是否在将来有开始日期?如果是这样的话,你的比较就全错了。根据您的逻辑,当前任务的开始日期为将来或现在,到期日期为过去或现在

第二,有五个!需要查询并访问数据库的位置。没有理由不能用初始光标读入您需要的所有内容。然后根据需要进行分析和处理

还有很多其他地方我会提出建议。我不想列出所有的代码,所以我只想包括代码。你可以接受我的小建议,也可以不接受,但一定要验证你的约会逻辑,不要频繁地访问数据库。在执行一次磁盘访问所需的时间内,可以执行数千行代码。它仍然没有我希望的生产代码那么完美,但已经足够让您开始了。而且,虽然我不能访问您的表,但它应该可以工作

create or replace PROCEDURE SENDTASKREMINDER AS
      l_html        VARCHAR2(100);
      r_html        VARCHAR2(32767);
      C_header      varchar2( 40 ) := '<h1>Your current pending tasks</h1>';
      F_header      varchar2( 32 ) := '<h1>Your future tasks</h1>';
      EndHeader     varchar2( 80 ) := '<table><tr><th>Task Name</th><th>Description</th><th>Due Date</th></tr>';
      EndFooter     varchar2( 32 ) := '</table></body></html>';
      PrevOwnerId   int         := -1; -- Must not be a valid OwnerID
      C_count       int         := 0;
      F_count       int         := 0;
      l_today       date        := Trunc( SysDate );
      CURSOR OwnerCursor IS 
        SELECT TASK_OWNER_ID, PK_TASK_ID, TASK_NAME, TASK_DESCRIPTION, TASK_START_DATE, TASK_DUE_DATE
        FROM TASKS
        WHERE (TASK_START_DATE <= l_today and TASK_DUE_DATE >= l_today)
            or TASK_START_DATE  > l_today
        order by TASK_OWNER_ID, TASK_START_DATE; -- So first the Current then Future tasks
BEGIN
    l_html := '<html><head><title>Task Reminder</title></head><body>';

    /* Get all Tasks */
    for OwnerRec in OwnerCursor LOOP
        if OwnerRec.TASK_OWNER_ID != PrevOwnerId then
            -- This is the first record for this owner. Unless it's also the first record of the loop,
            -- close the html and send the email.
            if C_count > 0 or F_count > 0 then
              r_html := r_html || EndFooter;
            end if;

            send_mail(p_to => 'XXX.XXX@xxx.com',
              p_from       => 'fromemail@xxx.com',
              p_subject    => 'Your Pending and Future tasks for OwnerId' || ownerId,
              p_text_msg   => 'text msg is not yet created',
              p_html_msg   => r_html,
              p_smtp_host  => 'localhost');

            r_html      := l_html;
            PrevOwnerId := OwnerRec.TASK_OWNER_ID;
            C_count     := 0;
            F_count     := 0;
        end if;

        if OwnerRec.TASK_START_DATE <= l_today then
            if C_count = 0 then
                -- This is the first Current entry, write the header
                r_html := r_html || C_header || EndHeader;
            end if;
            C_count := c_count + 1;
        else
            if F_count = 0 then
                -- This is the first Future entry. See if there were any Current entries
                if C_count > 0 then
                    -- There was, so terminate the Current table
                    r_html := r_html || '</table>';
                    C_count := 0;
                end if;
                -- Write the header
                r_html := r_html || F_header || EndHeader;
            end if;
            F_count := F_count + 1;
        end if;

        -- Whether Current or Future, write the task particulars
        r_html  := r_html || '<tr><td>' || OwnerRec.TASK_NAME || '</td><td>' || OwnerRec.TASK_DESCRIPTION || '</td><td>' || OwnerRec.TASK_DUE_DATE ||'</td></tr>';
    END LOOP;

    -- The last owner has not completed processing. But maybe there were no tasks at all...
    if C_count > 0 or F_count > 0 then
        r_html := r_html || EndFooter;

        send_mail(p_to => 'XXX.XXX@xxx.com',
          p_from       => 'fromemail@xxx.com',
          p_subject    => 'Your Pending and Future tasks for OwnerId' || ownerId,
          p_text_msg   => 'text msg is not yet created',
          p_html_msg   => r_html,
          p_smtp_host  => 'localhost');

        dbms_output.put_line( r_html );
    end if;
END;

哇。从哪里开始。Diederikh和Scott讨论了几个突出的问题,但仔细看代码,有很多是没有意义的

首先,当前任务的开始日期是过去还是现在,到期日期是将来还是现在?将来的任务是否在将来有开始日期?如果是这样的话,你的比较就全错了。根据您的逻辑,当前任务的开始日期为将来或现在,到期日期为过去或现在

第二,有五个!需要查询并访问数据库的位置。没有理由不能用初始光标读入您需要的所有内容。然后根据需要进行分析和处理

还有很多其他地方我会提出建议。我不想列出所有的代码,所以我只想包括代码。你可以接受我的小建议,也可以不接受,但一定要验证你的约会逻辑,不要频繁地访问数据库。在执行一次磁盘访问所需的时间内,可以执行数千行代码。它仍然没有我希望的生产代码那么完美,但已经足够让您开始了。而且,虽然我不能访问您的表,但它应该可以工作

create or replace PROCEDURE SENDTASKREMINDER AS
      l_html        VARCHAR2(100);
      r_html        VARCHAR2(32767);
      C_header      varchar2( 40 ) := '<h1>Your current pending tasks</h1>';
      F_header      varchar2( 32 ) := '<h1>Your future tasks</h1>';
      EndHeader     varchar2( 80 ) := '<table><tr><th>Task Name</th><th>Description</th><th>Due Date</th></tr>';
      EndFooter     varchar2( 32 ) := '</table></body></html>';
      PrevOwnerId   int         := -1; -- Must not be a valid OwnerID
      C_count       int         := 0;
      F_count       int         := 0;
      l_today       date        := Trunc( SysDate );
      CURSOR OwnerCursor IS 
        SELECT TASK_OWNER_ID, PK_TASK_ID, TASK_NAME, TASK_DESCRIPTION, TASK_START_DATE, TASK_DUE_DATE
        FROM TASKS
        WHERE (TASK_START_DATE <= l_today and TASK_DUE_DATE >= l_today)
            or TASK_START_DATE  > l_today
        order by TASK_OWNER_ID, TASK_START_DATE; -- So first the Current then Future tasks
BEGIN
    l_html := '<html><head><title>Task Reminder</title></head><body>';

    /* Get all Tasks */
    for OwnerRec in OwnerCursor LOOP
        if OwnerRec.TASK_OWNER_ID != PrevOwnerId then
            -- This is the first record for this owner. Unless it's also the first record of the loop,
            -- close the html and send the email.
            if C_count > 0 or F_count > 0 then
              r_html := r_html || EndFooter;
            end if;

            send_mail(p_to => 'XXX.XXX@xxx.com',
              p_from       => 'fromemail@xxx.com',
              p_subject    => 'Your Pending and Future tasks for OwnerId' || ownerId,
              p_text_msg   => 'text msg is not yet created',
              p_html_msg   => r_html,
              p_smtp_host  => 'localhost');

            r_html      := l_html;
            PrevOwnerId := OwnerRec.TASK_OWNER_ID;
            C_count     := 0;
            F_count     := 0;
        end if;

        if OwnerRec.TASK_START_DATE <= l_today then
            if C_count = 0 then
                -- This is the first Current entry, write the header
                r_html := r_html || C_header || EndHeader;
            end if;
            C_count := c_count + 1;
        else
            if F_count = 0 then
                -- This is the first Future entry. See if there were any Current entries
                if C_count > 0 then
                    -- There was, so terminate the Current table
                    r_html := r_html || '</table>';
                    C_count := 0;
                end if;
                -- Write the header
                r_html := r_html || F_header || EndHeader;
            end if;
            F_count := F_count + 1;
        end if;

        -- Whether Current or Future, write the task particulars
        r_html  := r_html || '<tr><td>' || OwnerRec.TASK_NAME || '</td><td>' || OwnerRec.TASK_DESCRIPTION || '</td><td>' || OwnerRec.TASK_DUE_DATE ||'</td></tr>';
    END LOOP;

    -- The last owner has not completed processing. But maybe there were no tasks at all...
    if C_count > 0 or F_count > 0 then
        r_html := r_html || EndFooter;

        send_mail(p_to => 'XXX.XXX@xxx.com',
          p_from       => 'fromemail@xxx.com',
          p_subject    => 'Your Pending and Future tasks for OwnerId' || ownerId,
          p_text_msg   => 'text msg is not yet created',
          p_html_msg   => r_html,
          p_smtp_host  => 'localhost');

        dbms_output.put_line( r_html );
    end if;
END;

如果你很难理解简单的循环和退出,那么你也可以考虑一个for循环。对于初学者来说,它们更容易理解。非常感谢。现在很快。我现在有另一个问题。它正在发送大约1000多封内容相同的电子邮件。在我的表格中,我只有两个任务所有者,他们将收到提醒电子邮件。所以,应该只有2封电子邮件发送给我测试我的id。然而,我收到1000+重复电子邮件。你知道为什么吗?如果你很难理解简单的循环和退出,那么你也可以考虑一个for循环。对于初学者来说,它们更容易理解。非常感谢。现在很快。我现在有另一个问题。它正在发送大约1000多封内容相同的电子邮件。在我的表格中,我只有两个任务所有者,他们将收到提醒电子邮件。所以,应该只有2封电子邮件发送给我测试我的id。然而,我收到1000+重复电子邮件。你知道为什么吗?如果你很难理解简单的循环和退出,那么你也可以考虑一个for循环。对于初学者来说,它们更容易理解。非常感谢。现在很快。我现在有另一个问题。它正在发送大约1000多封内容相同的电子邮件。在我的表格中,我只有两个任务所有者,他们将收到提醒电子邮件。所以,应该只有2封电子邮件发送给我测试我的id。然而,我收到1000+重复电子邮件。你知道为什么吗?如果你很难理解简单的循环和退出,那么你也可以考虑一个for循环。对于初学者来说,它们更容易理解。非常感谢。现在很快。我现在有另一个问题。它正在发送大约1000多封内容相同的电子邮件。在我的表格中,我只有两个任务所有者,他们将收到提醒电子邮件。所以,应该只有2封电子邮件发送给我测试我的id。然而,我收到1000+重复电子邮件。知道为什么吗?