なった・・・
真っ黒。何もできない・・・、けど右上をクリックするとちゃんと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になってわかったのは、
・スキルの継続・終了とセッションの維持・破棄を明示的に合わせておくこと。
・セッションを維持させる場合は、維持させることがわかるような発話をすること。 (例えば、「ほかにないですか?」とか「もう一度教えて」とか)
で、何とか審査はパス。
お手数おかけしました。
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();
みたいに、返ってきたデータの行数を取得してやるのだ。
中途半端な日本語を書かれると、こういう勘違いをしてしまう。
仕事で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 ←ダメでした
基本
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.25
docker pull wordpress:latest
docker images
で取り込めたかどうか確認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
で作成できたかどうか確認docker exec -it {コンテナ名} /bin/bash
cat wp-config.php
で定義ファイル内の確認docker stop {コンテナ名}
コンテナ停止docker start {コンテナ名}
コンテナ開始docker rm {コンテナ名}
コンテナ削除docker rmi {イメージ名}
イメージ削除apt update
apt 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行しか出てこない始末となった。
う~ん、なんとかなんないのだろうか。他にうまくできる方法を探さなければ、「関数作成してカーソル利用」しか思いつく方法がない・・・
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()を使うかは子要素だけなのか、関係なく下の階層すべてに適用するのかで決める。
$(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番目の要素だけ取得できる。
今日はここまで。次はソートについてメモせねば。
歩道を歩いていて、前のヤツがタバコ吸うとこっちは受動喫煙になる。
もっとも、周りに気使う喫煙者は、路上や屋外で喫煙はしない。
路上や屋外で喫煙する奴は基本的にアホです。ハイ。ハッキリ断言します。
周りに人がいる、来るかもしれない場所での喫煙はやめましょう。ただの迷惑。
久しぶりの投稿の冒頭がこれかい!って感じですが、いつになったら分かるんでしょうねって思うので書いてみた。
で、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。
という話なのである。
クリップボードに文字列を貼り付けるときに、貼り付かないケースがあったので、
別の方法を調べてみた。
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
結構間が開いてしまった。
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=***”)
ってな感じで変換してやる。