【问题标题】:Progress / 4GL: Export table as .csv with column names and double quotes?Progress / 4GL:将表格导出为带有列名和双引号的 .csv?
【发布时间】:2015-07-09 20:16:38
【问题描述】:

我正在尝试编写一个 .p 脚本,它将数据库中的表导出为 csv。以下代码创建 csv:

OUTPUT TO VALUE ("C:\Users\Admin\Desktop\test.csv").

FOR EACH table-name NO-LOCK:
    EXPORT DELIMITER "," table-name.
END.

OUTPUT CLOSE.
QUIT.

但是,我不知道如何用双引号封装所有字段。我也无法弄清楚如何让 .csv 的第一行具有表的列名。怎么做呢?

我对 Progress / 4GL 非常陌生。最初我使用 R 和 ODBC 连接来导入和格式化表格,然后将其保存为 csv。但我了解到我正在使用的 ODBC 驱动程序不能可靠地工作……有时它不会返回表中的所有行。

最终目标是将参数(表名)传递给 .p 脚本,该脚本会将表导出为 csv。然后我可以在 R 中导入 csv,操作/格式化数据,然后再次将表格导出为 csv。

任何建议将不胜感激。

编辑:

  • 我使用的Progress版本是9.1D

  • 使用上面的代码,输出可能如下所示...

    "ACME",01,"Some note that may contain carriage returns.\n More text",yes,"01A"
    
  • 之所以尝试用双引号封装每个字段,是因为某些字段可能包含回车或其他特殊字符。 R 并不总是喜欢在字段中间回车。所以想要的输出是......

    "ACME","01","Some note that may contain carriage returns.\n More text","yes","01A"
    

【问题讨论】:

  • 圣牛 9.1D 已经超越了古老......对于大多数更优雅的解决方案来说已经如此了 :-) 你不想摆脱回车吗?
  • @TheMadDBA 我目前有一个使用 gsub 删除任何“特殊字符”的 R 脚本。但是,如果我可以在导出 csv 之前找到并替换任何这些“特殊字符”,那可能是解决问题的更好方法!
  • 在编辑后的答案中尝试新版本。如果您有要替换的 ascii 字符值列表,应该很容易修改。

标签: r csv odbc progress-4gl


【解决方案1】:

了解进度版本很重要。您的 ODBC 问题可能是由于进行中的格式是默认显示格式,实际上并不限制要存储的数据量。这当然会让 SQL 发疯。

您可以使用此知识库了解 DBTool 实用程序来修复 SQL 宽度http://knowledgebase.progress.com/articles/Article/P24496

就导出而言,您所做的已经处理了字符列的双引号。根据您的 Progress 版本,您有几个选项可以解决标题问题。无论您的版本如何,这个都可以使用,但不如新选项那么优雅....

基本上将其复制到过程编辑器中,它将为数据库中的每个表生成一个包含内部过程的程序。通过传入表名和所需的 csv 文件来运行 csvdump.p(运行 csvdump.p ("mytable","myfile")。

免责声明您可能会遇到一些无法像 RAW 那样导出但并不常见的奇怪数据类型。

DEF VAR i AS INTEGER NO-UNDO.

OUTPUT TO csvdump.p.

PUT UNFORMATTED 
    "define input parameter ipTable as character no-undo." SKIP
    "define input parameter ipFile  as character no-undo." SKIP(1)
    "OUTPUT TO VALUE(ipFile)." SKIP(1)
    "RUN VALUE('ip_' + ipTable)." SKIP(1)
    "OUTPUT CLOSE." SKIP(1).

FOR EACH _file WHERE _file._tbl-type = "T" NO-LOCK:

    PUT UNFORMATTED "PROCEDURE ip_" _file._file-name ":" SKIP(1)
                    "EXPORT DELIMITER "~",~"" SKIP.

    FOR EACH _field OF _File NO-LOCK BY _Field._Order:

      IF _Field._Extent = 0 THEN
         PUT UNFORMATTED "~"" _Field-Name "~"" SKIP.
      ELSE DO i = 1 TO _Field._Extent:
          PUT UNFORMATTED "~"" _Field-Name STRING(i,"999") "~"" SKIP.
      END.
    END.

    PUT UNFORMATTED "." SKIP(1)
                    "FOR EACH " _File._File-name " NO-LOCK:" SKIP
                    "    EXPORT DELIMITER "~",~" " _File._File-Name "." SKIP
                    "END." SKIP(1).

    PUT UNFORMATTED "END PROCEDURE." SKIP(1).

END.

OUTPUT CLOSE.

BIG 免责声明....我没有 9.1D 可供测试,因为它已经过了支持的日期....不过我相信所有这些都会奏效。

即使在 9.1D(动态查询)中也有其他方法可以做到这一点,但如果需要,这可能会更容易修改,因为您是 Progress 的新手。此外,它可能比纯粹的动态导出表现更好。您可以继续嵌套 REPLACE 函数以删除越来越多的字符...或者只是复制替换行并让它在需要时一遍又一遍地运行。

DEF VAR i AS INTEGER NO-UNDO.

FUNCTION fn_Export RETURNS CHARACTER (INPUT ipExtent AS INTEGER):
  IF _Field._Data-Type = "CHARACTER" THEN
    PUT UNFORMATTED "fn_Trim(".

  PUT UNFORMATTED _File._File-Name "." _Field._Field-Name.

  IF ipExtent > 0 THEN
    PUT UNFORMATTED "[" STRING(ipExtent) "]" SKIP.

  IF _Field._Data-Type = "CHARACTER" THEN
    PUT UNFORMATTED ")".

  PUT UNFORMATTED SKIP.

END.

OUTPUT TO c:\temp\wks.p.

PUT UNFORMATTED 
    "define input parameter ipTable as character no-undo." SKIP
    "define input parameter ipFile  as character no-undo." SKIP(1)

    "function fn_Trim returns character (input ipChar as character):" SKIP
    "   define variable cTemp as character no-undo." SKIP(1)
    "   if ipChar = '' or ipChar = ? then return ipChar." SKIP(1)
    "   cTemp = replace(replace(ipChar,CHR(13),''),CHR(11),'')." SKIP(1)
    "   return cTemp." SKIP(1)
    "end." SKIP(1)

    "OUTPUT TO VALUE(ipFile)." SKIP(1)
    "RUN VALUE('ip_' + ipTable)." SKIP(1)
    "OUTPUT CLOSE." SKIP(1).

FOR EACH _file WHERE _file._tbl-type = "T" NO-LOCK:

    PUT UNFORMATTED "PROCEDURE ip_" _file._file-name ":" SKIP(1)
                    "EXPORT DELIMITER "~",~"" SKIP.

    FOR EACH _field OF _File NO-LOCK BY _Field._Order:

      IF _Field._Extent = 0 THEN
         PUT UNFORMATTED "~"" _Field-Name "~"" SKIP.
      ELSE DO i = 1 TO _Field._Extent:
          PUT UNFORMATTED "~"" _Field-Name STRING(i) "~"" SKIP.
      END.
    END.

    PUT UNFORMATTED "." SKIP(1)
                    "FOR EACH " _File._File-name " NO-LOCK:" SKIP.

    PUT UNFORMATTED "EXPORT DELIMITER ~",~"" SKIP.

    FOR EACH _field OF _File NO-LOCK BY _Field._Order:

      IF _Field._Extent = 0 OR _Field._Extent = ? THEN 
         fn_Export(0).

      ELSE DO i = 1 TO _Field._Extent:
         fn_Export(i).
      END.
    END.

    PUT UNFORMATTED "." SKIP(1)
                    "END." SKIP(1).

    PUT UNFORMATTED "END PROCEDURE." SKIP(1).

END.

OUTPUT CLOSE.

【讨论】:

    【解决方案2】:

    我希望与@TheMadDBA 在一点上有所不同。使用 EXPORT 将不会处理以 CSV 样式引用输出中的所有字段。例如,逻辑字段不会被引用。

    'CSV 格式'是最模糊的标准,但导出命令不符合它。它不是为此而设计的。 (我注意到在@TheMadDBA 的最后一个示例中,他们也不使用导出。)

    如果您希望引用所有非数字字段,则需要自己处理。

    def stream s.
    output stream s to value(v-filename).
    
    for each tablename no-lock:  
        put stream s unformatted
                '"' tablename.charfield1 '"'
                ',' string(tablename.numfield)
                ',"' tablename.charfield2 '"'
                skip.
    end.
    
    output stream s close.
    

    在此示例中,我假设您可以为单个表编写特定转储,而不是通用解决方案。您当然可以使用 @TheMadDBA 的答案中的元编程,使用 ABL 的动态查询语法,或者甚至使用 - 愿上帝原谅我们俩 - 包含文件来完成后者。但这是一个更高级的话题,你说你刚刚开始学习 ABL。

    您仍然需要按照@TheMadDBA 的回答处理字符串截断。

    【讨论】:

      【解决方案3】:

      在@TheMadDBA 的一些启发和额外的想法之后,这是我对问题的解决方案......

      我决定用 R 编写一个脚本来生成 p 脚本。 R 脚本使用一个输入,即表名并转储 p 脚本。

      下面是一个示例 p 脚本...

      DEFINE VAR columnNames AS CHARACTER.
      columnNames = """" + "Company" + """" + "|" + """" + "ABCCode" + """" + "|" + """" + "MinDollarVolume" + """" + "|" + """" + "MinUnitCost" + """" + "|" + """" + "CountFreq" + """".
      
      /* Define the temp-table */
      
      DEFINE TEMP-TABLE tempTable
          FIELD tCompany AS CHARACTER
          FIELD tABCCode AS CHARACTER
          FIELD tMinDollarVolume AS CHARACTER
          FIELD tMinUnitCost AS CHARACTER
          FIELD tCountFreq AS CHARACTER.
      
      
      FOR EACH ABCCode NO-LOCK:
          CREATE tempTable.
          tempTable.tCompany = STRING(Company).
          tempTable.tABCCode = STRING(ABCCode).
          tempTable.tMinDollarVolume = STRING(MinDollarVolume).
          tempTable.tMinUnitCost = STRING(MinUnitCost).
          tempTable.tCountFreq = STRING(CountFreq).
      END.
      
      
      OUTPUT TO VALUE ("C:\Users\Admin\Desktop\ABCCode.csv").
      
      /* Output the column names */
      
      PUT UNFORMATTED columnNames.
      PUT UNFORMATTED "" SKIP.
      
      /* Output the temp-table */
      
      FOR EACH tempTable NO-LOCK:
          EXPORT DELIMITER "|" tempTable.
      END.
      
      OUTPUT CLOSE.
      
      QUIT.
      
      /* Done */
      

      R 脚本对 DB 进行 ODBC 调用以获取感兴趣表的列名,然后填充模板以生成 p 脚本。

      我不确定创建临时表并将所有内容都转换为角色是解决问题的最佳方法,但是...

      • 我们有列名
      • 所有内容都用双引号括起来
      • 我们可以选择任何分隔符(例如“|”而不是“,”)

      【讨论】:

        猜你喜欢
        • 2017-10-27
        • 1970-01-01
        • 2018-02-26
        • 1970-01-01
        • 2021-10-30
        • 2016-01-17
        • 1970-01-01
        • 2016-05-16
        • 2016-09-20
        相关资源
        最近更新 更多