なった・・・
真っ黒。何もできない・・・、けど右上をクリックするとちゃんとChromeが閉じる・・・
で、Windowsを再起動してもうまくいかず。
色々調べて、最終的にはショートカットのリンク先にパラメータを渡して解決。
“C:\Program Files (x86)\Google\Chrome\Application\chrome.exe” -disable-gpu
この「-disable-gpu」をつけることでとりあえずはなんとか普通に表示されるようになった。
なった・・・
真っ黒。何もできない・・・、けど右上をクリックするとちゃんとChromeが閉じる・・・
で、Windowsを再起動してもうまくいかず。
色々調べて、最終的にはショートカットのリンク先にパラメータを渡して解決。
“C:\Program Files (x86)\Google\Chrome\Application\chrome.exe” -disable-gpu
この「-disable-gpu」をつけることでとりあえずはなんとか普通に表示されるようになった。
サマリ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
できるのは解ったのだが、う~ん、どうなんだろう。
Displayを使ってみたくなってスキルを改修して申請したのだが、審査でNGになってしまった。
う~ん、Display表示の要素を足しただけなのになぁ・・・と思っていたら、ただ足しただけではダメっぽかった。
すいません、リファレンス読んでません。
Alexa Skills Kit Displayインターフェースのリファレンス
Qiita『Alexa ディスプレイ端末対応時にセッションをクローズする時の注意点』
審査でNGになってわかったのは、
・スキルの継続・終了とセッションの維持・破棄を明示的に合わせておくこと。
・セッションを維持させる場合は、維持させることがわかるような発話をすること。 (例えば、「ほかにないですか?」とか「もう一度教えて」とか)
で、何とか審査はパス。
お手数おかけしました。
今日は夢見が悪かった。
蛇に噛まれてゾンビ化しているラスカルみたいな動物に噛まれた夢を見た。
蛇に噛まれるのも嫌だが、ゾンビ化した奴も嫌だわ。
これはきっと、YouTube?か何かで、蛇を殺して蛇皮を水入れにする動画を見たからだ…と思ったのは、会社に来る最中だった。
と、人が倒れてる。トラックの下。傍らには自転車。そして血。
巻き込まれたんだな・・・
朝から嫌なもん見たな・・・と思いつつ、本当に嫌な思いをしているのは事故の当事者だろうから、こんなこと思うのも申し訳ない思いになった。
トラックが悪いのか、自転車が悪いのかはさておき、倒れていた人が無事でありますように。
あと、事故らないように気を付けよう。
と、思って歩いてたら、逆走するは、信号は守れないは、斜め横断はするは・・・という歩行者や自転車運転手に遭遇し、辟易してしまった。
ああいうの見たら解るのかな、自分たちのしていることがどういうことか、どういうことをもたらすのか。
とにかく、周りに人がいるところは、なるべくゆっくり走ろう、と思った。
加害者にはなりたくないわ。
HDDの容量を圧迫しているごみ箱の中身を削除したいのに、なんだかGUIでやると全然動作しない・・・
空にしますか?とも聞いてこない・・・
で、何とかならないのかと思ったら、コマンドプロンプト上でコマンドを打てばよいとのこと。
Cドライブのごみ箱なので、
rd /s c:\$Recycle.Bin
でやってみた。
でけた。
最近、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);
UPDATE、INSERT、DELETE文の最後に「RETURNING *」を加えてやると、「更新件数が返ってくる」というのをネット上で見たのだが、厳密にはどうやらそうではないらしいのでメモっておく。
INSERT .... RETURNING *
と書くとINSERTしたデータ行がすべて返ってくるのであって、件数が返ってくるわけではないのだ。
結局、
INSERT .... RETURNING {キー項目}
みたいに書いて、返ってきたデータの行数から更新件数を導きだす必要があるのだ。
だから、Javaなんかだと、SQL文を実行後のResultSetのgetRowメソッドを使う。
例)
int kensu = rs.getRow();
みたいに、返ってきたデータの行数を取得してやるのだ。
中途半端な日本語を書かれると、こういう勘違いをしてしまう。
毎度ですが、
明けました、おめでとうございます。
年末年始は相も変わらず食っちゃ寝、食っちゃ寝、の繰り返し。
しかし、今年はそれにプラスして、Amazonプライムでアニメやドラマ・映画を見ていて、結構よかった。
でも、疲れが抜けん。
そして、今日もまた仕事でミスってしまう。
と、毎年変わらぬことを繰り返しているのだが、そんだけ同じことばっかり繰り返している人生にも価値があるのだろうか・・・と少しばかり思って反省してしまう。
元日にはこれも毎年恒例の映画ファーストデーを利用した映画鑑賞をしたのだが、やはり今年はスターウォーズだ。
昔はファーストデーは1,000円で観られるから価値があったのだが、今は1,200円もする。
この200円の差は大きいな、と思いつつロビーでスナックとホットミルクティーを買ってシアターに入る。
年末に座席予約したとき、すでに上半分はほぼ埋まっていたので、仕方なく下のほうの席を取ったのだが、やっぱり角度的に下はきついなぁ、とちょっと後悔しながら予告を見る。
そして本編。
今までみたスターウォーズの中でも一番良かったんじゃないかい!!と思った。
観終わった感Maxな感じ。
(スピンオフとか作るの止めてね、ホント。もう「最後」気分味わったんで)
では、今年も「フォースと共にあらんことを」
って、なんのこっちゃ。
仕事で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 = "  ";
        
        // 解析
        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バージョンのダウンロードページを見つけたので貼っておく。
https://my.visualstudio.com/Downloads?q=visual%20studio%202015&wt.mc_id=o~msft~vscom~older-downloads
結局ダウンロードするには、Microsoftのアカウントが必要・・・
Windowsのコマンドプロンプトでpingをたたけば、そのIPアドレスと通信できるかがわかるが、ポート番号まで見てくれはしない。
IPアドレスとポート番号で通信できるか確認するときは、Powershellを使うらしい。
test-netconnection {IPアドレス} -port {ポート番号};
例)
test-netconnection 1.2.3.4 -port 5432;
で、実行すると以下のようなメッセージが返ってくる。
ComputerName     : 1.2.3.4
RemoteAddress    : 1.2.3.4
RemotePort       : 8080
InterfaceAlias   : Wi-Fi
SourceAddress    : 172.22.128.11
TcpTestSucceeded : True  ←つながりました!
警告: 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  ←ダメでした
はじめは撮った写真をアップして色々メモを残すためのブログだったが、もう仕事とかの備忘録になったので、記載内容にタイトルを合わせるするため変更した。
「Easy Photo Blog」改め、「My備忘録」。
安直やな・・・
基本
schtasks /query
CSV形式でファイル出力(/Vで詳細まで出力させる)
schtasks /query /V /FO CSV > d:\schtasklist1.csv
CSV形式でファイル出力(/Vで詳細まで出力させる,/NHで列タイトル非表示)
schtasks /query /V /NH /FO CSV > d:\schtasklist2.csv
こう見ると、タスクスケジューラにはいっぱいタスクが登録されているのだなぁ・・・と思った。
VMWare Workstationを起動して、仮想環境を立ち上げようとしたらエラーが出た。

あぁー、Dockerを入れた所為だなぁ・・・
というので、Dockerをアンインストールしようかと思ったが、一旦Dockerを使えなくしてしまう方法を探し、以下の手順でVMWareを無事起動できるようにした。
 
 
 
 
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 には互換性がありません。」のエラー解決」
で・・・
これでVMWare Workstationが動くか!と思ったら動かなかった・・・
しょうがないので、VMWare Workstationを再インストール。で、うまくVMWareの仮想環境が起動できた。
ん~、PC再起動でうまくいくって書いてあったんだけど、なんか変なことやらかしたかな。
まぁ、でも取りあえずはVMWareがうまく動いたので一安心。
で、最終的にはDockerをアンインストールしようと思うのだが、なにかうまい手はないか、と思っていたら、VMWare上のLinuxにDockerをいれるという手順を紹介している人がいて、「あぁ、なるほどな」などと感心してしまった。
また時間があったら試す予定。
まだDocker環境上へのDeployを試してない。これが肝心なのに。
そろそろDockerぐらい使えないと!と思って、仕事が暇な今こそ!とチャレンジ。
で、WordpressとMySQLの環境を作って繋げてやろうと意気込んで開始して、後述する手順でうまくいった。
ただ、後から分かったが、同じ環境でVMWare Workstationも使っているので、Dockerを動かしている間はVMWare側は動かせない・・・
しまった!!!
と思いつつ、とりあえずはDockerインストールと環境立ち上げの手順だけの備忘録を投稿。
docker pull mysql:5.7.25docker pull wordpress:latestdocker images で取り込めたかどうか確認docker run --name test-mysql -e MYSQL_ROOT_PASSWORD=password -d -p 3307:3306 mysql:5.7.25docker run --name test-wordpress --link test-mysql:mysql -d -p 8081:80 wordpress
docker container ls -a  で作成できたかどうか確認docker exec -it {コンテナ名} /bin/bash
cat wp-config.php で定義ファイル内の確認docker stop {コンテナ名}  コンテナ停止docker start {コンテナ名}  コンテナ開始docker rm  {コンテナ名}  コンテナ削除docker rmi  {イメージ名}  イメージ削除apt updateapt install -y vim (WordPressサーバ上で、VIMを利用する場合)apt install -y procps (MySQLサーバ上でMySQLが動作しているか確認で利用)ps aux (プロセス状態確認)mysql -u root -p (MySQL接続)exit (Linuxから抜ける)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.***」を忘れて、「うまくいかないぃ~」と思っていたが、こんなにあっさりできるとは。
SDKを入れたZipをアップロードすると、index.jsがないぜ!とエラーが出るので、ファイルツリーの表示欄を右クリックして、New Fileを選択。
できたファイルの名前をindex.jsに変更して、この中に処理を書いていく。
同じ要領で、package.jsonファイルも作っておく。
このとき、node_modulesフォルダとindex.js、package.jsonは同じフォルダ内に配置する。(違う場所に作ってもD&Dでファイル移動できる。)
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;
};
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関数のテストイベントの設定に貼り付けてテストしてみる。
すると、結果のログにエラー内容が出力されるので、そのエラー内容で処理の記載を変更してやればよい。
いつの間にかVersion2が出てた。
alexa-sdkからask-sdkに変わってた。
ということで、せっかくなので対応したLambda関数を作ってみようと思ったのだが、SDKを取得するのに相変わらずLinux環境が要るのだ。
簡単にできますよぉ~などという謳い文句とは裏腹に面倒なのだ。
しかし、普段Linuxを使うことのない私にとっては「いい勉強だ」などと半分自分に嘘をつきながら、SDKを取得してみた。
Linuxの環境はAWS EC2インスタンスで用意。
AWSのEC2(Amazon Linux)が便利なところは、ブラウザ上でコマンドが打てる点だ。気が利いている。
(とはいうものの、ブラウザを放置していると、コマンドが打てなくなるという不具合なのか、セッション切れなのかよくわからない現象が起こるので注意が必要。)
起動したら、npmまで入ってない・・・トホホ。
しょうがないからいろいろ入れることに。
※ここから下のコマンドは、上記の通りEC2インスタンス(Amazon Linux)をブラウザでコマンド実行例。
curl -L git.io/nodebrew | perl - setup export PATH=$HOME/.nodebrew/current/bin:$PATH source ~/.bashrc nodebrew help
で、NodeBrewのヘルプが見られたら、NodeBrewのインストールは完了。これでnpmも入る。
nodebrew install v8.10 nodebrew use v8.10 node -v
インストールして、8.10のバージョンを使うよ!と宣言後、nodeコマンドでインストールしたNode.jsのバージョンを確認。
npm install ask-sdk npm install ask-sdk-v1adapter npm install ask-sdk-core npm uninstall aws-sdk
上は例。要らないものは入れなくていい。取得したモジュールはすべてnode_modulesフォルダ内に入る。
AWS-SDKがあると容量が増えてLambda関数にアップロードできなくなるので、削除しておくこと。
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でも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行しか出てこない始末となった。
う~ん、なんとかなんないのだろうか。他にうまくできる方法を探さなければ、「関数作成してカーソル利用」しか思いつく方法がない・・・
今年はそんなに暑くないのだが、そろそろムシムシするので、と6/29にはクーラーをつけてしまった。
去年まではスプレーをプシューとしてから稼働させたのだが、今年はスプレーがなくてしょうがなく掃除しないままで稼働。
といっても、毎年そんなに汚れてないのは知っているので、今年はいいかなぁ・・・などとつけてみた。
うん、特に必要なさげ。
と、思ってるだけだが、本当はすごいカビ菌が繁殖してるかも。
というのは、窓の周りがカビだらけになっていることに、ハタと気づいてしまったので、クーラーも・・・となっているのである。
窓周辺のカビをふき取って、とりあえず湿気とりを簡易的に配置してみた。
後はカーテンを付け替えないといけない・・・
あー、カビは本当にヤダ。
やっぱり除湿機を買うべきだろうか。あぁ買うべきなんだろうな・・・