2016年4月28日木曜日

SPDE (Scalable Performance Data Engine) の触りと、暗黙ソートについて



SPDエンジンについて、まずはリファレンスを参照ください。

https://documentation.sas.com/doc/en/pgmsascdc/9.4_3.5/engspde/p0aw8yr81ntdapn1xw6lpn20o1lg.htm


上記リファレンスの説明をかいつまむと、


 ・ high-performance data delivery用に設計
 ・ データを合理化されたファイル形式に編成
 ・ 並列処理
 ・ 非常に大きなデータセットの処理を高速化。


って感じで、分かったような分からないような感じですが、とにかく使ってみましょう。
触った感じ、Base SASエンジンで使えるオプションが一部SPDエンジンで使えなかったり、逆にSPDエンジン専用のオプションが用意されてたりします。

(たとえばfirstobs=オプションはSPDエンジンに無いっぽい。)



とにかく使ってみる。

libname sp spde "任意のパス";

data sp.DT1;
input A B;
cards;
2 11
1 22
1 33
2 44
;
run;

作成されたファイル


SAS上で見るとデータセットDT1がひとつ作られたように見えますが、エクスプローラで見ると上記のようにファイルが2つ出来ています。

これらはコンポーネントファイルといって、定義部分とデータ部分に分かれてファイルが作られている事を意味します。
さらにインデックスを定義していればインデックス用のコンポーネントファイルも作られます。



何気に便利な暗黙ソート

普通BYステートメントを含む処理は、事前にSORTプロシジャで並べ替えとく必要がありますが、SPDエンジンの場合はそれが不要なんです!

BYSORT= LIBNAMEステートメントオプション
https://documentation.sas.com/doc/en/pgmsascdc/9.4_3.5/engspde/p1oeyuoie0h44vn11fxanawndqwz.htm



libname sp spde "任意のパス";

data sp.DT1;
input A B;
cards;
2 11
1 22
1 33
2 44
;
run;

proc means data=sp.DT1;
  var B;
  by A;
run;

proc transpose data=sp.DT1 out=sp.OUT1;
  var B;
  by A;
run;

変数Aの順に並んでないのに、「by A;」を含むMEANSやTRANSPOSEが動いちゃいます。

おまけにユーティリティファイルスペースを使用して並び替えられるので、DT1は上書きされず元の並び順のままです。


ちなみに、データステップやプロシジャの出力先にご注意下さい。
(例えば、上の例のTRANSPOSEで「OUT=」にBase SASエンジンのライブラリを出力先にした場合、Base SASエンジンのデータセットとして出力されます。
その辺の挙動もご留意下さい。



以上、触りなのでこの辺までとします。
実際に利用する場合は、細かい設定や注意点などがあるので、まずはリファレンスをご確認下さい。


2016年4月26日火曜日

プログラムスリム化計画 【COALESCE関数を活用する】


覚えたら絶対役に立つCOALESCE関数を紹介。


構文


  COALESCE関数( 引数1 , 引数2 ・・・ )


この関数は引数のうち最初に欠損値以外で登場する値を返してくれる。
引数が全て数値の場合「COALESCE」、全て文字の場合「COALESCEC」と使い分ける必要がある。


それではどんな時使えるのか、簡単な例を示します。

サンプルデータ

data DT1;
length YN 8. A B C $10.;
input YN A$ B$ C$;
cards;
0 . aa bb
1 . . cc
. . . .
;
run;

  YN   
  A   
  B   
  C  
   0      aa    bb  
   1         cc  
   .           


例①

サンプルデータに、有無変数YN 「0:なし、1:あり」 があります。
この変数が欠損値だったら 「99:不明」 と置き換えた変数YN2を作りたいとします。

  YN   
  YN2   
   0    0
   1    1
   .   99


通常以下のように書きますが、、
data OUT1;
   set DT1;
   if YN^=. then YN2 = YN;
   else YN2 = 99;
   keep YN YN2;
run;


COALESCE関数を使えば、一発です。
data OUT2;
   set DT1;
   YN2 = coalesce( YN, 99 );
   keep YN YN2;
run;





例②

以下のロジックで変数Xを導出したいとします。

・ Aに値があれば X =  A
・ 上記以外で、Bに値があれば X = B
・ 上記以外で、Cに値があれば X = C
・ 上記以外(すべて欠損値)なら X = "XXX"

  A  
  B   
  C   
  X  
     aa    bb    aa  
        cc    cc  
         XXX 

通常以下のような書き方になりますが、、
data OUT3;
   set DT1;
   length X $10.;
   if A^="" then X = A;
   else if B^="" then X = B;
   else if C^="" then X = C;
   else X = "XXX";
   keep A B C X;
run;


これも一発で書けます。
data OUT4;
   set DT1;
   length X $10.;
   X = coalescec( A, B, C, "XXX" );
   keep A B C X;
run;



他にも以下のような使い方など、工夫次第で色々な使い方ができる超便利な関数です。
変数指定は「V100-V1」のように逆にもできる。


2016年4月22日金曜日

プログラムスリム化計画 【LEFT、TRIM関数を使わない】



* Sample data ;
data DT1;
   length A B $10.;
   A = "aa";
   B = "b b";
run;

上のデータから以下の変数を作りたい。
  •  文字変数 A・B を結合した変数X
  •  結合時、各変数の両脇の半角スペースを除去する。


昔は以下のようにLEFT/TRIM関数を使用していました。
  
* 昔の書き方 ;
data OUT1;
   length X $20.;
   set DT1;
   X = trim(left(A)) || trim(left(B));
run;
 X   
 A   
B  
 aab b  aa  b b  


この昔から定石として書かれているプログラムは、以下のようにスリム化できます。

* プログラムのスリム化 ;
data OUT2;
   length X $20.;
   set DT1;
   X = cats( A, B );
run;


CATS関数やその他の半角スペース処理関連のテクニックは以下記事で解説しています。

2016年4月21日木曜日

INオペレータは 「X in (A B C)」 のようにカンマが無くてもOK


以下をご覧ください。

data DT1;
   set SASHELP.CLASS;
   where AGE in (13, 14, 15);
run;

「AGEが13, 14, 15のいずれか」 という条件になります。
この書き方は以下のようにカンマを入れなくても動きます。

data DT1;
   set SASHELP.CLASS;
   where AGE in (13 14 15);
run;



カンマを入れない方がプログラムがスッキリして見える気がします。
あと役にたつ例として、単純な例ですが以下のようなマクロがあったとします。

%macro TEST( IN );

   proc means data=SASHELP.CLASS;
       var AGE;
       where AGE in (&IN);
   run;

%mend;


「where AGE in (&IN)」 で INの中身をマクロ変数として指定するようにしています。
「where AGE in (13,14,15)」と展開したい場合、以下のように%STR関数を使ってカンマをクオートする必要がありますが、、
%TEST(%str(13,14,15));



カンマを省略すれば以下のような単純な書き方で済みます。
%TEST(13 14 15);



必要に応じて使い分けてみましょう。

2016年4月20日水曜日

ODSグラフ(SG系グラフ)保存時の落とし穴




いままで、SGPLOTで作ったグラフをpngファイルとして保存する際、以下のように書いてました。
使用してたバージョンはSAS9.2です。


9.2 (実行結果は環境・設定によって異なる)
 ods graphics  /  reset=index  imagename="SAMPLE"  imagefmt=png ;
 ods listing gpath="C:\TEST";

      proc sgplot data=SASHELP.CLASS;
         scatter x=HEIGHT y=WEIGHT;
      run;

 ods listing close;
 ods graphics  /  reset=all;
 ods listing;


ログ
NOTE: C:\TEST\SAMPLE.png に書き込まれたイメージ出力を一覧表示します。
NOTE: データセット SASHELP.CLASS から 19 オブザベーションを読み込みました。




で、最近9.3で同様のプログラムを実行しました。(「imagefmt」はオプション名が「outputfmt」に変わったのでそこを変更してるのと、最後の「ods listing;」は削ってます)

9.3  (実行結果は環境・設定によって異なる)
 ods graphics  /  reset=index  imagename="SAMPLE"  outputfmt=png ;
 ods listing gpath="C:\TEST";

      proc sgplot data=SASHELP.CLASS;
         scatter x=HEIGHT y=WEIGHT;
      run;

 ods listing close;
 ods graphics  /  reset=all;


ログ
NOTE: C:\TEST\SAMPLE1.pngに書き込まれたイメージ出力を一覧表示します。
NOTE: データセットSASHELP.CLASSから19オブザベーションを読み込みました。



それぞれのログを見ると、保存されたファイル名が異なってますよね。
9.2 ・・・ SAMPLE.png
9.3 ・・・ SAMPLE1.png

とある設定によって、上記のファイル名にならない方もいるはずで、そこがまた厄介。
ファイルの取り違いを起こしそうで、ちょっと危険です。



原因、の前に前提知識

画像ファイルが作成されるたびに「SAMPLE.png、SAMPLE1.png、SAMPLE2.png、SAMPLE3.png・・・」とファイル名の末尾が+1される仕組みになっていて、同じプログラムを複数回実行すると、+1ずつカウントが増えていってしまいます。
なので「ods graphics / reset=index」で前回実行時のカウントをリセットしていますが、9.3では何故か+1カウントされたような名前「SAMPLE1.png」になってるのが不思議ですよね。
しかも、複数回実行しても、毎回「SAMPLE1.png」というファイル名になって、「SAMPLE2.png、SAMPLE3.png・・・」とカウントが増えていくわけでもないのがまた不思議。



原因

9.3からSASのアウトプットのデフォルトがHTMLに変わったことが影響してます。

  • HTMLに表示されるグラフは、裏でグラフの画像ファイルが作成されています。
  • 今回の例ではまずHTMLに表示するグラフの画像ファイル「SAMPLE.png」が作成されます。
  • 次に「ods listing gpath="C:\TEST"」と書いてるので、ods listing出力先でもグラフの画像ファイルが作成されます。これは2個目の画像ファイルなので、+1カウントが進んで「SAMPLE1.png」となってしまいますよね。
  • HTML以外の一部ODS出力先も、裏で画像ファイルを作ってしまうものがあって、それらODS出力先が開いたままだと同様に保存する画像ファイル名の末尾がずれてしまいます。



解決策

先に開いているODS出力をCLOSEすればOK。

今回の例ではHTMLが出力先としてアクティブになっていたので、
先頭に「ods html close;」、末尾に「ods html;」を入れることで「SAMPLE.png」というファイル名にする事が出来ました。

2016年4月19日火曜日

プロシジャでもATTRIBやLABELなどのステートメントが使える。



以下をご覧ください。
data OUT1;
   label AGE="ねんれい";
   set SASHELP.CLASS;
run;

proc sort data=OUT1;
   by AGE;
run;

データステップでLABELを変更してから、SORTプロシジャで並び替えを行っています。
昔自分が書いたプログラムを見てたら、こんな感じで2ステップ使って書いてました。


これらの処理は1ステップで書けます。
proc sort data=sashelp.class out=OUT1;
   by AGE;
   label AGE="ねんれい";
run;

プロシジャにもATTRIB, LABEL, FORMATステートメントが使えます。
(プロシジャによっては使えない可能性もあります)


ただし一部挙動が異なる。

・ プロシジャ内でLENGTHステートメントは使えません。
書いても 「NOTE: LENGTH属性は変更できないため、無視されます。」 ってログに出て無視されます。

・ データステップの一番先頭にATTRIB等の変数属性を定義するステートメントを記述すると、記述した順に変数が並びますが、プロシジャ内で記述しても変数の順番は変わりません。



2016年4月18日月曜日

0オブザベーションのデータセットを作る方法 【まとめ】


変数属性だけ定義した0オブザベーションのデータセットを作る方法をまとめてみます。


①STOP
data DT1;
    set SASHELP.CLASS;
    stop;
run;

STOPステートメントでオブザベーションが出来る前に処理を停止させてしまう。


② OBS=
data DT2;
    set SASHELP.CLASS (obs=0);
run;

OBS=オプションで読み込むオブザベーション数を指定できます。


③ IF 0
data DT3;
    if 0 then set SASHELP.CLASS;
    stop;
run;

「if 0 then」 の 「0」 は 「false」 の意味になり、then以降の 「set SASHELP.CLASS」 の部分は実行されません。
。。のはずですが、たとえ実行されなくてもデータセット名の指定があると、コンパイル時にそのデータセットの定義情報だけを読み込みにいきます。
①とほとんど同じ方法ですが、こちらの方が内部的に余計な処理が入らないのでスマートです。


④ SET _NULL_;
data DT4;
    set SASHELP.CLASS;
    set _null_;
run;

http://sas-boubi.blogspot.jp/2016/04/data-null.html
http://sas-tumesas.blogspot.jp/2013/10/1obs.html(データステップ100万回)

新手かも。


⑤ WHERE 0
data DT5;
    set SASHELP.CLASS;
    where 0;
run;

この方法も見かけた事ないかも。


⑥ SQL
proc sql;
    create table DT6 like SASHELP.CLASS;
quit;

http://sas-tumesas.blogspot.jp/2013/10/blog-post.html(データステップ100万回)


2016年4月15日金曜日

「DATA _NULL_」 について。




以下の構文で、データセットを作らずにデータステップが実行できます。

DATA _NULL_;

      ~ なんかの処理 ~

RUN;

「データステップを実行したいけど、データセットへの出力は不要」という場合に使えます。




単純な例ですが、以下はデータセットSASHELP.CLASS の特定の変数値をログに出力する例です。

data _null_;
      set sashelp.class;
      put name sex age;
RUN;

ログに出力するだけで、データセットを作りたいわけじゃないんで、「_NULL_」 を使用しています。



ちなみに以下の文もエラーなく通ります。

* 何もPRINTしない ;
proc print data=_null_;
run;

* 何もSETしない ;
data dt1;
  set _null_;
run;

あんまり役に立つ例が思いつかないですが、マクロに組み込んだりすれば便利そうです。


ひとつ知ってるのが、0オブザベーションのデータセットを作りたい時に大体以下のように書くと思いますが、、

data empty;
   stop;
run;


何かの本で、

data empty;
   set _null_;
run;

と書いてる面白い例を見たことがあります。
なんかかっこいい書き方ですね。


他に何かアイディアとか、こういう時使ったという例があったら、教えてほしいです。



2016年4月14日木曜日

ログに独自の無効メッセージを表示する【ERRORステートメント】



以下のようなログをよく見かけると思います。

data DT1;
    A = "x";
    B = input("x",best32.);
run;

ログ

NOTE: 関数INPUT(行 71 カラム 9)の引数は無効です。
A=x B=. _ERROR_=1 _N_=1

何か無効なデータが読まれた時に、「~が無効です」 というNOTEメッセージとともに、その時のデータがログに表示されます。

ここで独自の条件に基づいて、上記のような無効メッセージを表示したいとします。
そこで使えるのがERRORステートメントです。



構文

  ERROR "ログに表示するメッセージ" ;

以下機能と組み合わせると、よりそれっぽいメッセージになります。





data DT2;
    A = 1;
    if A = 1 then  error "NOTE: 変数Aの値が無効です。";
run;

ログ

NOTE: 変数Aの値が無効です。
A=1 _ERROR_=1 _N_=1

変数Aの値が「1」の時に無効メッセージを表示しています。
メッセージの先頭部分に「NOTE: 」と入れてNOTEメッセージにしてます。


2016年4月11日月曜日

PRINTプロシジャで変数の数が多い時に使えるIDステートメント



以下のプログラムを実行すると変数が沢山出来ます。(変数NO, A1, A2, A3・・・A30)

*  サンプルデータ ;
data DT1;
  do NO=1 to 5;
     retain A1-A30 0;
     output;
  end;
run;


上のようなデータに対して、

「RTFに出力したい。データの中身を確認するだけだから、適当にPRINTプロシジャとかで出したやつでいいよ」

という状況があったとします。そこで以下のように書くと、、、

ods rtf file="出力するファイルのフルパスを指定";
   proc print data=DT1;
   run;
ods rtf close;

変数の数が多すぎて途中で折り返してしまいました。
これだと例えばNO=4のA27の値は・・・とかって見たいときに見辛いです。


そこでIDステートメントを使ってみます。
ods rtf file="出力するファイルのフルパスを指定";
    proc print data=DT1;
        id NO;
    run;
ods rtf close;

途中で折り返しても、IDステートメントに指定した変数が一番左側に表示されるようになるので、多少見やすくなります。


📝ちなみに

IDステートメントに指定した変数の「数が多い」か「格納値が長すぎる」と、
ログに長すぎるぞ!的なWARNINGが出て、IDステートメントが無効になることがあります。
ODS LISTINGでの出力時に発生したことがありますが、RTF等の他の出力時でも同様のことが起こるか不明なので、ログも要確認です。

また、今回の方法以外に、用紙の向きを横にして横幅を広げて見やすくするって方法もあるかと思います。
ODS出力時の用紙サイズや余白などを設定するオプション [まとめ]


2016年4月8日金曜日

CARDS vs CARDS4



今回は「CARDS」と「CARDS4」の違いを見ていきたいと思います。

CARDSの詳しい使い方や注意事項についてはSAS社のリファレンスをご確認下さい
(CARDSと同じ機能の「DATALINESステートメント」の方に詳しい使い方などが書かれてます)



ではまず、以下をご覧ください。
「CARDS;」 と 「;」 の間のテキストを読み込み範囲としています。

data DT1;
   length A $10. B 8.;
   input A B;
cards;
aaa .
. 1
;
run;
  A   
  B   
 aaa   .  
  1  


CARDSではセミコロン「;」が読み込み範囲終了の合図になっていますが、、

data DT2;
   length A $10. B 8.;
   input A B;
cards4;
aaa; .
. 1
;;;;
run;
  A   
  B   
 aaa;   .
   1  

CARDS4はセミコロン4つ「;;;;」が終了合図になっています。そのため読み込むテキスト中にセミコロンを含める事が出来ます。



ちなみに

似た機能で「DATALINES」「DATALINES4」というステートメントがありますが、「CARDS」「CARDS4」と挙動は一緒です。

また、マクロの中でCARDS等は使えない(エラーになる)ので、ご注意下さい。


2016年4月7日木曜日

INPUTステートメントで、空白を含むテキストを1変数に読み込む方法


以下のようなテキストファイルがあったとします。

C:\TEST.txt
 SAS boubi roku
 I'm a.matsu


このファイルを読み込んで以下のデータセットを作りたいとする。
 TEXT  
 SAS boubi roku 
 I'm a.matsu



失敗例

data DT1;
    length TEXT $2000.;
    infile "C:\TEST.txt"  lrecl=32767;
    input TEXT;
run;
  TEXT   
 SAS
 I'm

テキスト中の半角スペース(空白)が区切り文字として解釈されるので文字が切れてしまいます。



解決案 : 自動変数 _INFILE_ を使う


解決案は色々ありますが、今回は自動変数 「_INFILE_」 を使ってみます。
(注意事項を追記したので、記事の最後までご覧ください)

data DT2;
    length TEXT $2000.;
    infile "C:\TEST.txt"  lrecl=32767;
    input ;
    TEXT = _infile_;
run;
 TEXT  
 SAS boubi roku 
 I'm a.matsu


解説
  infile "C:\TEST.txt"  lrecl=32767;

まず読込むテキストファイルを指定。
また「LRECL=」で1レコードに読み込める最大バイト数を指定しています。この設定値が小さいと文字切れの原因になるので、例では最大設定値(環境によって異なる可能性あり)の「32767」を設定。
それと、LENGTHも32767にしてしまうと、ファイルサイズが大きくなってしまうので、実データにあわせた長さに適宜設定します。


  input;

テキストファイルを1レコード読み込みに行きます。
ただしINPUTの後に変数の指定がないので、変数への出力はしません。


  TEXT = _infile_;

自動変数「_INFILE_」には入力バッファの内容が格納されています。
つまりテキストファイルから読込んだ1レコード分全文字が格納されています。
この文字を変数TEXTに代入すれば出来上がりです。


📝注意

テキストファイルで「先頭に半角スペースを含むレコード」がある場合は注意。
「今回の方法で変数に読み込んだら先頭の半角スペースが消えてしまった」とご報告頂きました。
私のほうでテストしたところ、問題なくスペースを保持することが出来ましたが、環境依存の可能性もあるので、各自の環境で挙動確認したほうが良さそうです。

ちなみに、ご報告頂いた内容とは別件かもですが、変数値の先頭に半角スペースがあっても、表示上、スペースが消えてみえることがあります。
例えばPROC PRINTで変数値を表示すると先頭の半角スペースが消えてみえます。
実際の格納値と異なるため、混乱のもとになりがちです。



関連記事
「input &」で空白を含むテキストを1変数に読み込む


2016年4月1日金曜日

DS2プロシジャ入門9:SQLSTMTパッケージ



SQLSTMTパッケージは、DS2プロシジャ内でSQL(FedSQL)を実行したり、その結果を取得したりといった柔軟な処理が出来ます。




構文① SQLの実行


SQLSTMTパッケージを使えるようにする
 DCL PACKAGE SQLSTMT  インスタンス参照名( 'SQL文' );

SQLSTMTパッケージを使用するには、パッケージのインスタンスってのを作って、そのインスタンスを参照するための変数(インスタンス参照名)を定義します。


SQL実行
 インスタンス参照名.EXECUTE();


例1
* サンプルデータ ;
data DT1;
input A B;
cards;
1 100
2 200
;

data DT2;
input X Y;
cards;
1 .
1 .
2 .
2 .
;

* SQLSTMTを使ってデータの更新を行う ;
proc ds2;
   data _NULL_ ;
     dcl package sqlstmt sq( 'update DT2 set Y=1 where X=1' );
     method init();
          sq.execute();
     end;
   enddata;
   run;
quit;

dcl package sqlstmt sq( 'update DT2 set Y=1 where X=1' );
データ更新するSQL文を定義して、、

sq.execute();
実行しています。


📝ポイントと注意点
  • SQL文は「PROC SQL」ではなく「FedSQLステートメント」である点に注意(FedSQLの詳細についてはSAS社のリファレンスを参照下さい)
  • FedSQLステートメントはシングルクオーテーションで囲む
  • EXECUTEメソッドは以下のリターンコードを返します
    • 0 = 実行が成功、1 = エラーがある、2 = データがない(UPDATEやDELETEがどの行にも影響を与えない)
  • 例ではプログラムを見やすくするため、リターンコードに対する処理は省略してます。必要に応じ追加下さい。



例2
proc ds2;
   data _NULL_ ;
     dcl package sqlstmt sq( 'update DT2 set Y=? where X=?' , [B A);
     method run();
          set DT1;
          sq.execute();
     end;
   enddata;
   run;
quit;

dcl package sqlstmt sq( 'update DT2 set Y=? where X=?' , [B A] );
SETしたDT1の変数[B A]をSQL文中の「?」に渡すよう指定してます。

update DT2 set Y=[B] where X=[A]

SETしたDT1の各オブザベーションの変数A, Bを使って、DT2のデータを更新したいので、
RUNメソッドの中でEXECUTEメソッドを記述しています
(SETでDT1のオブザベーションを読み込む毎にSQLを実行している)



構文② SQLの結果を取得する


SQLの結果をDS2プロシジャ内の変数に紐づける
 インスタンス参照名.BINDRESULTS( [変数1 変数2 ・・・] );

「SQLの結果に含まれる変数」と「BINDRESULTSに指定した変数」を、左から順に紐づけます
(同じ変数名同士で紐づけるわけではないので注意)


SQLの結果から1行取ってきて、紐づけた変数にセットする
 インスタンス参照名.FETCH();


例1
proc ds2;
   data OUT1 / overwrite=yes ;
     dcl double A2 B2;
     dcl package sqlstmt sq( 'select A,B from DT1' );
     method init();
          sq.execute();
          sq.bindresults([A2 B2]);

          do while (sq.fetch()=0);
              output;
          end;
     end;
   enddata;
   run;
quit;

dcl package sqlstmt sq('select A,B from DT1');
sq.execute();
SQLでselectしたA,Bを、

sq.bindresults([A2 B2]);
DS2プロシジャ内で定義した変数A2, B2に紐づけます。

do while (sq.fetch()=0);
   output;
end;
SQLの結果を1行ずつ取り出して、紐づけた変数A2,B2に格納します。


📝ポイントと注意点
  • BINDRESULTSメソッドは以下のリターンコードを返します
    • 0 = 実行が成功、1 = エラーがある
  • FETCHメソッドは実行される度にSQLの結果から次の行を読み込んでいき、以下のリターンコードを返します
    • 0 = 実行が成功、1 = エラーがある、2 = データがない(FETCHする行がこれ以上ない)
  • 例ではリターンコードに対する処理は省略してます。必要に応じ追加下さい。



例2
proc ds2;
   data _NULL_ ;
     dcl double A2 B2;
     dcl package sqlstmt sq1( 'select A,B from DT1' );
     dcl package sqlstmt sq2( 'update DT2 set Y=? where X=?',[B2 A2] );
     method init();
          sq1.execute();
          sq1.bindresults([A2 B2]);

          do while (sq1.fetch()=0);
              sq2.execute();
          end;
     end;
   enddata;
   run;
quit;

今回の集大成。

dcl package sqlstmt sq1('select A,B from DT1');
sq1.bindresults([A2 B2]);
SQLでselectしたA,Bを、DS2プロシジャ内で定義した変数A2,B2に紐づけて取り出しながら、その値を使って、、

dcl package sqlstmt sq2('update DT2 set Y=? where X=?',[B2 A2]);
別のデータに更新をかけようというわけです。



DS2プロシジャ入門記事

1: 基本構文
: 変数の宣言
3: 変数属性と配列の定義
4: データ型①
8: ユーザー定義パッケージ
9: SQLSTMTパッケージ