カテゴリー: Development

ORACLE覚書 階層データを引数として渡す

サマリ1
 |– 明細1-1
 |– 明細1-2
サマリ2
 |– 明細2-1

のような、階層のあるデータを引数で渡したいと思って、調べてみたらオブジェクト型なるものを発見。

で、実際にどうやって実装すればいいのか、また調べて実行。

<手順>
1, まずはCREATE TYPEでもって、任意のオブジェクト型を作ってみる。

・明細データの型を作る。(Javaでいうと、データクラスみたいなもの)

CREATE TYPE DTL_TYPE AS OBJECT (
   DTL_ID   VARCHAR2(5)
   DTL_NAME VARCHAR2(10)
);

・次に作った明細(DTL_TYPE)のテーブルを型にする。(JavaでいうとList<明細>みたいなもの)

CREATE TYPE DTLTAB_TYPE AS TABLE OF DTL_TYPE;

・明細が済んだら次はサマリも同じように型を作る。

CREATE TYPE SUM_TYPE AS OBJECT (
  SUM_ID   VARCHAR2(5)
 ,SUM_NAME VARCHAR2(20)
 ,DTL      DTLTAB_TYPE
);
CREATE TYPE SUMTAB_TYPE AS TABLE OF SUM_TYPE;

2, 試しに作ったオブジェクト型を引数に、関数を作ってみる

---------------------------------------
-- 明細データをカンマ区切りで取得
---------------------------------------
CREATE OR REPLACE FUNCTION OTAMESHI_FUNC(
  PARAM IN SUMTAB_TYPE 
) 
RETURN VARCHAR
IS
  TEMP    VARCHAR(1000);
BEGIN
  -- サマリをぐるぐる回す
  DECLARE
  CURSOR CURSUM IS SELECT SUM_ID, SUM_NAME, DTL FROM TABLE(CAST(PARAM AS SUMTAB_TYPE));
  CURSUM_ROW CURSUM%ROWTYPE;
  BEGIN
    OPEN CURSUM;
    LOOP
      FETCH CURSUM INTO CURSUM_ROW;
      EXIT WHEN CURSUM%NOTFOUND;

      TEMP := TEMP || ',<<' || CUR_ROW2.SUM_ID || ':' || CUR_ROW2.SUM_ID || '>>';
      
      -- 明細をぐるぐる回す
      DECLARE
      CURSOR CUR IS SELECT DTL_ID, DTL_NAME  FROM TABLE(CAST(CURSUM_ROW.DTL AS DTLTAB_TYPE));
      CUR_ROW CUR%ROWTYPE;
      BEGIN
        OPEN CUR;
        LOOP
          FETCH CUR INTO CUR_ROW;
          EXIT WHEN CUR%NOTFOUND;

          TEMP := TEMP || ',' || CUR.DTL_ID || ':' || CUR.DTL_ID;

        END LOOP;
      END;


    END LOOP;
  END;

  -- TEMPに入れたデータを返す
  RETURN TEMP;
EXCEPTION
  WHEN OTHERS THEN
    RETURN 'エラーです.';
END;

3, 作った関数を呼び出してみる

SELECT
  OTAMESHI_FUNC(
      SUMTAB_TYPE(
        SUM_TYPE('01'
               , 'まさか'
               , DTLTAB_TYPE(
                             DTL_TYPE('A1','やさか')
                            ) 
               )
       ,SUM_TYPE('02'
               , 'どうか'
               , DTLTAB_TYPE(
                             DTL_TYPE('D1','ぎんか')
                            ,DTL_TYPE('B1','きんか')
                            ) 
               )
     )
  )
FROM DUAL

できるのは解ったのだが、う~ん、どうなんだろう。

Alexa覚書 Display表示

Displayを使ってみたくなってスキルを改修して申請したのだが、審査でNGになってしまった。

う~ん、Display表示の要素を足しただけなのになぁ・・・と思っていたら、ただ足しただけではダメっぽかった。
すいません、リファレンス読んでません。

Alexa Skills Kit Displayインターフェースのリファレンス

Qiita『Alexa ディスプレイ端末対応時にセッションをクローズする時の注意点』

審査でNGになってわかったのは、

・スキルの継続・終了とセッションの維持・破棄を明示的に合わせておくこと。
・セッションを維持させる場合は、維持させることがわかるような発話をすること。 (例えば、「ほかにないですか?」とか「もう一度教えて」とか)

で、何とか審査はパス。
お手数おかけしました。

コマンドプロンプト覚書 ゴミ箱を空に

HDDの容量を圧迫しているごみ箱の中身を削除したいのに、なんだかGUIでやると全然動作しない・・・
空にしますか?とも聞いてこない・・・

で、何とかならないのかと思ったら、コマンドプロンプト上でコマンドを打てばよいとのこと。

Cドライブのごみ箱なので、

rd /s c:\$Recycle.Bin

でやってみた。
でけた。

PowerShell覚書 ファイル操作いろいろ

最近、VBSよりPowerShellのほうをよく使うようになった。
イケてないところは多々あるものの、デバッグもしやすいので便利だ。

ファイル操作のアレコレをこの投稿にいろいろ書き足していこうと思う。
(アレコレって、漢字だと「彼是」って書くのね。知らんかった。)

【ファイル検索】
例えば、「Cドライブ」直下のファイルを漁りたいときはこんな感じ。
Get-ChildItemの戻り値を「 」(半角スペース)でSplitしてやれば、ファイル名のリストを取得できる。

$dir = "C:\";
$files = Get-ChildItem $dir;
$files_arr = $files -split " ";

【フォルダ検索】
「W」で始まる「Cドライブ」直下のディレクトリを漁りたいときはこんな感じ。
Get-ChildItemの戻り値を「 」(半角スペース)でSplitしてやれば、ディレクトリ名のリストを取得できる。
Where-Object で、「$_.PSIsContainer」を指定すればディレクトリだけを取得できる。

$dir = "C:\";
$subdir = Get-ChildItem $dir | Where-Object { $_.PSIsContainer } | Select-String "^W";
$sub_arr = $subdir -split " ";

【フォルダ・ファイル有無確認】
指定フォルダ、ファイルがあるかどうかは「Test-Path」を使う。

if (Test-Path ($dir)) {
   # あるとき
} else {
   # ないとき
}

【フォルダ作成】
フォルダ作成は「New-Item」で、パスを指定して、「-ItemType Directory」のオプションをつける。
「-Force」はすでにフォルダがあるときは、エラーにならないようにするときにつける。

New-Item $dir -ItemType Directory -Force;

【ファイルコピー】
ファイルコピーは「Copy-Item」で、コピー元のパスを指定して、「-Destination」でコピー先を指定する。
「-Force」はすでにコピー先にファイルがあっても、エラーにならないようにするときにつける。

Copy-Item $src_path -Destination $goto_path -Force;

【ファイル移動】
ファイル移動はコピーしてから削除するのがいい。
「Remove-Item」の「-Recurse」は削除対象がフォルダであれば、中のファイルを削除してからフォルダも消してくれる。
「-Force」をつければ、隠しファイルとかも消してくれるそう。

Copy-Item $src_path -Destination $goto_path -Force;
Remove-Item -Path $src_path -Recurse -Force;

【ライブラリ利用】
ライブラリは、パスを指定して読み込んでやらないと使えない。こんな感じ。

[string]$dll       = "C:\....\.....dll";
[void][reflection.assembly]::LoadFrom($dll);

PostgreSQL覚書 RETURNIG *

UPDATE、INSERT、DELETE文の最後に「RETURNING *」を加えてやると、「更新件数が返ってくる」というのをネット上で見たのだが、厳密にはどうやらそうではないらしいのでメモっておく。

INSERT .... RETURNING *

と書くとINSERTしたデータ行がすべて返ってくるのであって、件数が返ってくるわけではないのだ。
結局、

INSERT .... RETURNING {キー項目}

みたいに書いて、返ってきたデータの行数から更新件数を導きだす必要があるのだ。
だから、Javaなんかだと、SQL文を実行後のResultSetのgetRowメソッドを使う。
例)

int kensu = rs.getRow();

みたいに、返ってきたデータの行数を取得してやるのだ。

中途半端な日本語を書かれると、こういう勘違いをしてしまう。

JavaScript覚書 JSONパース

仕事でJSONデータをバリバリ使うことになった。
けど、階層が多いからちゃんとデータ設定できているのか気になって、解析しようとJavaScriptで組むことにした。
(本当はExcelのマクロでセルにペタッと出力させたかったのだが)

<body>
    <textarea id="src"></textarea><br>
    <button onclick="javascript:return parseData();">解析</button><br>
    <pre id="res"></pre>
    
    <script>
        var doc = "";
        var indent = "&nbsp;&nbsp;";
        
        // 解析
        function parseData() {
            var srcObj = document.getElementById("src");
            var resObj = document.getElementById("res");
            doc = "";
            resObj.innerHTML = doc;
            
            var json = JSON.parse(srcObj.value);
            convJson(json, "");
            resObj.innerHTML = doc;
        }
        
        // 解析処理本体
        // obj : JSONデータ, caps : 階層表示インデント
        function convJson(obj, caps) {
            if (typeof(obj) == "object") {
                for (var i in obj) {
                    if (typeof(obj[i]) == "object") {
                        doc += caps + "[" + i + "]<br>";
                        convJson(obj[i], caps + indent);   // 下の階層を解析
                    } else {
                        doc += caps + i + " : " + obj[i] + "<br>";
                    }
                }
            } else {
                        doc += caps + i + " : " + obj[i] + "<br>";
            }
        }
    </script>
</body>

Visual Studio 2015 インストーラ他

なんだかもう仕事がいやになりました。
色んなことが進みません。

サーバと接続できないし、Visual Studioもインストールできないし・・・

なんで、最新バージョンしかださず、古いバージョンも簡単に表示してくれないんだ!
必要なんだよ!!!

と、怒っていたが、すんなり行くときはいくもので。
とりあえず2015バージョンのダウンロードページを見つけたので貼っておく。

https://my.visualstudio.com/Downloads?q=visual%20studio%202015&wt.mc_id=o~msft~vscom~older-downloads

結局ダウンロードするには、Microsoftのアカウントが必要・・・

PowerShell覚書 指定IPアドレス+ポート番号で接続確認

Windowsのコマンドプロンプトでpingをたたけば、そのIPアドレスと通信できるかがわかるが、ポート番号まで見てくれはしない。
IPアドレスとポート番号で通信できるか確認するときは、Powershellを使うらしい。

test-netconnection {IPアドレス} -port {ポート番号};

例)

test-netconnection 1.2.3.4 -port 5432;

で、実行すると以下のようなメッセージが返ってくる。

OKの時

ComputerName : 1.2.3.4
RemoteAddress : 1.2.3.4
RemotePort : 8080
InterfaceAlias : Wi-Fi
SourceAddress : 172.22.128.11
TcpTestSucceeded : True ←つながりました!

NGの時

警告: TCP connect to (1.2.3.4 : 3306) failed
警告: Ping to 1.2.3.4 failed with status: TimedOut

ComputerName : 1.2.3.4
RemoteAddress : 1.2.3.4
RemotePort : 3306
InterfaceAlias : Wi-Fi
SourceAddress : *.*.*.*(こっちのアドレス)
PingSucceeded : False  ←ダメでした
PingReplyDetails (RTT) : 0 ms
TcpTestSucceeded : False  ←ダメでした

Windows覚書 タスクスケジュール一覧の取得コマンド

基本

schtasks /query

CSV形式でファイル出力(/Vで詳細まで出力させる)

schtasks /query /V /FO CSV > d:\schtasklist1.csv

CSV形式でファイル出力(/Vで詳細まで出力させる,/NHで列タイトル非表示)

schtasks /query /V /NH /FO CSV > d:\schtasklist2.csv

こう見ると、タスクスケジューラにはいっぱいタスクが登録されているのだなぁ・・・と思った。

Docker覚書 VMWareが使えない・・・のときの対処

VMWare Workstationを起動して、仮想環境を立ち上げようとしたらエラーが出た。

あぁー、Dockerを入れた所為だなぁ・・・
というので、Dockerをアンインストールしようかと思ったが、一旦Dockerを使えなくしてしまう方法を探し、以下の手順でVMWareを無事起動できるようにした。

  1. タスクバーの検索アイコンをクリックして「ローカルグループポリシーの編集」と入れて、ローカルグループポリシーにアクセス
  2. コンピューターの構成>管理用テンプレート>システム>Device Guardを選択し、「仮想化ベースのセキュリティを有効にする」の設定を無効にする。
  3. 次はコントロールパネル>プログラム>Windowsの機能の有効化または無効化をクリック。
  4. Hyper-Vのチェックを外す(下層もすべて外す)
  5. 適用ボタンをクリックすると、再起動するかどうか聞かれるが、ここでは再起動しない。
  6. コマンドプロンプトを念のため管理者権限で開いて、以下のコマンドを打つ。
    (Xドライブを作成するので、Xドライブががないことが前提。もしXドライブがすでにある場合は、「X:」の箇所を別のドライブに変更して実行。)

    mountvol X: /s
    copy %WINDIR%\System32\SecConfig.efi X:\EFI\Microsoft\Boot\SecConfig.efi /Y
    bcdedit /create {0cb3b571-2f2e-4343-a879-d86a476d7215} /d “DebugTool” /application osloader
    bcdedit /set {0cb3b571-2f2e-4343-a879-d86a476d7215} path “\EFI\Microsoft\Boot\SecConfig.efi”
    bcdedit /set {bootmgr} bootsequence {0cb3b571-2f2e-4343-a879-d86a476d7215}
    bcdedit /set {0cb3b571-2f2e-4343-a879-d86a476d7215} loadoptions DISABLE-LSA-ISO,DISABLE-VBS
    bcdedit /set {0cb3b571-2f2e-4343-a879-d86a476d7215} device partition=X:
    mountvol X: /d
    

    PCによっては、以下のコマンドを打つ。

    bcdedit /set hypervisorlaunchtype off  
    

    参考URL「あさまのブログ VMWare仮想マシン起動時に「Device/Credential Guard には互換性がありません。」のエラー解決」

  7. そしてようやくPC再起動。
    (再起動後はDockerは使えなくなる。)

で・・・

これでVMWare Workstationが動くか!と思ったら動かなかった・・・

しょうがないので、VMWare Workstationを再インストール。で、うまくVMWareの仮想環境が起動できた。
ん~、PC再起動でうまくいくって書いてあったんだけど、なんか変なことやらかしたかな。
まぁ、でも取りあえずはVMWareがうまく動いたので一安心。
で、最終的にはDockerをアンインストールしようと思うのだが、なにかうまい手はないか、と思っていたら、VMWare上のLinuxにDockerをいれるという手順を紹介している人がいて、「あぁ、なるほどな」などと感心してしまった。
また時間があったら試す予定。
まだDocker環境上へのDeployを試してない。これが肝心なのに。

Docker覚書 Win10にインストールの巻

そろそろDockerぐらい使えないと!と思って、仕事が暇な今こそ!とチャレンジ。
で、WordpressとMySQLの環境を作って繋げてやろうと意気込んで開始して、後述する手順でうまくいった。

ただ、後から分かったが、同じ環境でVMWare Workstationも使っているので、Dockerを動かしている間はVMWare側は動かせない・・・
しまった!!!
と思いつつ、とりあえずはDockerインストールと環境立ち上げの手順だけの備忘録を投稿。

Windows10でDocker上にWordPressを立ち上げる

  1. Docker CE → Desktop Docker for Windows インストール
  2. Desktop Dockerを起動、Setting
    • スタートメニュー等よりDesktop Dockerを起動
    • タスクバーにDockerアイコンが表示されたらアイコンを右クリックしてSettingをクリック
    • DaemonのExperimentalにチェックしApply
    • NetworkのDNS ServerはFixed:を選択して「8.8.8.8」を設定
    • ResetでRestart Docker…をクリック
  3. コマンドプロンプトを起動
  4. Imageの取込コマンド実行(次のrunコマンド実行でimageがなかったら自動的に取り込むのでここは省略できる。)
    • docker pull mysql:5.7.25
    • docker pull wordpress:latest
    • docker images で取り込めたかどうか確認
  5. コンテナー作成・起動
    • docker run --name test-mysql -e MYSQL_ROOT_PASSWORD=password -d -p 3307:3306 mysql:5.7.25
    • docker run --name test-wordpress --link test-mysql:mysql -d -p 8081:80 wordpress
      • それぞれ、ポート番号は他と被らない番号にしておきます。
    • docker container ls -a  で作成できたかどうか確認
  6. localhost:8081に接続して確認
    • WordPressの初期設定画面が表示されたら、うまく動作していることになる。

うまくいかないパターン

  • DB接続できない
    • MySQLのバージョンがWordpressと噛み合ってないのが原因

他のDockerコマンド

  • docker exec -it {コンテナ名} /bin/bash
    • Linux上でコマンド操作可能に
    • cat wp-config.php で定義ファイル内の確認
  • docker stop {コンテナ名}  コンテナ停止
  • docker start {コンテナ名}  コンテナ開始
  • docker rm {コンテナ名}  コンテナ削除
  • docker rmi {イメージ名}  イメージ削除

他のLinuxコマンド

  • apt update
  • apt install -y vim (WordPressサーバ上で、VIMを利用する場合)
  • apt install -y procps (MySQLサーバ上でMySQLが動作しているか確認で利用)
  • ps aux (プロセス状態確認)
  • mysql -u root -p (MySQL接続)
  • exit (Linuxから抜ける)

Node.js覚書 外部ファイルの変数定義を参照する

Map変数の設定するデータの量が多すぎて、別のファイルに移せないかなぁと思って調べた。

呼出元

変数

var exp = require('./mapxxx.js');
var mapX = exp.data;

呼出先

ここでは、mapxxx.jsとして保存。
変数定義後、exports.ホゲホゲ = 定義した変数 としておけば、呼出元で使えるようになる。

var maps = new Map([
    ['D01', 'DDDDDD'],
    ['E02', 'EEEE'],
    ........
]);

exports.data = maps;

最後の「exports.***」を忘れて、「うまくいかないぃ~」と思っていたが、こんなにあっさりできるとは。

AWS覚書 Lambda関数を作る(2)~コード編集

SDKを入れたZipをアップロードすると、index.jsがないぜ!とエラーが出るので、ファイルツリーの表示欄を右クリックして、New Fileを選択。
できたファイルの名前をindex.jsに変更して、この中に処理を書いていく。
同じ要領で、package.jsonファイルも作っておく。
このとき、node_modulesフォルダとindex.js、package.jsonは同じフォルダ内に配置する。(違う場所に作ってもD&Dでファイル移動できる。)

index.jsサンプル

LaunchRequestHandlerは、呼出名のみ言われたときの処理。
*******IntentHandlerは、*******Intentに引っかかったときの処理。(*******は任意で。)
その他、HelpやErrorのIntentHandlerは処理部分をLaunchRequestHandlerと同じように記載していく。
今回は、DBアクセスなどがないので、軽量SDK「ask-sdk-core」を利用する。

const Alexa = require('ask-sdk-core');

// --------------------------------------------------------------------
const LaunchRequestHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
  },
  handle(handlerInput) {
    return handlerInput.responseBuilder
                       .speak('返答内容')
                       .reprompt('質問内容')
                       .getResponse();
  }
};

// customize intent ***************************
const *******IntentHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
      && handlerInput.requestEnvelope.request.intent.name === '*******Intent';
  },
  handle(handlerInput) {
    
    var intent = handlerInput.requestEnvelope.request.intent;
    var dat = intent && intent.slots && intent.slots..*****. && intent.slots.*****.value;
    
    return handlerInput.responseBuilder.speak(speechText)
                                       .withSimpleCard('タイトル', '返答内容')
                                       .getResponse();
  },
};
// customize intent ***************************

const HelpIntentHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
      && handlerInput.requestEnvelope.request.intent.name === 'AMAZON.HelpIntent';
  },
  handle(handlerInput) {
      ・・・・(省略)・・・・
  },
};

const CancelAndStopIntentHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
      && (handlerInput.requestEnvelope.request.intent.name === 'AMAZON.CancelIntent'
        || handlerInput.requestEnvelope.request.intent.name === 'AMAZON.StopIntent');
  },
  handle(handlerInput) {
      ・・・・(省略)・・・・
  },
};

const SessionEndedRequestHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'SessionEndedRequest';
  },
  handle(handlerInput) {
      ・・・・(省略)・・・・
  },
};

const ErrorHandler = {
  canHandle() {
    return true;
  },
  handle(handlerInput, error) {
      ・・・・(省略)・・・・
  },
};

// --------------------------------------------------------------------
let skill;
exports.handler = async function (event, context) {
    if (!skill) {
      skill = Alexa.SkillBuilders.custom()
                                          .addRequestHandlers( LaunchRequestHandler,
                                                               *******IntentHandler,
                                                               HelpIntentHandler,
                                                               CancelAndStopIntentHandler,
                                                               SessionEndedRequestHandler
                                          )
                                          .addErrorHandlers(ErrorHandler)
                                          .create();
    }

    const response = await skill.invoke(event, context);
    return response;
};

package.jsonサンプル

nameはLambda関数の名前でOK。versionは関数のバージョン。dependenciesに利用するSDKを設定。

{
  "name": "****",
  "version": "1.0.0",
  "private": true,
  "dependencies": {
    "ask-sdk-core": "^2.7.0"
  }
}

処理を記述後、Alexaからの呼出で想定通りの返答がなされていればOK。
もし、うまくいかないときは、Alexa Developer コンソールのテスト画面に表示される「スキルI/O」の「JSON入力」に出力されるJSONをコピーして、Lambda関数のテストイベントの設定に貼り付けてテストしてみる。
すると、結果のログにエラー内容が出力されるので、そのエラー内容で処理の記載を変更してやればよい。

AWS覚書 Lambda関数を作る(1)~Alexa SDK for Node.js v2

いつの間にかVersion2が出てた。
alexa-sdkからask-sdkに変わってた。

ということで、せっかくなので対応したLambda関数を作ってみようと思ったのだが、SDKを取得するのに相変わらずLinux環境が要るのだ。
簡単にできますよぉ~などという謳い文句とは裏腹に面倒なのだ。

しかし、普段Linuxを使うことのない私にとっては「いい勉強だ」などと半分自分に嘘をつきながら、SDKを取得してみた。
Linuxの環境はAWS EC2インスタンスで用意。
AWSのEC2(Amazon Linux)が便利なところは、ブラウザ上でコマンドが打てる点だ。気が利いている。
(とはいうものの、ブラウザを放置していると、コマンドが打てなくなるという不具合なのか、セッション切れなのかよくわからない現象が起こるので注意が必要。)

起動したら、npmまで入ってない・・・トホホ。
しょうがないからいろいろ入れることに。
※ここから下のコマンドは、上記の通りEC2インスタンス(Amazon Linux)をブラウザでコマンド実行例。

NodeBrewのインストール

NodeBrewはNode.jsのバージョン切替用ツールらしい。そして、NodeBrewを使ってNode.jsのいろんなバージョンを取得し、バージョン切替が簡単にできるのである。
すごいなぁ・・・などと感心。

curl -L git.io/nodebrew | perl - setup
export PATH=$HOME/.nodebrew/current/bin:$PATH
source ~/.bashrc
nodebrew help

で、NodeBrewのヘルプが見られたら、NodeBrewのインストールは完了。これでnpmも入る。

Node.jsのインストール

ここではversion8で利用予定のversion8.10.*のインストールを行う。

nodebrew install v8.10
nodebrew use v8.10
node -v

インストールして、8.10のバージョンを使うよ!と宣言後、nodeコマンドでインストールしたNode.jsのバージョンを確認。

ask-sdkをnpmを使ってインストール

npm install ask-sdk
npm install ask-sdk-v1adapter
npm install ask-sdk-core
npm uninstall aws-sdk

上は例。要らないものは入れなくていい。取得したモジュールはすべてnode_modulesフォルダ内に入る。
AWS-SDKがあると容量が増えてLambda関数にアップロードできなくなるので、削除しておくこと。

ask-sdkをZip圧縮後、S3へアップロード

cd ./
zip -r alexa_ask_sdk.zip node_modules

上の階層に行って、node_modulesフォルダを丸ごとZIP圧縮。
Zipの中身を確認するときは、以下のようにless文を使う。

less alexa_ask_sdk.zip

(コマンド終了は:q)

次はS3へアップロード。これはAWS CLIを使う。
{****}はバケット名。

aws s3 cp alexa_ask_sdk.zip s3://{****}/alexa.zip

S3にアップロードしたzipをLambda関数の関数コードのところで取り込めば、ようやくコードの編集が始められる。
続きは(2)へ。

PostgreSQL覚書 crosstabでPIVOT

PostgreSQLでもOracleのPIVOTみたいなことをやりたいなぁ・・・と思って調べた。
crosstabらしい。
拡張機能らしい。
早速PgAdminで以下のSQLを実行して取り込む。

CREATE EXTENSION IF NOT EXISTS tablefunc;

いくつか関数が追加された中に、crosstabがあればOK。
で、続けてSQLを組み立てる。

今回は以下のように縦並びである店舗日別データ(qty)を全店SUM(qty)にして横並びにしてみる。
tab_shop_qty カラム構成(y |m |d |no |shop | qty)

CREATE TABLE tab_shop_qty (
  y integer,
  m integer,
  d integer,
  no integer,
  shop character varying(10),
  qty numeric(5,0)
);
INSERT INTO public.tab_shop_qty values(2019,1,1,1, 'abc', 10);
INSERT INTO public.tab_shop_qty values(2019,1,1,2, 'abc', 8);
INSERT INTO public.tab_shop_qty values(2019,1,2,1, 'abc', 25);
INSERT INTO public.tab_shop_qty values(2019,1,2,2, 'abc', 15);
INSERT INTO public.tab_shop_qty values(2019,1,1,1, 'efg', 12);
INSERT INTO public.tab_shop_qty values(2019,1,1,2, 'efg', 7);
INSERT INTO public.tab_shop_qty values(2019,1,1,2, 'efg', -2);
INSERT INTO public.tab_shop_qty values(2019,1,2,1, 'hij', 20);

店舗(shop)別に、日毎のqtyを横に並べる

SELECT c.shop,c."1",c."2",...,c."31"
FROM crosstab(
      'SELECT shop,d,SUM(qty) qty FROM tab_shop_qty WHERE y=2019 AND m=1 GROUP BY shop,d ORDER BY 1'
     ,'SELECT d FROM generate_series(1,31) d'
     )  
AS c(shop varchar,"1" numeric,"2" numeric,...,"31" numeric) 
ORDER BY c.shop

上の例だと、shopというカラム1つだけがキーになっているのでこのようなSQLで済むのだが、キーが複数カラムある場合は、第1引数のSQL文にORDER BY句を追加するという工夫が必要。

店舗,NO(shop,no)別に、日毎のqtyを横に並べる

SELECT c.shop,c.no,c."1",c."2",...,c."31"
FROM crosstab(
      'SELECT shop,no,d,SUM(qty) qty FROM tab_shop_qty WHERE y=2019 AND m=1 GROUP BY shop,no,d ORDER BY 2'
     ,'SELECT d FROM generate_series(1,31) d'
     )  
AS c(shop varchar,"1" numeric,"2" numeric,...,"31" numeric) 
ORDER BY c.shop,c.no

ドキュメントurl: https://www.postgresql.jp/document/9.6/html/tablefunc.html では、
第1引数のORDER BY句は
「実際は、同じrow_name(キー項目のこと)を持つ値をまとめられるように、source_sql(第1引数のSQL文のこと)問い合わせでは常にORDER BY 1を指定すべきです。」
との記載があるが、「ORDER BY 1」なのであれば指定ありとなしでは変わりはなかった。

結局、ORDER BYの指定の仕方がイマイチわからなかったが、ORDER BYの1つめで指定したカラム数まではキー、次が第2引数のデータとの関連付け用、その次がPIVOTとして出力するデータ、かな・・・などと思った。

————————
<2019/08/08 追記>
いろいろ試していた時に発覚したことがあったので追記。
crosstabの第1引数のSQL文だが、グルーピングするカラムのうち、1つでもデータが1種類しかないカラムが存在した場合、うまく動作しないことが分かった。
上の説明文だと自分で後から見直しても、なんのこっちゃ!って思うと推測するので、追記しておくと・・・・
最後の例だとshop,noの2カラムがグループ,dが横並びにするカラム,qtyが横並びにするデータとなるのだが、
テーブルのレコードにshop=’abc’のデータ1種類しかない場合、crosstabがうまく動作しないということがある、ということだ。
ORDER BY 2までならうまくいくかもしれないが、少なくとも私が試してみたORDER BY 3(つまりグルーピングするカラムが3列)の場合、うまくマージされず、本来は何全行と出るはずの結果レコードが1行しか出てこない始末となった。

う~ん、なんとかなんないのだろうか。他にうまくできる方法を探さなければ、「関数作成してカーソル利用」しか思いつく方法がない・・・

JQuery3覚書 属性・階層と付き合う

HTMLを初めて触ったのは22とか23の頃で、もう20年以上前の話になってしまった。
そのころはタグは大文字で書くのが普通だった。

いつの間にかIDやNAME、CLASSでスタイルを指定できる世の中になってしまった。
タグは小文字で書くのが当然になった。

そして今、属性を自分で勝手に作ってデータ操作する時代。
jsのライブラリもいっぱい世の中に出回っているが、何をやっているのかわからないやら、余計なことをするわで、結局自分でゴリゴリ作った方がカスタマイズしやすいんではないのか?などと後戻りできない旅路の一歩をを踏み出してしまう。

■ 属性

例えばの以下のようなテーブル構成の場合でいうならば、「data-no」というのが勝手に作った属性。
この属性の設定値を取得する方法をメモする。

<table id="tab1">
    <thead>
        <tr>
            <th data-no='1'>COL1</th>
            <th data-no='2'>COL2</th>
            <th data-no='3'>COL3</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td data-cd="101">DATA1-1</td>
            <td data-cd="102">DATA1-2</td>
            <td data-cd="103">DATA1-3</td>
        </tr>
        <tr>
            <td data-cd="201">DATA2-1</td>
            <td data-cd="202">DATA2-2</td>
            <td data-cd="203">DATA2-3</td>
        </tr>
    </tbody>
</table>

JQueryで、thタグをクリックしたときに、そのdata-noが何かをアラート表示させてみる。

    $(document).on('click', '#tab1>thead>tr>th', function (event) {
        alert($(this).attr('data-no'));
    });

これは、以下のように書いても問題はないのだが、もし当該テーブル、あるいはテーブルの要素が動的であった場合は、CLICKイベントが検知できないので、上記のように書いた方が汎用的のよう。

    $('#tab1>thead>tr>th').on('click', function (event) {
        // (記載略)
    });

対象の指定は下のように省略して書いても、tbodyタグにthがないときは問題ない。

    $('#tab1 th').on('click', function (event) {
        // (記載略)
    });

なお、attrは、attr({要素名})とすると、指定した要素の値があればその設定を返し、ないときはundefindedを返す。
そして、指定した要素に値を設定するときは、 attr({要素名}, {設定値}) とする。


■ 階層(親・子)

次に階層(親・子)を考慮しながら値を取る方法をメモしておく。
はじめにイベントの対象要素を「$(‘#tab1>thead>tr>th’)」と、恩着せがましく記述したのだが、これは親子関係をわかりやすくするためでもある。

#tabの子供がthead、その子供はtr、その子供はth

ということで、逆に言うと、thの親はtr、trの親は・・・と、もう書かないけどそんな感じ。

> 階層(親)

親は簡単に取れる。なんせ1つだから。

親である(tr)のHTML記述内容をアラート表示させてみる。

    $(document).on('click', '#tab1>thead>tr>th', function (event) {
        alert($(this).parent().html());
    });

簡単。親の親(thead)はparent().parent()。
親の親の親(#tab1)なら、parent().parent().parent()ということに。もはやfor文でも使った方がよさそうだ。

> 階層(子)

子の取り方はいろいろ。

試しに#tab1 tbody tr の行をクリックしたら、子要素(td)を順番にとって、コンソールに出力してみる。

    $(document).on('click', '#tab1>tbody>tr', function (event) {
        $(this).children().each(function (event) {    // (a)
            console.log($(this).html());              // (b)
        });
    });

(a)の$(this)は「tr」の要素のこと。
(b)の$(this)は子要素「td」のこと。
これだと、trの子要素はすべて取得できるので、tdとthの両方のタグがある場合は両方とも取得できる。

    $(document).on('click', '#tab1>tbody>tr', function (event) {
        $(this).children('td').each(function (event) {
            console.log($(this).html());
        });
    });

これだと、trの子要素のうちtdタグのみを取得できる。

    $(document).on('click', '#tab1>tbody>tr', function (event) {
        $(this).find('td').each(function (event) {
            console.log($(this).html());
        });
    });

findだと、引数で指定した要素であれば、子要素でも、孫要素でも範囲内すべてから取得可能になる。
サンプルだとテーブルの中にテーブルを入れ子していないので、子要素のみ取得できる。

children()を使うか、find()を使うかは子要素だけなのか、関係なく下の階層すべてに適用するのかで決める。

> 階層(子要素のn個目)
    $(document).on('click', '#tab1>tbody>tr', function (event) {
        $(this).children('td:nth-child(2)').each(function (event) {
            console.log($(this).html());
        });
    });

childrenで対象を指定するときに「:nth-child({n})」を使うと、n番目の要素だけ取得できる。

今日はここまで。次はソートについてメモせねば。

JQuery3覚書 初歩的なこと

歩道を歩いていて、前のヤツがタバコ吸うとこっちは受動喫煙になる。
もっとも、周りに気使う喫煙者は、路上や屋外で喫煙はしない。
路上や屋外で喫煙する奴は基本的にアホです。ハイ。ハッキリ断言します。
周りに人がいる、来るかもしれない場所での喫煙はやめましょう。ただの迷惑。

久しぶりの投稿の冒頭がこれかい!って感じですが、いつになったら分かるんでしょうねって思うので書いてみた。

で、JQuery。
今使っているのはバージョンは3.3.1。
色々データをこねくり回さなければいけないということで、ネットを検索しながらHTML/CSS/JSのファイルを作成。
今頃気づいたのだが、JavaScriptとJQueryは別モノ。
当たり前なのだが、JQueryで取得した要素に、JavScriptの標準メソッドを適用させようとするとエラーになる。

$('#table1').innerHTML 

これはダメ。左はJQuery、右はJavaScript標準。

$('#table1').html()

これはJQueryの記述方法で、OK。

document.getElementById('table1').innerHTML 

これはJavaScript標準の記述方法で、OK。

という話なのである。

Excel VBA覚書 クリップボード

クリップボードに文字列を貼り付けるときに、貼り付かないケースがあったので、
別の方法を調べてみた。

String型変数 buf の内容をクリップボードに設定するということで・・・
a) DataObjectを使う方法1

        Dim cb As Object
        Set cb = New DataObject
        cb.SetText buf
        cb.PutInClipboard

b) DataObjectを使う方法2

        With New MSForms.DataObject
            .SetText buf
            .PutInClipboard
        End With

c) TextBoxを使う方法

       With CreateObject("Forms.TextBox.1")
          .MultiLine = True
          .Text = buf
          .SelStart = 0
          .SelLength = .TextLength
          .Copy
        End With
        If cb.CanPaste Then cb.Paste

Access覚書 リンク貼替

結構間が開いてしまった。
1か月に1度ぐらいはせめて投稿しようと思いつつ。

テーブルやらクエリやらがAサーバのDBをみているのだが、Bサーバのテスト環境のDBをみるように変えたい
となったとき、結構大変である。
標準機能で変更できるのかどうか、やり方がわからん。

ということで、VBAでできないか探してみたら、やはり一括でどべっと変更できるやり方があった。

' テーブルとクエリのリンク先を変更
' 引数 :from_srv = 変更前(例:172.0.0.1)
'    :to_srv   = 変更後(例:196.128.1.10)
Sub ChangeLinks(from_srv As String, to_srv As String )

    Dim db As DAO.Database
    Dim tb As DAO.TableDef
    Dim qr As DAO.QueryDef
    
    Set db = CurrentDb
    On Error Resume Next
    For Each tb In db.TableDefs
        If tb.Connect <> "" Then
            tb.Connect = Replace(tb.Connect, from_srv , to_srv)     'サーバ変更
            tb.RefreshLink  ' テーブルはリフレッシュ
        End If
    Next
    For Each qr In db.QueryDefs
        If qr.Connect <> "" Then
            qr.Connect = Replace(qr.Connect, from_srv , to_srv)     'サーバ変更
        End If
    Next
End Sub

パスワードとかだと、
Replace(tb.Connect, “PWD=***”, “PWD=***”)
ってな感じで変換してやる。

ExcelVBA覚書 Transport関数をうまく使う

SQLを実行してきてデータを取得するまではよいのだが、その後シート上に出力するときに異様に時間がかかる場合がある。
で、端末個体の理由は置いといて、複数のワークブックを開けていた場合に起こるのであれば、対策は、

 処理中は必要なとき以外「自動計算」させない!

ということらしい。

With Application
    .ScreenUpdating = False
    .Calculation =xlCalculationManual
    .EnableEvents = False
End With

としておけば、
 画面を更新せず、
 再計算させず、
 イベント有効にしない
とできるわけである。
これで、余計なイベントも発生しないので良いのだが、再計算してほしいときにはCaluculateメソッドを発行しておかねばならない。

で、Application.Calculation = xlCalculationManualをせず、シート上にバサッと出力したいなぁ~などと思っていたら、

 WorksheetFunction.Transpose()

シート関数のTRANSPORT関数を使って、引数に出力したい配列を設定することで、ドバっとあっという間に出力できてしまうやり方を紹介しているページがあったので、ありがたやありがたやとやってみたところ、本当にあっさりとドバっと出力された。

参考URL:エクセルの神髄「マクロVBAの高速化・速度対策の具体的手順と検証」

入れる配列は(横、縦)の状態にして、Transport関数で縦横逆にして出力!ってなことになっている。
ありがたやありがたや!