PowerShell SQLPlus Select結果（複数行）をいい感じで受け取る

ニッチすぎる。

ODBCとかOracleClientとかそっち使えよって感じだけど
業務上PowerShell＋SQLPLusじゃないとダメって人向け。

```PowerShell:Hoge.ps1
# チェック
if(!$ENV:Path.Contains("C:\Oracle\bin")){
    $ENV:Path+=";C:\Oracle\bin"
    echo "ADD PATH"
}

# SQL*Plusを使用してSQLファイルを実行し、結果を受け取る
$sqlPlusOutput = @"
set heading on
set underline off
set termout off 
set feedback off 
set colsep ,
set lines 9999
set trimspool on 
set trimout on 
set tab off 
SET RECSEP OFF

select * from TABLE1 WHERE COLUMN1 in ('11','21');
"@

$command = @"
echo '$sqlPlusOutput' | sqlplus -S -L 'sys/Oradoc_db1@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(Host=localhost)(Port=1521))(CONNECT_DATA=(SID=ORCLCDB)))' as sysdba 
"@

$table = (Invoke-Expression -Command $command)

if($table | Out-String -Stream | ?{$_.Contains('ORA-') -or $_.Contains('SP2-')}) {
    echo $table 
    exit 1
}

$data = @()
$Header = @()
foreach($row in $table[1].Split(",")){
    $Header += $row.Trim()
}
for ($i = 2; $i -lt $table.Length; $i++) {
    $rowData = @{}
    for ($j = 0; $j -lt $Header.Length; $j++) {
        $columnName = $Header[$j]
        $rowData[$columnName] = ($table[$i].Split(","))[$j].Trim()
    }
    $data += $rowData
}

echo $data[1].COLUMN4
```

```batcher.bat
@echo off

REM 引数チェック
if "%~1"=="" (
    echo Usage: executeBat.bat 100
    exit 11
)

SET script_path=C:\IF\powershell.ps1
if not exist "%script_path%" (
    echo スクリプトが見つかりません: %script_path%
    exit 1
)

powershell -ExecutionPolicy Bypass -File "%script_path%" %~1
set powershell_exit_code=%errorlevel%
if %powershell_exit_code% equ 0 (
    echo PowerShell スクリプトは正常に終了しました。 管理番号[%~1]
) else (
    echo PowerShell スクリプトはエラーで終了しました。管理番号[%~1] エラーコード: %powershell_exit_code% 
)
exit %powershell_exit_code%

```

```exec.sql
DECLARE
  v_table_name   VARCHAR2(100) := '&table_name';-- テーブル名をパラメータとして指定
  v_execute_sql  VARCHAR(200);      -- 実行するSQLを格納
  v_cursor       PLS_INTEGER;       -- カーソルを定義
  v_column_count INTEGER;           -- 列数を格納する変数
  v_column_desc  dbms_sql.desc_tab; -- 列の情報を格納する配列
  v_column_name  VARCHAR2(100);      -- 列名を格納する変数
  v_column_value VARCHAR2(4000);    -- 列の値を格納する変数、適宜サイズを調整
  v_pre_count    VARCHAR2(100);     -- pre件数を取得する
  v_pre_sql      VARCHAR2(200);      -- pre実行するSQLを格納
BEGIN
  -- カーソルを開く
  v_cursor := dbms_sql.open_cursor;
  
  -- SQLの準備
  v_execute_sql := 'SELECT * FROM ' || v_table_name;
  v_pre_sql     := 'SELECT count(*) FROM ' || v_table_name;
  -- SQL文を解析する
  dbms_sql.parse(v_cursor, v_execute_sql, dbms_sql.native);

  -- 列の情報を取得する
  dbms_sql.describe_columns(v_cursor, v_column_count, v_column_desc);

  -- 行数と、列数を出力する
  EXECUTE IMMEDIATE v_pre_sql into v_pre_count;
  DBMS_OUTPUT.PUT_LINE(v_pre_count||','||v_column_count);

  -- 各列について処理を行う
  FOR i IN 1..v_column_count LOOP
    -- 列名を取得
    v_column_name := v_column_desc(i).col_name;
    -- 列の値を定義
    dbms_sql.define_column(v_cursor, i, v_column_value, 4000);
  END LOOP;

  -- 実行結果をバインドする
  dbms_sql.define_column(v_cursor, 1, v_column_value, 4000);

  -- SQL文を実行する
  IF dbms_sql.execute(v_cursor) = 0 THEN
    -- 結果を取得する
    LOOP
      EXIT WHEN dbms_sql.fetch_rows(v_cursor) = 0;
      FOR i IN 1..v_column_count LOOP
        -- 列の値を取得
        dbms_sql.column_value(v_cursor, i, v_column_value);
        -- 結果を出力
        DBMS_OUTPUT.PUT_LINE(v_column_desc(i).col_name || ',' || v_column_value);
      END LOOP;
    END LOOP;
  END IF;

  -- カーソルを閉じる
  dbms_sql.close_cursor(v_cursor);
END;
/
```


```powershell:psooo.ps1
Set-StrictMode -Version 3.0
$BATCH_RESULT = [ordered]@{
    SUCC =  0; # 成功
    ERR  = -1; # エラー
}
$PLSQL_RESULT = [ordered]@{ 
    SUCC = 0; # 成功
    ERR  = 1; # エラー
    PERR = 2; # 一部エラー
    WARN = 3; # 警告
}
$EXE_STATE = [ordered]@{
    RUN  = 0; # 実行中
    WAIT = 1; # 待機中
}
$resultValue = $PLSQL_RESULT.ERR;
Write-Host  $($PLSQL_RESULT.WARN) 
if($resultValue -eq $PLSQL_RESULT.ERR) {
   Write-Host "result_VAlue is err"
}
$key = ($EXE_STATE.GetEnumerator() | Where-Object { $_.Value -eq  $resultValue }).Key
Write-Host $key

$($BATCH_RESULT | Out-String -Stream | %{ $_ -replace "\s+$" ,''} )
$($PLSQL_RESULT | Out-String -Stream | %{ $_ -replace "\s+$" ,''} )
```

```powershell:solo.ps1

# 関数の呼び出しを別プロセスとして実行
$to = "recipient@example.com"
$from = "sender@example.com"
$subject = "Test Email"
$body = "This is a test email sent from PowerShell."
$smtpServer = "smtp.example.com"
$port = 587
$username = "your_smtp_username"
$password = "your_smtp_password"

$job = Start-Job -ScriptBlock {
    param (
        $To, $From, $Subject, $Body, $SmtpServer, $Port, $Username, $Password
    )
    # 関数の再定義
    function Send-Email {
        param (
            [string]$To,
            [string]$From,
            [string]$Subject,
            [string]$Body,
            [string]$SmtpServer,
            [int]$Port = 25,
            [string]$Username,
            [string]$Password
        )

        try {
            # Send-MailMessage コマンドレットを使用してメールを送信
            Send-MailMessage -To $To -From $From -Subject $Subject -Body $Body -SmtpServer $SmtpServer -Port $Port -ErrorAction Stop
            
            # 正常終了の場合の終了コード
            return 0
        }
        catch {
            # エラーメッセージを表示
            Write-Error "Errorです.$PID $_"

            # 異常終了の場合の終了コード
            return 1
        }
    }

    # 関数の呼び出し
    Send-Email -To $To -From $From -Subject $Subject -Body $Body -SmtpServer $SmtpServer -Port $Port -Username $Username -Password $Password
} -ArgumentList $to, $from, $subject, $body, $smtpServer, $port, $username, $password

try {
    # ジョブの終了を待機して結果を表示
    $job | Wait-Job >> $null

    # 終了コードを取得
    $jobResult = $job | Receive-Job
    if ($jobResult -eq 0) {
        Write-Output "メール送信が正常に完了しました。"
    } else {
        Write-Output "メール送信中にエラーが発生しました。"
    }
}catch {
    Write-Host "$PID OK__ $_ "
} finally {
    # ジョブの削除
    Remove-Job -Job $job
}

```
```powershell:debugcursor.sql
DECLARE
    TYPE ref_cursor IS REF CURSOR;
    c ref_cursor;
    l_sql VARCHAR2(32767) := 'SELECT empno, ename, job, mgr, hiredate, sal, comm, deptno FROM emp'; -- 可変SQL
    l_column_value VARCHAR2(4000);
    l_column_name VARCHAR2(4000);
    l_column_list DBMS_SQL.desc_tab2;
    l_cursor INTEGER;
    l_dummy INTEGER;

    PROCEDURE debug_dynamic_cursor(p_cursor IN ref_cursor) IS
        l_col_cnt INTEGER;
        l_desc DBMS_SQL.desc_tab2;
        l_col_name VARCHAR2(1000);
        l_col_type NUMBER;
        l_rowid ROWID;
        l_value VARCHAR2(4000);
    BEGIN
        l_cursor := DBMS_SQL.TO_CURSOR_NUMBER(p_cursor);
        DBMS_SQL.describe_columns2(l_cursor, l_col_cnt, l_desc);
        
        FOR i IN 1 .. l_col_cnt LOOP
            l_col_name := l_desc(i).col_name;
            l_col_type := l_desc(i).col_type;

            DBMS_SQL.DEFINE_COLUMN(l_cursor, i, l_value, 4000);

            CASE l_col_type
                WHEN 1 THEN
                    DBMS_SQL.DEFINE_COLUMN(l_cursor, i, l_value, 4000); -- VARCHAR2
                WHEN 2 THEN
                    DBMS_SQL.DEFINE_COLUMN(l_cursor, i, l_value, 4000); -- NUMBER
                WHEN 12 THEN
                    DBMS_SQL.DEFINE_COLUMN(l_cursor, i, l_value, 4000); -- DATE
                ELSE
                    DBMS_SQL.DEFINE_COLUMN(l_cursor, i, l_value, 4000); -- その他の型
            END CASE;
        END LOOP;

        IF DBMS_SQL.FETCH_ROWS(l_cursor) > 0 THEN
            FOR i IN 1 .. l_col_cnt LOOP
                DBMS_SQL.COLUMN_VALUE(l_cursor, i, l_value);
                DBMS_OUTPUT.PUT_LINE(l_desc(i).col_name || ': ' || l_value);
            END LOOP;
        END IF;
        
        DBMS_SQL.CLOSE_CURSOR(l_cursor);
    END debug_dynamic_cursor;

BEGIN
    -- 動的SQLを開く
    OPEN c FOR l_sql;
    -- デバッグ用プロシージャを呼び出し
    debug_dynamic_cursor(c);
    -- カーソルを閉じる
    CLOSE c;
END;
/
```