2012-09-05 84 views
1

我刚刚发布了一些随机导致错误的代码。我已经通过完全改变查询方式来解决问题。然而,我仍然困扰我,我不知道是什么原因造成的问题,所以想知道是否有人可能知道答案。我有一个存储过程中的以下查询。我不是在寻找有关使用嵌套函数调用进行查询的好实践的评论以及诸如此类的东西:-)。只是真的想找出为什么它不一致工作。随机查询中的函数将返回一个非数字值并导致连接出错。但是,如果我立即重新运行查询,它工作正常。SQL Server 2008不一致的结果

SELECT  cscsf.cloud_server_current_software_firewall_id, 
       dbo.fn_GetCustomerFriendlyFromRuleName(cscsf.rule_name, np.policy_name) as rule_name, 
       cscsf.rule_action, 
       cscsf.rule_direction, 
       cscsf.source_address, 
       cscsf.source_mask, 
       cscsf.destination_address, 
       cscsf.destination_mask, 
       cscsf.protocol, 
       cscsf.port_or_port_range, 
       cscsf.created_date_utc, 
       cscsf.created_by 
    FROM  CLOUD_SERVER_CURRENT_SOFTWARE_FIREWALL cscsf 
    LEFT JOIN CLOUD_SERVER cs 
    ON   cscsf.cloud_server_id = cs.cloud_server_id 
    LEFT JOIN CLOUD_ACCOUNT cla 
    ON   cs.cloud_account_id = cla.cloud_account_id 
    LEFT JOIN CONFIGURATION co 
    ON   cla.configuration_id = co.configuration_id 
    LEFT JOIN DEDICATED_ACCOUNT da 
    ON   co.dedicated_account_id = da.dedicated_account_id 
    LEFT JOIN CORE_ACCOUNT ca 
    ON   da.core_account_number = ca.core_account_id 
    LEFT JOIN NETWORK_POLICY np 
    ON   np.network_policy_id = (select dbo.fn_GetIDFromRuleName(cscsf.rule_name)) 
    WHERE  cs.cloud_server_id = @cloud_server_id 
    AND   cs.current_software_firewall_confg_guid = cscsf.config_guid 
    AND   ca.core_account_id IS NOT NULL 
    ORDER BY cscsf.rule_direction, cscsf.cloud_server_current_software_firewall_id 

如果您发现该加入

ON   np.network_policy_id = (select dbo.fn_GetIDFromRuleName(cscsf.rule_name)) 

调用一个函数。

这里是一个函数:

ALTER FUNCTION [dbo].[fn_GetIDFromRuleName] 
(
    @rule_name    varchar(100) 
) 
RETURNS varchar(12) 
AS 
BEGIN 
    DECLARE  @value  varchar(12) 

     SET @value = dbo.fn_SplitGetNthRow(@rule_name, '-', 2) 
     SET @value = dbo.fn_SplitGetNthRow(@value, '_', 2) 
     SET @value = dbo.fn_SplitGetNthRow(@value, '-', 1) 

    RETURN  @value 
END 

,然后调用这个函数:

ALTER FUNCTION [dbo].[fn_SplitGetNthRow] 
(
    @sInputList  varchar(MAX), 
    @sDelimiter  varchar(10) = ',', 
    @sRowNumber  int = 1 
) 
RETURNS varchar(MAX) 
AS 
BEGIN 
    DECLARE  @value  varchar(MAX) 

    SELECT  @value = data_split.item 
         FROM 
         (
          SELECT *, ROW_NUMBER() OVER (ORDER BY (SELECT 1)) as row_num FROM dbo.fn_Split(@sInputList, @sDelimiter) 
         ) AS data_split 
         WHERE 
         data_split.row_num = @sRowNumber 

    IF   @value IS NULL 
     SET  @value = '' 

    RETURN  @value 
END 

这最终调用这个函数:

ALTER FUNCTION [dbo].[fn_Split] (
    @sInputList VARCHAR(MAX), 
    @sDelimiter VARCHAR(10) = ',' 
) RETURNS @List TABLE (item VARCHAR(MAX)) 
BEGIN 
    DECLARE @sItem VARCHAR(MAX) 
    WHILE CHARINDEX(@sDelimiter,@sInputList,0) <> 0 
     BEGIN 
      SELECT @sItem=RTRIM(LTRIM(SUBSTRING(@sInputList,1,CHARINDEX(@sDelimiter,@sInputList,0)-1))), @sInputList=RTRIM(LTRIM(SUBSTRING(@sInputList,CHARINDEX(@sDelimiter,@sInputList,0)+LEN(@sDelimiter),LEN(@sInputList)))) 
      IF LEN(@sItem) > 0 
       INSERT INTO @List SELECT @sItem 
     END 

    IF LEN(@sInputList) > 0 
     INSERT INTO @List SELECT @sInputList -- Put the last item in 
    RETURN 
END 
+0

np.network_policy_id的数据类型是什么? –

+0

您正在处理隐式数值转换。该函数返回一个'varchar(12)'数据类型,并且你正在udf中进行字符串操作,因此你确实有可能获得非数字数据,而且你没有执行任何验证来检查返回数据是否为数字(假设那就是你想要的)。因此连接中的错误。 – Kash

+0

np.network_policy_id是一个int。我知道我得到一个错误,因为函数返回一个varchar(12),不能转换为整数。具体来说,函数返回值'NPG'。我不明白的是为什么它随机返回不同的东西。 –

回答

6

的原因是 “随机” 返回不同的事情与SQL Server如何优化查询以及它们如何短路有关。要解决这个问题

一种方法是改变fn_GetIDFromRuleName的返回值:

return (case when isnumeric(@value) then @value end) 

或者,改变连接条件:

on np.network_policy_id = (select case when isnumeric(dbo.fn_GetIDFromRuleName(cscsf.rule_name)) = 1) 
             then dbo.fn_GetIDFromRuleName(cscsf.rule_name) end) 

根本问题是评价的顺序。 “case”语句修复问题的原因是因为它在转换前检查数字值 SQL Server保证case语句中的评估顺序。请注意,转换数字(如数字“6e07”或“1.23”)仍然存在问题,但不是整数。

它为什么有时会工作?很显然,查询执行计划正在发生变化,无论是静态还是动态。失败的情况可能是由WHERE条件排除的行。为什么它会尝试进行转换?问题在于转换发生在哪里。

转换是否发生取决于查询计划。这可能依次取决于何时读取表格cscf。如果它已经在成员中,那么它可能会被读取并试图转换为查询中的第一步。然后你会得到错误。在另一种情况下,另一个表可能是filtererd,并在转换之前删除行。

在任何情况下,我的建议是:

  • 从来没有在查询中隐式转换。
  • 使用case语句进行显式转换。
  • 不要依赖WHERE子句来过滤数据以使转换工作。使用case语句。