代码之家  ›  专栏  ›  技术社区  ›  Mike Sickler

如何将参数设置为BIRT报告中的值列表?

  •  8
  • Mike Sickler  · 技术社区  · 14 年前

    我有一个带有如下查询的数据集:

    select s.name, w.week_ending, w.sales 
    from store s, weekly_sales_summary w 
    where s.id=w.store_id and s.id = ?
    

    我想修改查询以允许我指定存储ID列表,例如:

    select s.name, w.week_ending, w.sales 
    from store s, weekly_sales_summary w 
    where s.id=w.store_id and s.id IN (?)
    

    我如何在BIRT中完成这项工作?我需要指定哪种参数?

    4 回复  |  直到 14 年前
        1
  •  9
  •   user359040    14 年前

    简单部分是报告参数:将显示类型设置为列表框,然后选中允许多个值选项。

    现在最难的部分是:不幸的是,您不能将多值报表参数绑定到数据集参数(至少在3.2版中没有,我使用的是这个版本)。Birt World博客上有一个帖子: http://birtworld.blogspot.com/2009/03/birt-multi-select-statements.html 它描述了如何使用代码插件将多选报表参数绑定到报表数据集。

    不幸的是,当我尝试它时,它不起作用。如果你能让它工作,那就是我推荐的方法;如果你不能,那么另一种方法是修改数据集的querytext,在适当的点将report参数中的所有值插入到查询中。假设 s.id 是数字,这里有一个函数可以粘贴到 数据源 :

    function fnMultiValParamSql ( pmParameterName, pmSubstituteString, pmQueryText )
    {
    strParamValsSelected=reportContext.getParameterValue(pmParameterName);
    strSelectedValues="";
    for (var varCounter=0;varCounter<strParamValsSelected.length;varCounter++)
    {
        strSelectedValues += strParamValsSelected[varCounter].toString()+",";
    }
    strSelectedValues = strSelectedValues.substring(0,strSelectedValues.length-1);
    return pmQueryText.replace(pmSubstituteString,strSelectedValues);
    }
    

    然后可以从的beforeopen事件脚本调用 数据集 ,像这样:

    this.queryText = fnMultiValParamSql ( "rpID", "0 /*rpID*/", this.queryText );
    

    假设您的报告参数称为rpid。您需要修改您的查询,如下所示:

    select s.name, w.week_ending, w.sales 
    from store s, weekly_sales_summary w 
    where s.id=w.store_id and s.id IN (0 /*rpID*/)
    

    0包含在脚本中,以便查询脚本在设计时有效,并且数据集值将正确绑定到报表;在运行时,将删除此硬编码0。

    但是,这种方法可能非常危险,因为它可能使您容易受到SQL注入攻击: http://en.wikipedia.org/wiki/SQL_injection ,如本文所示: http://xkcd.com/327/ .

    如果是从预先定义的选择列表中选择的纯数字值,则不可能进行SQL注入攻击;但是,在允许参数的自由格式输入字符串的情况下,同样的方法很容易受到攻击。

        2
  •  5
  •   bluish dmajkic    12 年前

    仅供参考:BIRT World的文章应该有效(我写的),但这是对这个问题的早期解决方案。

    我们已经创建了一个开源插件,您可以将它添加到BIRT中,这样可以更清晰地解决这个问题。BIRT函数库中的绑定参数函数提供了一种从多值参数中进行多重选择的简单方法。

    如果您仍然感兴趣,请查看 birt-functions-lib project 在Eclipse实验室。

        3
  •  2
  •   gnomie    13 年前

    这是另一个。基于我在别处找到的一些提示,我进行了扩展以保留数据集SQL中的参数数量。此解决方案与您在打开数据集之前调用的javascript函数一起使用:

    prepare(this);
    
    function prepare(dataSet) {
        while (dataSet.queryText.indexOf("@IN?")>=0) {
            dataSet.queryText = dataSet.queryText.replace(
                "@XYZ?", 
                "('"+params["products"].value.join("','")+"') or ?=0"
            );
        }
    }
    

    在查询中,替换出现的(?)@ XYZ?以上方法确保 查询具有实际值,仍然是一个参数(这样数据集编辑器和预览就不会抱怨)。

    注意:注意SQL注入,例如不允许字符串值

        4
  •  2
  •   bluish dmajkic    12 年前

    我创建了一个更通用的解决方案,它处理 可选/必需参数 行为也是如此。当参数不是必需的并且用户没有选择任何值时,IN子句将被禁用。它还允许用户同时选择实值和空值。

    在报告中 initialize 脚本I添加此代码:

    /** Fullfill IN-clause in a data set query,
     *  using a List box report parameter.
     *  Placeholder must be the parentheses after IN keyword with wathever you want inside.
     *  If required is false then the whole IN-clause in the query 
     *  must be surrounded by parentheses.
     *  dataType and required refers to the parameter, they must be passed, 
     *  but should be better to find a way to retrieve them inside this function
     *  (given parameter name).
     */
    function fulfillInClause(dataSet, placeholder, param, dataType, required) {
    
        if (dataSet.queryText.indexOf(placeholder)>=0) {
    
            var paramValue = params[param].value;
            var emptyParam = (paramValue==null || paramValue.length<=0);
    
            //build the list of possible values
            //  paramValue==null check in ternary operators 
            //  will prevent exceptions when user doesn't select any value
            //  (it will not affect the query if param is optional, 
            //  while we will never arrive here if it is required)
            var replacement = " (";
            if (dataType == "string")
                replacement += (emptyParam ? "''" : createList(paramValue, ",", "'", "varchar(10)") );
            else if (dataType == "integer")
                replacement += (emptyParam ? "0"  : createList(paramValue, ",", "" , "int"        ) );
            else
                //TODO implement more cases
                return;
            replacement += ") ";
    
            //if param is not required and user doesn't select any value for it
            //then nullify the IN clause with an always-true clause
            if (!required && emptyParam)
                replacement += " or 0=0 ";
    
            //put replacement in the query
            dataSet.queryText = dataSet.queryText.replace( placeholder, replacement );
            //DEBUG
            params["debug" + dataSet.name + "Query"]=dataSet.queryText;        
        }
    }
    
    /** Create a string list of array values,
     *  separated by separator and each of them surrounded by a pair surrounders
     */
    function createList(array, separator, surrounder, sqlDataType){
        var result = "";
    
        for(var i=0; i<array.length; i++) {
    
            if(result.length>0)
                result += separator;
    
            if(array[i]!=null)
                result += surrounder + array[i] + surrounder;
            else
                result += "cast(null as " + sqlDataType + ")";
        }
        return result;
    }
    

    使用实例

    在数据集查询中,将特殊IN子句:

    select F1, F2
    from T1 
    where F3='Bubi'
      and ( F4 in (''/*?customers*/) )
    

    beforeOpen 带有IN子句的数据集脚本写入:

    fulfillInClause(this, "(''/*?customers*/)", "customers", "string", false);
    

    注意,我使用了一个占位符,它允许查询也在替换之前运行(例如,它有引号,因为f4是varchar)。 您可以构建一个适合您的案例的占位符。