Progress / 4GL:使用列名和双引号将表导出为.csv?

时间:2015-07-09 20:16:38

标签: r csv odbc progress-4gl

我正在尝试编写一个.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驱动程序无法可靠地运行...有时它不会返回表中的所有行。

最终目标是将参数(table-name)传递给.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"
    

3 个答案:

答案 0 :(得分:2)

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

您可以使用此KB了解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.

答案 1 :(得分:0)

我不同意@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。

根据@TheMadDBAs答案,你仍然需要处理字符串截断。

答案 2 :(得分:0)

在获得@TheMadDBAs的一些灵感之后,我想在这里解决问题......

我决定在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脚本对数据库进行ODBC调用以获取感兴趣的表的列名,然后填充模板以生成p脚本。

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

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