douzhi7661 2019-08-05 14:40
浏览 138
已采纳

从php调用MSSQL存储过程

We have been stumped with this all morning.

I have an MSSQL stored proc which processes some records. Each record generates an email with dynamic content, and this is populated from another stored proc.

So the first stored proc has a cursor, and each row is processed resulting in a call to another stored proc, which itself has a cursor to loop through. The first stored proc has no output parameters or returned values, etc, while the second uses output parameters to return fields to the first proc.

This works fine from Datagrip.

Calling this from php using PDO (or using other drivers) it fails to run fully. It would produce a small batch of records then stop (tends to be either 5, 9, 13 or 45 - has changed as we have experimented with difference solutions).

We have managed to get it to run now using PDOStatement::nextRowset . We use a query of the first stored proc and then using while ( $stmt->nextRowset() ) ; to loop through the (non existent / unwanted) row sets.

This works. But as the first stored proc isn't returning anything (just that pdo seems to want to process some kind of internal result set) this seems very dirty and inefficient.

Is there an alternative? Possibly a parameter to pass to pdo, or a setting within the stored proc?

Below there is some simplified code to show how things interact.

PHP calling script.

<?php
$emailRepository = new EmailRepository(hostname, port, dbname, username, password);

$ret = $emailRepository->sendRenewalEmails();

class EmailRepository
{

    public function __construct($hostname, $port, $dbname, $username, $password)
    {
        $this->hostname = $hostname;
        $this->port = $port;
        $this->dbname = $dbname;
        $this->username = $username;
        $this->password = $password;
        $this->connect();
    }

    private function connect()
    {
        try {
            $this->db = new PDO ("sqlsrv:Server=$this->hostname,$this->port;Database=$this->dbname", $this->username, $this->password);
            $this->db->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
        } catch (PDOException $e) {
            var_dump("Failed to get DB handle: " . $e->getMessage());
        }
    }

    public function sendRenewalEmails()
    {
      try {
        $stmt = $this->db->prepare("EXEC membership_queue_renewal_emails");
        $stmt->execute();
        do {
          echo '>';
        } while($stmt->nextRowset());
        return true;
      } catch (Exception $e) {
        echo $e->getMessage();
      }
    }
}

First stored proc (heavily cut down)

CREATE PROCEDURE [dbo].[queue_renewal_emails]
AS
BEGIN

    DECLARE @curr_member_cursor     CURSOR;
    DECLARE @curr_club_cursor       CURSOR;
    DECLARE @g_personid             INT;

    DECLARE @g_emailTemplateId      INT;
    DECLARE @g_email_subject        VARCHAR(200);
    DECLARE @g_email_html           VARCHAR(max);
    DECLARE @g_email_plain          VARCHAR(max);

    DECLARE @g_personEmail          VARCHAR(128);

    SET @curr_club_cursor = CURSOR
    LOCAL STATIC READ_ONLY FORWARD_ONLY
    FOR
    SELECT  DISTINCT
            bgemailTemplate.bgemte_name,
            bgemailTemplate.bgemte_emailtemplateid,
            vpersonpe.pers_emailaddress,
            vpersonpe.pers_personId,
    FROM  company               WITH(NOLOCK)
    INNER JOIN complink             WITH(NOLOCK)    ON complink.clli_companyid = complink.Comp_CompanyId
    AND complink.clli_Deleted is null
    INNER JOIN vpersonpe            WITH(NOLOCK)    ON vpersonpe.pers_personId = complink.clli_personId
    INNER JOIN bgemailTemplate      WITH(NOLOCK)    ON bgemailTemplate.bgemte_Deleted IS NULL
    WHERE vPersonPE.pers_deleted                    IS NULL
    AND   company.comp_deleted                      IS NULL
    AND   vPersonPE.pers_parentid                   IS NULL
    AND   vpersonpe.pers_status                     NOT IN ('Cancelled','Expired','Suspended','Awaiting Approval','Declined','On hold','Revoked','Expelled');

    -- loop through each course
    OPEN @curr_club_cursor;
    FETCH NEXT FROM @curr_club_cursor INTO @g_email_subject, @g_emailTemplateId, @g_personEmail, @g_personid;
    WHILE @@fetch_status = 0
    BEGIN
        EXEC dbo.populateEmail @g_emailtemplateid   /* Email template id */,
                                @g_email_plain OUTPUT /* Plain text email to have the placeholders replaced */,
                                @g_email_subject OUTPUT,
                                @g_personid ;
        FETCH NEXT FROM @curr_club_cursor INTO @g_email_subject, @g_emailTemplateId, @g_personEmail, @g_personid;
    END
    CLOSE @curr_club_cursor
    DEALLOCATE @curr_club_cursor

END
go

Second stored proc (heavily cut down).

CREATE PROCEDURE [dbo].[populateEmail]
    @p_emailtemplateid          INT,
    @p_email_text               VARCHAR(max) OUTPUT,
    @p_email_subject            VARCHAR(200) OUTPUT,
    @p_person_id                INT
AS
BEGIN

    SET NOCOUNT ON;

    -- CURSORs
    DECLARE @curr_field_cursor                  CURSOR;
    DECLARE @g_email_plain                      VARCHAR(MAX) = '';
    DECLARE @g_email_subject                    VARCHAR(200) = '';

    DECLARE @g_emte_emailtemplateid             INT;
    DECLARE @g_EmailPlaceholderId               INT;
    DECLARE @g_place_holder                     VARCHAR(128);
    DECLARE @g_source_column                    VARCHAR(128);
    DECLARE @g_prev_source_query_num            INT;
    DECLARE @g_source_query_num                 INT;

    -- Variables to read results into from each query
    DECLARE @g_q11_comp_name                    VARCHAR(180);
    DECLARE @g_q11_comp_website                 VARCHAR(300);
    DECLARE @g_q11_comp_pers_salutation         VARCHAR(30);
    DECLARE @g_q11_comp_pers_firstname          VARCHAR(90);
    DECLARE @g_q11_comp_pers_lastname           VARCHAR(120);
    -- Start processing

    SET @p_email_text = '';

    SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

    SET @curr_field_cursor = CURSOR
    LOCAL STATIC READ_ONLY FORWARD_ONLY
    FOR
    SELECT a.emte_emailtemplateid,
            b.emtl_EmailPlaceholderId,
            c.empl_PlaceHolder,
            c.empl_SourceQueryNum,
            c.empl_SourceColumn,
            a.emte_plaintextemail,
            a.emte_subject
    FROM EmailTemplate a with (nolock)
    LEFT OUTER JOIN  EmailTemplateLink b with (nolock)
    ON a.emte_emailtemplateid = b.emtl_EmailTemplateId
    LEFT OUTER JOIN  EmailPlaceholder c with (nolock)
    ON b.emtl_EmailPlaceholderId = c.empl_EmailPlaceholderID
    WHERE a.emte_emailtemplateid = @p_emailtemplateid
    ORDER BY c.empl_SourceQueryNum;

    -- Loop through each required place holder for the passed email template.

    SET @g_prev_source_query_num = 0;

    OPEN @curr_field_cursor
    FETCH NEXT FROM @curr_field_cursor INTO @g_emte_emailtemplateid, @g_EmailPlaceholderId, @g_place_holder, @g_source_query_num, @g_source_column, @g_email_plain, @g_email_subject;
    WHILE @@fetch_status = 0
    BEGIN

        IF @g_prev_source_query_num = 0
        BEGIN
            SET @p_email_text = @g_email_plain;
            SET @p_email_subject = @g_email_subject;
        END;

        IF @g_source_query_num = 11
        BEGIN
            IF @g_prev_source_query_num != @g_source_query_num
            BEGIN
                SELECT @g_q11_comp_name = comp_name,
                        @g_q11_comp_website = comp_website,
                        @g_q11_comp_pers_salutation = Pers_Salutation,
                        @g_q11_comp_pers_firstname = pers_firstname,
                        @g_q11_comp_pers_lastname = pers_lastname
                FROM company with (nolock)
                LEFT OUTER JOIN vPerson with (nolock) ON company.Comp_PrimaryPersonId = vPerson.Pers_PersonId
                LEFT OUTER JOIN address with (nolock) ON company.Comp_PrimaryAddressId = address.Addr_AddressId
                WHERE company.Comp_CompanyId        = @p_person_id;
            END;

            IF @g_source_column = 'comp_name'
            BEGIN
                SET @p_email_text       = REPLACE(@p_email_text, @g_place_holder, COALESCE(@g_q11_comp_name, ''));
                SET @p_email_subject    = REPLACE(@p_email_subject, @g_place_holder, COALESCE(@g_q11_comp_name, ''));
            END;
            ELSE IF @g_source_column = 'comp_website'
            BEGIN
                SET @p_email_text       = REPLACE(@p_email_text, @g_place_holder, COALESCE(@g_q11_comp_website, ''));
                SET @p_email_subject    = REPLACE(@p_email_subject, @g_place_holder, COALESCE(@g_q11_comp_website, ''));
            END;
            ELSE IF @g_source_column = 'comp_primary_person_firstname'
            BEGIN
                SET @p_email_text       = REPLACE(@p_email_text, @g_place_holder, COALESCE(@g_q11_comp_pers_salutation, ''));
                SET @p_email_subject    = REPLACE(@p_email_subject, @g_place_holder, COALESCE(@g_q11_comp_pers_salutation, ''));
            END;
            ELSE IF @g_source_column = 'comp_primary_person_salutation'
            BEGIN
                SET @p_email_text       = REPLACE(@p_email_text, @g_place_holder, COALESCE(@g_q11_comp_pers_firstname, ''));
                SET @p_email_subject    = REPLACE(@p_email_subject, @g_place_holder, COALESCE(@g_q11_comp_pers_firstname, ''));
            END;
            ELSE IF @g_source_column = 'comp_primary_person_lastname'
            BEGIN
                SET @p_email_text       = REPLACE(@p_email_text, @g_place_holder, COALESCE(@g_q11_comp_pers_lastname, ''));
                SET @p_email_subject    = REPLACE(@p_email_subject, @g_place_holder, COALESCE(@g_q11_comp_pers_lastname, ''));
            END;
        END;

        SET @g_prev_source_query_num = @g_source_query_num;
        FETCH NEXT FROM @curr_field_cursor INTO @g_emte_emailtemplateid, @g_EmailPlaceholderId, @g_place_holder, @g_source_query_num, @g_source_column, @g_email_plain, @g_email_subject;
    END;

    CLOSE @curr_field_cursor
    DEALLOCATE @curr_field_cursor

END
go
  • 写回答

1条回答 默认 最新

  • douhezi2285 2019-08-05 14:52
    关注

    as the first stored proc isn't returning anything ... is there an alternative to while ( $stmt->nextRowset() ) ;

    I'ts probably the rowcount messages causing the client to see empty rowsets. Add SET NOCOUNT ON as the first line of each stored procedure.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 用visual studi code完成html页面
  • ¥15 聚类分析或者python进行数据分析
  • ¥15 逻辑谓词和消解原理的运用
  • ¥15 三菱伺服电机按启动按钮有使能但不动作
  • ¥15 js,页面2返回页面1时定位进入的设备
  • ¥50 导入文件到网吧的电脑并且在重启之后不会被恢复
  • ¥15 (希望可以解决问题)ma和mb文件无法正常打开,打开后是空白,但是有正常内存占用,但可以在打开Maya应用程序后打开场景ma和mb格式。
  • ¥20 ML307A在使用AT命令连接EMQX平台的MQTT时被拒绝
  • ¥20 腾讯企业邮箱邮件可以恢复么
  • ¥15 有人知道怎么将自己的迁移策略布到edgecloudsim上使用吗?