在 INFORMIX 4GL 的报告中对 "ORDER BY" 使用 "IF statement"

Use an "IF statement" on the "ORDER BY" in a report in INFORMIX 4GL

我有一份被调用多次的报告,但我希望每次的顺序都不同。

如何根据变量更改 "order by"。

例如

report print_label()

   If is_reprint
   then
       order by rpt.item_code, rpt.description
   else
       order by rpt.description, rpt.item_code
   end if

我尝试在调用报表时传入一个变量:

let scratch = "rpt.item_code, rpt.description"
start print_label(scratch)

我在报告中做了:

order by scratch

但是没有用……还有其他建议吗?? 谢谢!

您可以在 order by 子句中使用 case 语句,如下所示:

order by 
       case 
         when 1 = 1 then 
            rpt.item_code, rpt.description
         else
            rpt.description, rpt.item_code
       end

简答

I4GL REPORT 函数中的 ORDER BY 子句对实现报告的代码的生成方式具有至关重要的影响。像 run-time.

那样重新连接生成的代码是根本不可行的

因此,您无法直接达到您想要的结果。

备注

请注意,您可能应该使用 ORDER EXTERNAL BY 而不是 ORDER BY — 不同之处在于使用 EXTERNAL,报告可以假设数据以正确的顺序呈现,但是如果没有,报告必须保存所有数据(在数据库中的临时 table 中),然后 select 来自 table 的数据按要求的排序顺序,制作成two-pass 报告。

如果你很勇敢并且拥有 I4GL c-code 编译器,你应该看看为报告生成的代码,但请注意这是你可能会遇到的最可怕的代码在很长的时间。它使用了您自己做梦也想不到的各种技巧。

解决方案——概要

好的;所以你可以直接做。你有什么选择?在我看来,您有两个选择:

  1. 使用两个专门用于选择顺序的参数,然后使用始终以固定顺序列出它们的 ORDER BY(不带 EXTERNAL)子句。但是,当需要使用报告时,请选择您想要参数的顺序。

  2. 写两个报告,仅报告名称和 ORDER EXTERNAL BY 子句不同。根据您想要的顺序安排调用正确的报告。

其中,选项 2 更简单 — 除了维护问题。您很可能会安排从单个副本生成代码。也就是说,您将 REPORT print_label_code_desc 保存在一个文件中,然后安排将其编辑为 REPORT print_label_desc_code(例如,使用 sed)——并且编辑会颠倒名称的顺序在 ORDER BY 子句中。这在 makefile 中并不难,但需要小心。

实践中的选项 1

选项 1 在实践中是什么样的?

DECLARE c CURSOR FOR
    SELECT * FROM SomeTable

START REPORT print_label -- optional specification of destination, etc.

FOREACH c INTO rpt.*
    IF do_item_desc THEN
        OUTPUT TO REPORT print_label(rpt.item_code, rpt.description, rpt.*)
    ELSE
        OUTPUT TO REPORT print_label(rpt.description, rpt.item_code, rpt.*)
    END IF
END FOREACH

FINISH REPORT print_label

报告函数本身可能如下所示:

REPORT print_label(col1, col2, rpt)
    DEFINE col1 CHAR(40)
    DEFINE col2 CHAR(40)
    DEFINE rpt  RECORD LIKE SomeTable.*

    ORDER BY col1, col2

FORMAT

    FIRST PAGE HEADER
        …
    BEFORE GROUP OF col1
        …
    BEFORE GROUP OF col2
        …
    ON EVERY ROW
        …
    AFTER GROUP OF col1
        …
    AFTER GROUP OF col2
        …
    ON LAST ROW
        …
END REPORT

对大纲代码中的任何错误表示歉意;自从我上次编写任何 I4GL 代码以来已经有一段时间了。

关键是按值排序是专门传递给报表的,仅用于控制其组织。你可能需要 能够在 BGO 中为两列打印不同的详细信息(shorthand 用于 BEFORE GROUP OF;AGO 用于 AFTER GROUP OF)部分。这通常由(gasp)全局变量处理——这是 I4GL,它们是开展业务的正常方式。实际上,如果报告 driver 代码(调用 START REPORT、OUTPUT TO REPORT 和 FINISH REPORT 的代码)与报告本身在同一个文件中,它们应该是模块变量而不是全局变量。您需要这样做,因为通常组级别的报告(在 BGO 和 AGO 块中)将需要不同的标题或标签,具体取决于您是在描述之前对代码进行排序,还是相反。请注意,组聚合的含义会根据 ORDER BY 子句中的顺序而变化。

请注意,并非每份报告都适合进行此类重新排序。简单地 运行 不同顺序的 BGO 和 AGO 块不足以使报告输出看起来合理。在这种情况下,您将回到选项 2 — 或选项 2A,即编写两个单独的报告,它们不会假装只是对 ORDER BY 子句的重新排序,因为数据的格式需要根据ORDER BY 子句。

如您所见,这需要小心——比替代方案(选项 2)要小心得多。如果您使用动态 SQL 创建 SELECT 语句,您可以安排将正确的 ORDER BY 子句放入随后准备的字符串中,以便游标将以正确的顺序获取数据 - 允许毕竟你要使用 ORDER EXTERNAL BY。

总结

如果您是 I4GL 的新手,请选择选项 2。如果您的团队在 I4GL 方面经验不足,请选择选项 2。我不太喜欢它,但这是可以的方式易于处理,您自己、您现在的同事以及未来的同事都容易理解。

如果您对 I4GL 相当满意table 并且您的团队对 I4GL 相当有经验——并且报表布局确实有助于动态重组——然后考虑选项 1。它比较棘手,但我过去在 I4GL 中做过更糟糕的事情。

我用于此类问题的技术与

类似
REPORT report_name(x)
DEFINE x RECORD
    param1,param2, ..., paramN ...,
    sort_method ...,
    data ...
END RECORD

ORDER [EXTERNAL] BY x.param1, x.param2, ..., x.paramN

BEFORE GROUP OF x.param1
    CASE 
        WHEN x.sort_method ...
            PRINT ...
        WHEN x.sort_method ...
            PRINT ...
    END CASE

BEFORE GROUP OF x.param2
    # similar technique as above

...

BEFORE GROUP OF x.paramN
    # similar technique as above

ON EVERY ROW
    PRINT ...

AFTER GROUP OF x.paramN
   # similar technique as above
...

AFTER GROUP OF x.param2
    # similar technique as above

AFTER GROUP OF x.param1
    # similar technique as above

...然后在调用 REPORT 的 4gl 中,使用用于排序的所需参数填充 x.param1、x.param2、...、x.paramN,例如

CASE x.sort_method 
    WHEN "product,branch"
        LET x.param1 = x.data.product_code
        LET x.param2 = x.data.branch_code
    WHEN "branch,product"
        LET x.param1 = x.data.branch_code
        LET x.param2 = x.data.product_code
END CASE
OUTPUT TO REPORT report_name(x.*)

因此,根据我的示例,这是我见过并用于股票报告等内容的技术。 warehouse/branch/store 经理希望看到 warehouse/branch/store 订购的东西,然后是 product/sku/item,而产品经理希望看到 product/sku/item,然后 warehouse/branch/store 订购的东西].可以使用相同的技术完成具有更多潜在参数的更多分析报告。我认为我看到的记录是 6。所以在那种情况下,一份报告涵盖所有 6!=720 个潜在组合要好得多,而不是为每个可能的订单组合编写单独的报告。

所以可能类似于 Jonathan 选项 1,尽管我对复杂性没有相同的保留意见。我不记得在代码审查时发现我的初级开发人员犯了严重的错误。事实上,如果报告足够通用,您会发现您不需要经常触摸它。

您可以使用准备:

let query_txt="select ... "
If is_reprint then
  let query_txt=query_txt clipped, " order by rpt.item_code, 
  rpt.description"
else
  let query_txt=query_txt clipped, " order by rpt.description, 
  rpt.item_code"
end if
prepare statement1 from query_txt
declare cursor_name cursor for statement1

现在开始报告,使用 foreach 等等...

P.S。您必须将 query_txt 定义为足够长的字符以供整个文本使用。