mysql存储过程group_concat为游标中的选择返回null

Posted

技术标签:

【中文标题】mysql存储过程group_concat为游标中的选择返回null【英文标题】:mysql stored procedure group_concat returning null for select in cursor 【发布时间】:2016-10-31 02:54:40 【问题描述】:

我有一个存储过程,我在游标中循环遍历结果。我遇到的问题是 license_attributes 值在不应该为空时始终为空。如果我在存储过程之外执行 select 语句,或者作为调试在存储过程中的游标之外执行 select 语句,我会得到我期望的结果(不是 null)

这是选择中始终在光标中返回 null 的部分:

        (SELECT 
            CONCAT('""',sf.Asset_Attribute__c.Type__c,'"": ',GROUP_CONCAT(
            '""',sf.Asset_Attribute__c.Key__c,'"":""',LOWER(sf.Asset_Attribute__c.Value__c),'""'
            ),'')
            FROM 
                sf.Asset_Attribute__c 
            WHERE 
                sf.Asset_Attribute__c.Asset__c = license_id
            GROUP BY sf.Asset_Attribute__c.Asset__c) AS `license_attributes`

这是存储过程的部分:

    GETCLOUDACCOUNTS:BEGIN

    DECLARE no_more_cloud_accounts_records boolean DEFAULT FALSE;

    DECLARE company VARCHAR(255) DEFAULT null;
    DECLARE license_status VARCHAR(50) DEFAULT null;
    DECLARE license_id VARCHAR(18) DEFAULT null;
    DECLARE cloud_owner_email VARCHAR(255) DEFAULT null;
    DECLARE entitlement_plan VARCHAR(255) DEFAULT null;
    DECLARE role VARCHAR(500) DEFAULT null;
    DECLARE is_trial BOOLEAN DEFAULT false;
    DECLARE license_attributes VARCHAR(2000) DEFAULT null;
    DECLARE zuora_account_id VARCHAR(100) DEFAULT '';
    DECLARE zuora_account_number VARCHAR(50) DEFAULT null;
    DECLARE zuora_account_status VARCHAR(50) DEFAULT null;
    DECLARE zuora_account_last_invoice_date DATETIME DEFAULT null;
    DECLARE has_active_subscriptions BOOLEAN DEFAULT false;

    DECLARE cloud_accounts_cursor CURSOR FOR 
        SELECT
            (SELECT `sf`.`Contact`.`CompanyName__c` FROM `sf`.`Contact` WHERE `sf`.`Asset`.`ContactId`=`sf`.`Contact`.`Id`) AS `company`,
            `sf`.`License_Key_Association__c`.`License_Key_Status__c` AS `license_status`,
            `sf`.`License_Key_Association__c`.`License_Key__c` AS `license_id`,
            `sf`.`Asset`.`ContactEmail__c` AS `cloud_owner_email`,
            (SELECT `sf`.`Contact`.`CloudEntitlementPlan__c` FROM `sf`.`Contact` WHERE `sf`.`Asset`.`ContactId`=`sf`.`Contact`.`Id`) AS `entitlement_plan`,
            `sf`.`License_Key_Association__c`.`Role__c` AS `role`,
            IF( (SELECT `sf`.`Product2`.`IsCommercial__c` FROM `sf`.`Product2` WHERE `sf`.`Product2`.`Id`=`sf`.`Asset`.`Product2Id`) = 0,true,false ) AS `is_trial`,
            (SELECT 
                CONCAT('""',sf.Asset_Attribute__c.Type__c,'"": ',GROUP_CONCAT(
                '""',sf.Asset_Attribute__c.Key__c,'"":""',LOWER(sf.Asset_Attribute__c.Value__c),'""'
                ),'')
                FROM 
                    sf.Asset_Attribute__c 
                WHERE 
                    sf.Asset_Attribute__c.Asset__c = license_id
                GROUP BY sf.Asset_Attribute__c.Asset__c) AS `license_attributes`
        FROM
            `sf`.`License_Key_Association__c`
        LEFT JOIN `sf`.`Asset`
            ON `sf`.`License_Key_Association__c`.`License_Key__c` = `sf`.`Asset`.`Id` 
        JOIN `sf`.`Contact`
            ON `sf`.`Contact`.`Id` = `sf`.`License_Key_Association__c`.`Contact__c`
        WHERE
            `sf`.`Contact`.`ExternalID__c`='someexternalidhere';

    DECLARE CONTINUE HANDLER FOR NOT FOUND SET no_more_cloud_accounts_records = true;
        SELECT
            (SELECT `sf`.`Contact`.`CompanyName__c` FROM `sf`.`Contact` WHERE `sf`.`Asset`.`ContactId`=`sf`.`Contact`.`Id`) AS `company`,
            `sf`.`License_Key_Association__c`.`License_Key_Status__c` AS `license_status`,
            `sf`.`License_Key_Association__c`.`License_Key__c` AS `license_id`,
            `sf`.`Asset`.`ContactEmail__c` AS `cloud_owner_email`,
            (SELECT `sf`.`Contact`.`CloudEntitlementPlan__c` FROM `sf`.`Contact` WHERE `sf`.`Asset`.`ContactId`=`sf`.`Contact`.`Id`) AS `entitlement_plan`,
            `sf`.`License_Key_Association__c`.`Role__c` AS `role`,
            IF( (SELECT `sf`.`Product2`.`IsCommercial__c` FROM `sf`.`Product2` WHERE `sf`.`Product2`.`Id`=`sf`.`Asset`.`Product2Id`) = 0,true,false ) AS `is_trial`,
            (SELECT 
                CONCAT('""',sf.Asset_Attribute__c.Type__c,'"": ',GROUP_CONCAT(
                '""',sf.Asset_Attribute__c.Key__c,'"":""',LOWER(sf.Asset_Attribute__c.Value__c),'""'
                ),'')
                FROM 
                    sf.Asset_Attribute__c 
                WHERE 
                    sf.Asset_Attribute__c.Asset__c = license_id
                GROUP BY sf.Asset_Attribute__c.Asset__c) AS `license_attributes`
        FROM
            `sf`.`License_Key_Association__c`
        LEFT JOIN `sf`.`Asset`
            ON `sf`.`License_Key_Association__c`.`License_Key__c` = `sf`.`Asset`.`Id` 
        JOIN `sf`.`Contact`
            ON `sf`.`Contact`.`Id` = `sf`.`License_Key_Association__c`.`Contact__c`
        WHERE
            `sf`.`Contact`.`ExternalID__c`=@p_externalId;

    OPEN cloud_accounts_cursor;
    CLOUDACCOUNTSLOOP: loop

        fetch cloud_accounts_cursor into company, license_status, license_id, cloud_owner_email, entitlement_plan, role, is_trial, license_attributes;

        IF is_trial = true THEN
            SET has_active_subscriptions = true;
        END IF;

        SET zuora_account_id = `z`.`getZAccountId`(cloud_owner_email);

        IF zuora_account_id IS NOT NULL THEN
            SELECT `accountNumber`,`status`,`lastInvoiceDate` INTO zuora_account_number,zuora_account_status,zuora_account_last_invoice_date FROM zuora.Account WHERE id=zuora_account_id;

            IF has_active_subscriptions = false THEN
                SET has_active_subscriptions = (SELECT IF((SELECT COUNT(*) FROM `z`.`RatePlan`
                    RIGHT JOIN `z`.`ProductRatePlan` ON `z`.`RatePlan`.`productRatePlanId` = `z`.`ProductRatePlan`.`id`
                    LEFT JOIN `z`.`Subscription` ON `z`.`RatePlan`.`subscriptionId` = `z`.`Subscription`.`id`
                    WHERE
                    `z`.`ProductRatePlan`.`wowzaRatePlanCode__c` IN ( (SELECT `code` FROM `z`.`zCloudRatePlanCodes`) )
                    AND `z`.`Subscription`.`status` = 'Active'
                    AND `z`.`Subscription`.`accountId` = zuora_account_id ) > 0, true, false));
            END IF;
        END IF;

        REPLACE INTO `sf`.`zCloudAccounts` (`user_email`,`company`,`license_status`,`license_id`,`cloud_owner_email`,`entitlement_plan`,`role`,`is_trial`,`attributes`,`zuora_account_id`,`zuora_account_number`,`zuora_account_status`,`zuora_account_last_invoice_date`,`has_active_subscriptions`) VALUES(@p_userEmail,company,license_status,license_id,cloud_owner_email,entitlement_plan,role,is_trial,license_attributes,zuora_account_id,zuora_account_number,zuora_account_status,zuora_account_last_invoice_date,has_active_subscriptions);

        IF no_more_cloud_accounts_records THEN
            CLOSE cloud_accounts_cursor;
            LEAVE CLOUDACCOUNTSLOOP;
        end if;

    END LOOP CLOUDACCOUNTSLOOP;

END GETCLOUDACCOUNTS;

如果我在 GETCLOUDACCOUNTS 块之外执行完整的 select stateout,我会得到预期的结果:

company, license_status, license_id, cloud_owner_email, entitlement_plan, role, is_trial, license_attributes
Test Company, Active, 02iq0000000jKgMAAU, myemail@email.com, Standard, Owner, 0, ""cloud"": ""cloud_num_247_t_streams"":""0"",""cloud_num_247_p_streams"":""0""
Test Company, Active, 02iq0000000xlBBAAY, otheremail@email.com, Standard, Admin;wcl_admin;wcl_support, 0, ""cloud"": ""cloud_num_247_t_streams"":""1"",""cloud_num_247_p_streams"":""1"",""test_attribute"":""true"",""api_access"":""true""

但块内的结果显示 license_attributes 为空:

company, license_status, license_id, cloud_owner_email, entitlement_plan, role, is_trial, license_attributes
Test Company, Active, 02iq0000000jKgMAAU, myemail@email.com, Standard, Owner, 0, null
Test Company, Active, 02iq0000000xlBBAAY, otheremail@email.com, Standard, Admin;wcl_admin;wcl_support, 0, null

非常感谢任何帮助!

【问题讨论】:

【参考方案1】:

您可以COALESCE 处理您的 GROUP_CONCAT 中的 NULL 值

COALESCE(`YourColumnName`,'')

您还可以使用显着不同的字符串来显示 NULL 值的来源,以便您可以修复查询。

COALESCE(`YourColumnName`,'**UNEXPECTED NULL**')

【讨论】:

【参考方案2】:

我怀疑这个问题与过程变量 license_id 有关。

SELECT 列表中的相关子选择包括

 WHERE sf.Asset_Attribute__c.Asset__c = license_id
                                        ^^^^^^^^^^

局部变量优先于列引用。由于license_id在代码块中被声明为变量,

 DECLARE license_id VARCHAR(18) DEFAULT null;

SELECT 语句中对license_id 的引用是对该过程变量的引用。

在代码块之外,可能没有名为license_id 的局部变量。所以同样的 SQL SELECT 语句,对license_id 的引用不是对变量的引用,而是对列的引用。

我没有追踪所有的逻辑,或者license_id 变量的内容。但我怀疑这解释了在代码块内与块外执行的语句所观察到的行为的差异。

【讨论】:

果然!谢谢你。将其更改为引用 sf.License_Key_Association__c.License_Key__c 而不是 license_id 变量,瞧! 我不确定为什么这个问题被否决了。我不认为这是一个坏问题。我投票赞成这个问题 (+10) 不是因为这是一个很好的问题,而是为了平衡不应得的反对票。 谢谢。我也不知道为什么它被否决了。我认为我符合有效问题的所有标准。 我的偏好是总是限定所有列引用。这样做的一个重要原因是将语句与更改隔离开......就像稍后将具有相同名称的列添加到查询中使用的表中......之前明确的内容可能变得不明确,从而破坏了语句。 (通过限定列引用,我的语句不会中断。)或者,在这种情况下,如果名为 license_id 的局部变量不存在,但后来被添加。限定列引用也有助于未来的读者,因为它不会强迫他们在表定义中寻找列名 说得通,我坚信会继续这样做。谢谢!

以上是关于mysql存储过程group_concat为游标中的选择返回null的主要内容,如果未能解决你的问题,请参考以下文章

mysql中存储过程和游标调用问题

MySql 存储过程.... group_concat 中的错误

MySQL存储过程,处理多个游标和查询结果

MySQL 存储过程,获取使用游标查询的结果集

mysql存储过程 游标双重循环

mysql之——存储过程 + 游标 + 事务