Community寫作與編輯github.com

sasjs/skills

為 SASjs 生態系與 SAS 程式語言提供的代理技能

skills 是什麼?

此技能提供了撰寫符合慣用風格的生產級 SAS 程式碼的全面指南。它涵蓋 SAS 語言的核心組成部分,包括 DATA 步驟程式設計、PROC SQL、巨集語言、常用 PROC、格式與輸入格式、ODS 輸出和檔案 I/O。技能定義了一個嚴謹的工作流程:分析需求、起草程式碼、執行(或心理模擬)、檢查日誌,並根據常見陷阱清單進行審查,其核心要求是「零 WARNING」。它同時適用於 SAS 9 和 Viya 平台,並包含大量 Before/After 範例和明確的樣式規則,這些規則遵循 @sasjs/core 編碼標準。該技能本身是一個靜態語言參考,不執行程式碼或存取外部資源。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add sasjs/skills

Installed? Explore more 寫作與編輯 skills: steipete/notion, langchain-ai/langchain, bytedance/podcast-generation · View all 6 →

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

SAS 語言

撰寫符合慣用風格、生產品質的 SAS 程式碼。此技能涵蓋 SAS 語言本身,與任何框架無關。

範圍

  • DATA 步驟程式設計(SET/MERGE/BY,RETAIN,陣列,DO 迴圈,Hash 物件,first./last. 處理)
  • PROC SQL(聯結,子查詢,檢視,使用 CONNECT TO 的 Pass-Through)
  • 巨集語言(%macro/%mend,巨集變數,%local/%global,條件式 %IF 邏輯,引用函數 %STR/%NRSTR/%BQUOTE,CALL SYMPUTX)
  • 常用 PROC:SORT,MEANS/SUMMARY,FREQ,TRANSPOSE,REPORT,PRINT,CONTENTS,DATASETS,FORMAT,IMPORT/EXPORT
  • 格式與輸入格式(透過 PROC FORMAT 的使用者自訂格式,Picture 格式)
  • ODS 輸出(HTML,PDF,RTF,Excel,OUTPUT 目的地)
  • 檔案 I/O:LIBNAME,FILENAME,INFILE/INPUT,FILE/PUT

工作流程

對每個產生 SAS 程式碼的請求套用此循環——無論是新程式、編輯或除錯:

  1. 分析請求。 識別輸入:資料集、欄位、巨集變數、平台(SAS 9 或 Viya)。記下期望的輸出:一個資料集、一份報表(ODS)、一個巨集變數或一個檔案。如果請求不明確,請陳述您的假設並繼續進行。
  2. 起草程式碼。 根據以下樣式規則,撰寫最簡短的正確程式。使用二階層資料集名稱,為字元變數明確指定 length,在巨集內部使用 %local,並為 proc sql 加上 quit;。傾向使用 proc sort nodupkey 而非手動撰寫的去重複邏輯。
  3. 執行(或描述執行過程)。 如果有可用的 SAS 工作階段,請提交並擷取日誌。如果沒有,請根據常見陷阱清單在腦中追蹤程式碼——指出任何您無法驗證的步驟。
  4. 檢查日誌。 將任何 WARNING: 或 ERROR: 視為需要修復的缺陷,而不是可忽略的註記。常見罪魁禍首:未初始化的變數、隱含的數值→字元轉換、因隱含 length=8 導致的截斷、「沒有觀測值」、未解析的巨集參考、唯讀的自動巨集變數。在合理的情況下也應去除多餘的 NOTE。
  5. 根據陷阱清單審查。 在返回前,根據常見陷阱清單重新閱讀最終程式碼。修正或註記任何被標記的問題。

輸入: 一個請求(自然語言或 .sas 檔案),可選的範例資料或日誌片段。輸出: 符合慣用風格的 SAS 程式碼(2 空格縮排,以 run;/quit; 結尾),以及在必要時,針對所處理的陷阱或所做的假設加上簡短說明。驗證: 返回的程式碼在心理日誌掃描中必須沒有任何 WARNING/ERROR,並且在實際執行(如果可行)時產生一個乾淨的日誌。

不可協商:無 WARNING

生成的 SAS 程式碼必須乾淨執行——日誌中零 WARNING(和零 ERROR)。將每個 WARNING: 視為缺陷:未初始化的變數、隱含的型別轉換、應被視為警告的截斷註記、「沒有觀測值」、未解析的巨集參考等。如果某個警告確實無法避免,請刻意將其抑制(例如使用明確的選項)並說明原因。同樣地,在合理的情況下也應避免多餘的 NOTE。

樣式規則

這些規則遵循 @sasjs/core 編碼標準——將它們應用於所有 SAS 程式碼:

  • 每行一個陳述式;縮排 = 2 個空格,無跳格鍵,無尾隨空白
  • 每行不超過 80 個字元;Unix(LF)換行;UTF-8
  • 完全避免非 ASCII / 特殊字元——以達到跨 SAS 安裝和編碼的最大相容性
  • 步驟總是以 run; 結尾;對於 proc sql(以及連接到 CAS 的程序),必須使用 quit; 以避免在 Viya 上出現 WARNING: You cannot disconnect or terminate session ...
  • 所有資料集參考必須是二階層(work.blah,而非 blah)——這可以防止 DATASTMTCHK=ALLKEYWORDS 和作用中的 USER 函式庫
  • 為字元變數使用明確的 length / attrib,而不是依賴預設值(避免隱含的 length=8 截斷)
  • 為求清晰,使用字面後綴('01JAN2020'd,'12:30't)
  • 傾向使用 proc sort 搭配 nodupkey,而非手動的去重複邏輯
  • 巨集:
    • 即使沒有參數,定義時也要加上括號:%macro x(); 而非 %macro x;
    • 結尾的 %mend; 必須重複巨集名稱
    • 巨集呼叫不以分號結尾:%my_macro() 而非 %my_macro();
    • 必要參數使用位置標記法;選用參數使用關鍵字(var=)樣式
    • 巨集名稱使用小寫,採用動詞-名詞慣例
    • 巨集變數不使用尾隨點號(&var 而非 &var.),除非需要防止不正確的解析
    • 巨集變數名稱不區分大小寫:&Foo、&FOO 和 &foo 都解析為同一個符號(這同樣適用於 %symexist/%superq/symget 參數,這些參數接受的是名稱而非值)。不要將大小寫不匹配當成錯誤來追查——這絕不會是原因。
    • 所有巨集變數必須是 %local,除非故意設為全域(全域變數應使用應用程式前綴以避免衝突);在 DATA 步驟中使用 call symputx(而非 symput)
    • 在巨集內部使用 /* */ 進行註解(星號註解會被編譯到巨集中)
    • 使用 %length(&var)=0 檢查來保護巨集邏輯,而非 &var=(空的比較是不安全的)
  • 避免命名衝突:使用基於 %sysfunc-/&syslast 的工作表格(例如 data &output; set &syslast; run;),而不是硬編碼的名稱
  • WORK 函式庫在工作階段終止時會自動清除:SAS 在工作階段結束時會刪除 WORK 中的每個表格,因此對於一般的暫存表格,不需要明確的清理(proc datasets lib=work nolist; delete ...; quit;)——請不要添加它,並且在審查時不要將其缺失標記為缺陷。僅在暫存檔案過大或數量過多,有導致執行期間磁碟空間不足的風險時,才進行明確清理;在這種情況下,請在不再需要時立即刪除它們,而不是留待工作階段結束時處理。
  • 無開放式(非巨集)條件程式碼:將平台分支或有條件執行的區塊(例如 %if %mf_getplatform()=VIYA %then %do; ... %end;)包裝在 %macro ... %mend 中,並呼叫該巨集。在程式層級的開放式 %if 在某些執行環境(作業/排程器/測試框架)中會失敗,並且會隱藏作用域洩漏問題。

可攜性意識

  • 注意 SAS 9.4 和 Viya 之間的程式碼差異(例如 CAS 動作 vs. 程序,用於載入 sashdat 的 proc casutil,在鎖定的伺服器上沒有 X 指令)
  • 除非要求,否則避免硬編碼的實體路徑和特定於引擎的選項
  • 使用 if/else 邏輯時,不要有開放的巨集程式碼:將分支區塊包裝在 %macro ... %mend 中並呼叫它們——開放的 %if/%else 在所有的 SAS 環境中並不會如預期般運作。

Before / After 範例

每組配對展示一個常見的 WARNING 或缺陷模式及其慣用的修正方式,並說明它所解決的日誌問題。

DATA 步驟:未初始化的變數 + 隱含長度截斷

Before——region 被參考但從未被指派或透過 set 引入,因此日誌顯示 WARNING: Variable region is uninitialized;並且 name 從來源繼承了 first_name 的長度(通常為 $8),靜默地截斷了串接後的值:

data work.customers;
  set work.source;
  region = upcase(region);                 /* region 不在 work.source 中 */
  name = catx(' ', first_name, last_name); /* 被截斷為來源中 name 的長度 */
run;

After——region 透過 set(或明確指派)引入,並且在 set 之前使用明確的 length 來放寬 name,使 catx 不會被截斷:

data work.customers;
  length name $ 50;
  set work.source;                          /* region 必須存在於 work.source 中 */
  region = upcase(region);
  name = catx(' ', first_name, last_name);
run;

PROC SQL:隱含轉換 + 笛卡兒乘積

Before——使用字元 id 與數值 cust_id 進行聯結,觸發了 NOTE: Character values have been converted to numeric(或在某些選項下產生 WARNING),而缺少聯結鍵則有導致笛卡兒爆炸的風險:

proc sql;
  create table work.joined as
  select a.*, b.balance
  from work.orders a, work.accounts b
  where a.id = b.cust_id;
quit;

After——使用明確的 length/cast 使型別匹配,明確的 inner join,以及一個防止因缺少鍵而產生笛卡兒乘積的防護:

proc sql;
  create table work.joined as
  select a.id, a.amount, b.balance
  from work.orders as a
  inner join work.accounts as b
    on a.id = input(b.cust_id, 8.) /* 明確的 cast;或修正上游長度 */
  ;
quit;

如果一個鍵值欄位在兩側都確實有重複值,請先進行彙總或去重複——絕不要依賴偶然的 1:1 聯結。

巨集:缺少 %local + 不安全的空值檢查

Before——巨集內部的 %let 在全域作用域建立了 dsid(沒有 %local),而 %if &filter= 是不安全的——當 &filter 為空時,它解析為 %if = %then,這會造成語法錯誤而不是一個乾淨的分支:

%macro apply_filter(filter=);
  %if &filter= %then %do;
    data work.out; set work.in; run;
  %end;
  %else %do;
    data work.out; set work.in; where &filter; run;
  %end;
  %let dsid = &syslast;
%mend apply_filter;

After——%local dsid 將暫存變數保持在巨集作用域內,%length(&filter)=0 安全地防護了空值檢查,巨集定義使用了括號,並且 %mend 重複了名稱:

%macro apply_filter(filter=);
  %local dsid;
  %if %length(&filter)=0 %then %do;
    data work.out; set work.in; run;
  %end;
  %else %do;
    data work.out; set work.in; where &filter; run;
  %end;
  %let dsid = &syslast;
%mend apply_filter;

審查時需標記的常見陷阱

  • 非預期的多對多 MERGE
  • 自動變數 _ERROR_ / 隱含的 RETAIN 意外
  • 巨集時序問題:在 &macrovar 存在之前就參考它,%if 評估資料步驟變數(應改用 if/symget)
  • 自動巨集變數(&syscc,&syswarningtext,&syserrortext,&sysdate,&sysuserid 等)是唯讀的——嘗試覆寫它們(例如 %let syswarningtext=; 或 call symput('syscc',...))會引發 ERROR: Unable to assign value to a macro variable that is read only(或類似訊息)。永遠不要試圖「清除」它們。
  • 因第一次指派時的隱含 length=8 導致的截斷
  • proc sql 笛卡兒乘積警告

限制

此技能是一個靜態的語言參考——它為撰寫和審查 SAS 程式碼提供指導。它不執行 SAS 程式碼、不執行 shell 命令、不存取檔案系統、不連接資料庫,也不發送網路請求。所有程式碼範例僅供說明,必須由使用者提交到 SAS 工作階段。像「檔案 I/O」、「PROC SQL」、「指令」和「執行」等術語指的是 SAS 語言概念(檔案處理陳述式、SQL 程序、巨集執行環境),而不是由此技能執行的作業系統層級操作。

相關技能