换行符在SAS的宏参数中丢失

Line breaks are lost in macro parameters of SAS

我想将包含换行符的文本从 SAS table 传输到外部数据库 table(通过 ODBC)。 我使用宏变量作为缓冲区。我使用宏将文本插入 table 的远程数据库。问题是文本中的换行符丢失了, 如果我将文本作为参数传递给宏。 如何保留换行符?

%macro dbinsert (id=,text=);
/*
   Macro for inserting text into table of second database.
   For test purposes, odbc table replaced with SAS table.
   Issue: line breaks will be lost. How can i keep the line breaks?

   Original Code:
      proc sql;
         CONNECT TO ODBC (DSN="mydb");
         EXEC (update mytable
            set text= "&text."
            where id = &id.;
            ) BY ODBC;
      quit;
*/
   proc sql noprint;
       update x2
       set text = "&text."
       where id = &id.;
   quit;

%mend dbinsert;

%macro main;

/* Initialize SAS table. Just for test purposes. */
data x1;
   text="Hello Martin how are you?"; output;
   text="Thank you I am fine!"; output;
   text="And how are you?"; output;
run;

/* Read content from SAS table into macro variable (text1). The content is separated via line breaks 0D0A. */
proc sql noprint;
   select text into :text1 separated by '0D0A'x from x1;
quit;

/* Initialize table in second database. Just for test purposes. */
data x2;
   length id 8 text 0;
   id=1; text="111"; output;
   id=2; text="222"; output;
run;

%dbinsert(id=2,text=&text1.);

%mend main;
%main;

宏不支持 non-printable 个字符。

来自文档

Getting Started with the Macro Facility
...
The SAS macro language is a string-based language. It does not support the use of hexadecimal character constants.


Note: The SAS macro language does not support using hexadecimal values to specify non-printable characters.

Macro Variables
...
Macro variable values have a maximum length of 65,534 characters. The length of a macro variable is determined by the text assigned to it instead of a specific length declaration. So its length varies with each value that it contains. Macro variables contain only character data. However, the macro facility has features that enable a variable to be evaluated as a number when it contains character data that can be interpreted as a number. The value of a macro variable remains constant until it is specifically changed. Macro variables are independent of SAS data set variables.


Note: Only printable characters should be assigned to macro variables. Non-printable values that are assigned to macro variables can cause unpredictable results.

可能的选择

你知道EXEC并通过了——很好!

考虑使用 SQL 为连接字符串文字的字符串表达式 构造源代码。该表达式将包含使用远程数据库本机字符函数的源代码。必须修改 DBINSERT 宏以处理现在包含表达式的参数。

同样,宏将 non-printable 个字符转换为 space,因此如果 x1.text 中的数据值包含嵌入的 CRLF,它们将不会出现在构造的表达式中。

示例:

重要提示: 请勿将此版本的 DBINSERT 与不受信任的用户提供的任何输入或网络输入一起使用。来自数据值的 Codegen 是用于 SQL 注入或代码注入的向量。

%macro dbinsert (id=,expression=);
   proc sql noprint;
       update x2
       set text = &expression
       where id = &id.;
   quit;
%mend dbinsert;

data x1;
   text="Hello Martin how are you?"; output;
   text="Thank you I am fine!"; output;
   text="And how are you?"; output;
run;

/*
 * CONSTRUCT SOURCE CODE FOR AN EXPRESSION THAT CONCATENTATES STRING LITERALS
 * (also known as codegen short for code generator)
 */
proc sql noprint;
  select 
    quote(trim(text)) 
    into :text1 
    separated by '||byte(13)||byte(10)||' 
    from x1;

data x2;
   length id 8 text 0;
   id=1; text="111"; output;
   id=2; text="222"; output;
run;

options mprint;
%dbinsert(id=2,expression=&text1.);
%symdel text1;

title;
ods html style=monospace;
proc print data=x2; where id=2; 
proc print data=x2; where id=2; format text $HEX400.;
proc print data=x2; where id=2; 
  var text / style=[asis=true];
run;
ods html close;

日志显示在 DBINSERT 调用时正在运行的 codegen。

634  %dbinsert(id=2,expression=&text1.);
MPRINT(DBINSERT):   proc sql noprint;
MPRINT(DBINSERT):   update x2 set text = "Hello Martin how are you?"||byte(13)||byte(10)||"Thank
you I am fine!"||byte(13)||byte(10)||"And how are you?" where id = 2;
NOTE: 1 row was updated in WORK.X2.

检查结果。 SAS ViewTable 将 non-printables 呈现为 space,但数据值中存在控制字符。

查看器中的结果图像

ODS HTML 中的相同结果,第二个 table 显示十六进制格式时的文本值。第三个 table 显示当样式选项 ASIS=YES 生效时由 ODS HTML 解释的控制字符的视图。

无需尝试添加将宏变量的值转换为有效代码所需的引号,您只需编写宏以假定该值已经是包含在生成代码中的有效语法。那么 SQL 语法就是:

set text = &text.

如果您需要添加远程系统用于指定字符串中特殊字符的任何语法,那么您可以这样做

%dbinsert (id=,text='ABC'||byte(13)||'XYZ')

如果您有一个现有的宏变量并且想用单引号将其括起来,您可以使用数据步骤来保留任何特殊字符。

data _null_;
  call symputx('text',quote(symget('text'),"'"));
run;

请注意,使用数据步会将您限制为 32K 字节的字符串,而不是宏变量可以容纳的 64k 字节的限制。