カテゴリー: Development

ExcelVBA覚書 どのボタンを押した?

シート上にある複数のボタンから1つのサブプロシージャを起動するときなんぞに、どのボタンを押したのかわかればよいなぁ~と調べたら、意外と簡単にできるらしかった。

[ A ] [ B ] [ C ]

って3つのボタンがあって、それぞれ名前(ShapeオブジェクトのName)を「A」「B」「C」とつけておく。
(図形 1とか、ボタン 1とかの名前を変えてやるのだ。もちろん、そのままでもいいけど。)

で、これらのボタン全部 にマクロ登録して、AbcButton_Click() を実行させるとすると、

Sub AbcButton_Click () 
    Dim buf As String
    buf = Application.Caller
    Select Case buf
    Case "A"
        Msgbox "Aが押された!"
    Case "B"
        Msgbox "Bを押しましたね"
    Case "C"
        Msgbox "Cなんですか?"
    End Select
End Sub

という感じで、Application.Callerがボタンの名称を教えてくれるので、そこから分岐させる処理を記述すればよい。

ん~、もっと早く知っとけばよかった。
できないだろうという思い込みはよくない。実によくない!

ExcelVBA覚書 リモート実行

リモート実行にはいろいろやり方があるようだが、レジストリを触らなきゃいけないとかいわれると気が引けるものだ。

で、行き着いたのは、CreateObject(“WbemScripting.SWbemLocator”)を利用するこのスクリプト。

[VBScript] 他端末のプログラムをリモートで実行する

これを参照してリモートサーバのバッチファイルを叩くことに成功。

ありがとう!

ExcelVBA覚書 Clipboardに格納

クリップボードに値を設定するときは、「DataObject」を使うらしいが、CreateObjectで利用するときはいかのようにするらしい。

Dim buf As String

buf = "あいうえお"
With CreateObject("new:{1C3B4210-F441-11CE-B9EA-00AA006B1A69}")
    .SetText buf
    .PutInClipboard
End With 

VBA覚書 グループ開閉(行)

マクロの記録中だと、
メニュー「データ」→「グループとアウトラインの設定」→「詳細データの表示」
と、メニュー上で行うと記録できるらしい。

Excel質問掲示板(VBA) シートの外のグループ化の展開はできますか?

ということで、開くとき
ExecuteExcel4Macro “SHOW.DETAIL(1,” & CStr(グループの開始行) & “,True)”

閉じるとき
ExecuteExcel4Macro “SHOW.DETAIL(1,” & CStr(グループの開始行) & “,False)”

VBA覚書 保存させずに終了させる方法

保存ボタンを押されたときに保存させないのは、WorkbookのBeforeSaveで「Cancel = True」の1文を入れたらよい。
では、終了時に保存確認させたくないときはどうするか。
保存したことにして確認ダイアログを出さないようにする手があるようだ。


Private Sub Workbook_BeforeClose(Cancel As Boolean)
    If MsgBoxInfomation("終了確認", "終了してよろしいですか?", vbQuestion) <> vbYes Then
        Cancel = True
    End If
    ThisWorkbook.Saved = True  '保存した!ということにする
End Sub

Webアプリ覚書 HTML5・CSS3・IE対応

Bootstrap3関連

自分はBootstrap2のときに初めて使ったのだけど結構奥深いので、公式ページではよくわからない箇所はリンクを参照。
Bootstrap3移行ガイド
Bootstrap3 徹底解説!

ファビコンを作る

結局一番使いやすいのはX-Icon Editorだった。(ただし、64、32、24、16ビットサイズにしか対応していない。)
他のは使い勝手がものすごく悪かったり、pngに対応してなかったり、下手をするとウィルス付だったり・・・

X-Icon Editor

より見やすい画面に(コントラスト比の算出)

こういうのはWebアプリだけでなく、いろいろ使えそうなので。

Contrast-A

IE対応

もうこの世から消し去りたいIE8とか9。だが、なかなか消えない・・・

IE対策 HTML5+CSS3でサイトを作ってみる

1) HTML5の反映

昔はhtml5.jsを使っていたようだけど、今はhtml5shiv.jsを使うようだ。Googleからとってくるのだ。

<!--[if lt IE 9]>
<script src="/js/html5shiv.js"></script>
<![endif]-->

2) IEの最新バージョンをレンダリング
  1)だけでうまくいかなかった・・・ということでメタ情報も(挿入する位置に注意しなければならないよう)

<meta http-equiv="X-UA-Compatible" content="IE=edge">

3) CSS3の適用

これでグラデーションとか角丸がいけるようになるらしい。
CSS3PIE

あとは旧ブラウザへの対応も見ておこう。

他にもあれば継ぎ足しておこう。

PostgresSQL覚書 関数の戻り値をレコードに

関数の戻り値というと、普通は1つの値なのだけれど、複数行とってきたいとか、テーブルとしてとってきたいとか思う。

問い合わせ言語(SQL)関数

を見ているとできなくないらしいけれど、

CREATE FUNCTION sum_n_product_with_tab (x int)
    RETURNS TABLE(sum int, product int) AS $$
    SELECT $1 + tab.y, $1 * tab.y FROM tab;
$$ LANGUAGE SQL;

で、LANGUAGEが「SQL」となっていて、「plpgsql」で作っているものだとRETURNSの後ろをTABLE(ほにゃほにゃ)に変えただけではうまくいかなかった。
戻り値が
sum | product
—–+——————
50 | aaa
66 | bbb

とかになってほしいんだけど、
sum_n_product_with_tab
———————–
( 50, aaa)
( 66, bbb)

になってしまって、せっかくTABLEにしている意味ないんじゃない?ということになった。

ふむ。どうすればよいのじゃ・・・

ということで、せめて複数行とってくることにしよう。

CREATE OR REPLACE FUNCTION hoge(parm_cd character varying)
  RETURNS numeric  AS
$BODY$
DECLARE
    ・・・・
BEGIN

  CREATE TEMP TABLE wk_master(code numeric(2,0))
  ・・・・
  RETURN QUERY SELECT code FROM wk_master;
  DROP TABLE wk_master;

END;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100
  ROWS 1000;

RETURN QUERY・・・とすると、複数行で返してくれる。

今日はこの辺で終了。

PostgreSQL覚書 Backup&Restore

またあっという間に時が経った。
師走だ。

WindowsバッチでAサーバにあるDBをBサーバに移すことにした。

んで、とりあえずいろいろ参考にして作ったのが下の例。

まずはパスワードを設定しておいて、途中で聞かれないようにしてから・・・
pg_dump を使って D:\backup\test.backupに 111.111.111.111:5432 のA1_DBをバックアップ。
(ユーザはpostgres)
バックアップファイルからpg_restoreを使って、111.111.111.123:5432 のA2_DBへリストア。
(こっちもユーザはpostgres)

—————————————————————–
SET PGPASSWORD=password
“C:\Program Files\PostgreSQL\9.3\bin\pg_dump.exe” -f D:\backup\test.backup -i -v -h 111.111.111.111 -p 5432 -U “postgres” -Fc “A1_DB”
if not %ERRORLEVEL%==0 stop

“C:\Program Files\PostgreSQL\9.3\bin\pg_restore.exe” -c -h 111.111.111.123 -p 5432 -U “postgres” -Fc -d “A2_DB” D:\backup\test.backup

stop
—————————————————————–
オプション関連はドキュメントを見てもらえばいいと思うけど、書き順やらがドキュメントではいまいちわからんかったのでメモっとく。

SQLServer覚書 ストアド内で別のストアドを実行

ストアドプロシージャ内で、別のストアドプロシージャを呼び出す方法。

例えば、proc_GetEmployeeIn(引数:@bumon_cd) を呼び出して結果を取得する場合だと、
以下のように、あらかじめワークテーブルを作っておいてから、
INSERT INTO ワークテーブル EXEC プロシージャ名 引数1, 引数2 , …
というようになる。

CREATE TABLE #work_table (
      emp_cd VARCHAR(1000)
    , emp_name VARCHAR(10)
)
INSERT INTO #work_table EXEC proc_GetEmployeeIn @bumon_cd

PostgreSQL覚書 IsNumericとかIsNumberの代わり

いつのまにか10月になって、いつのまにかWordPressがVer4.0になってた。
時の経つのは早いもの。

PostgreSQLにはIsNumericとかIsNumberみたいな関数はないのねぇ・・・
Function作ればいいんだよ!って、言われてもねぇ・・・

ということで、代替案を漁ってみた。

SELECT (CASE WHEN emp_cd ~'^[0-9]*$' THEN emp_cd ELSE NULL END) emp_cd FROM master_table

ってな感じで、正規表現を使うらしい。
関数作って使ったほうが楽じゃね?って言わないでね・・・

PostgreSQL覚書 接続中を調べる

昔バグった話。
接続しっぱなしでロジックを終わらせてたら、どんどん接続数が増えて処理できなくなった。
CLOSEすんのを忘れてたんです。スイマセン。

で、大騒ぎになる前に。

SELECT * FROM pg_stat_activity
 ORDER BY usename,application_name;

で、ちゃんとクローズしたことを確認する。
application_nameがpgAdmin以外で、IPアドレスを確認して、current_queryがアイドル状態になっておらず、SQL文が設定されたままだとクローズしていない。
処理が終わってもこういうのが残っていると、ちゃんと切れてないので切断すること。

このバグのせいで、運用問題にまで発展しテンヤワンヤとなったので、メモ。

ExcelVBA覚書 誰かが使ってる・・・

Excelファイルを開く。
だけど、誰かが開いているから保存はできないよ。
だから処理をやめるね。

ってなことがやりたい!ということで調べてみた。
なんか、Open xxx For Append とか Open xxx For Binary とかで誰かがつかんでることを確認する方法とかあったのだけど、ファイルがちょっと特殊なところにあり、こういうことができない。
それに、パスワード設定しているため、誰かが使っているときにReadOnlyをFalseにして開くと、パスワードまで聞いてくる始末・・・
なので、脳みその10%をフル活用してみた。

'
' 引数で指定されたパスを開いて、開いたブックを返す
' (ここではパスワード設定は省略)
Private Function OpenBook(filePath As String, readonlyFlg as Boolean) As Workbook

    Dim fileName As String

    On Error Goto ErrorFunc

    '(1) とにかく読取専用で開く(確認メッセージとか出さない)
    Set OpenBook=  Application.Workbooks.Open(Filename:=filePath , UpdateLinks:=False _
                                              , Notify:=False, ReadOnly:=True)
    '(2) 読取専用で開くときはここで終わり
    If readonlyFlg Then Exit Function

    '(3) 開いたブックを読み書きできるようにする(確認メッセージとか出さない)
    fileName = OpenBook.Name
    OpenBook.ChangeFileAccess Mode:=xlReadWrite, Notify:=False

    '(4) なぜか、開き直しになり設定が切れてしまうので、もう一度設定しなおす
    Set OpenBook = Workbooks(fileName)

    '(5)ファイルが読取専用でなかったら、編集可能なブック!
    If Not OpenBook.ReadOnly Then Exit Funtion

    '(6) OpenBook.ReadOnly = True なら、誰かが掴んでる!=> 閉じる
    OpenBook.Close SaveChanges:=False
    Set OpenBook = Nothing
    Exit Function

ErrorFunc:

    '指定したパスのファイルがないときは、(1)でエラー発生し、ここに来る!

End Function 

ということで、回りくどいやり方をすれば何とかできることが分かった。
上の例だと、ファイルがないときも、開けないときもNothingで返してしまうので、区別したいときはフラグを返してやるとか工夫が必要。

ブックを開いた後は

    OpenBook.Windows(1).WindowState = xlMinimized

で最小化しておくと、画面のちらつきは最小限で済む。

で、これが「特殊な環境下」でうまく動くかどうかはまだ試せていない。

ExcelVBA覚書 AutoFilterまとめ

あぁ、フィルタよ、フィルタよ・・・
結構ややこしかったぞよ。

1つの列に対する複数条件の設定とか、フィルタ解除の方法やら、マクロの記録で出力されないロジックを調べるのに時間がかかった。

1) フィルタの解除

' フィルタが設定されていたら解除しちゃうよ
Private Sub ClearAutoFilter(target_ws as Worksheet)
    If target_ws.AutoFilterMode Then target_ws.AutoFilterMode = False
End Sub

2) 1つの列に対する複数条件設定

これが結構難しかった。というより、参考になるサイトがあまりなく、やっとこさで探し当てた。
別のところで配列(Variant型)を作っとく。
たとえば、
Dim arr As Varient
arr = Split(“あ,い,う”)
として、
SetAutoFilterAt Activesheet.Range(“A1:E100”), 1, arr
ってな感じで下のプロシージャを呼び出す。
すると、「あ」「い」「う」に該当するレコードが抽出される。

Private Sub SetAutoFilterOn(target_rng as Range, col as Long, crit_arr as Variant)
    target_rng.AutoFilter Field:=col , Criteria1:=crit_arr , Operator:=xlFilterValues
End Sub

試してないから、動かんかったらごめんなさい。
でも、イメージ的にはこんな感じで、Criteria1とOperator の設定がポイントということが分かった。

3) 絞り込んだデータの取得(A) 1行ずつ漁る

    Dim cnt As Long
    Dim rng As Range
    Dim target_rng As Range

    Set target_rng = Activesheet.Range("A1:E100")

    'target_rngの1行目がタイトル行なので、-1 する。
    With target_rng
        cnt = .Columns(1).SpecialCells(xlCellTypeVisible).Cells.Count -1
        If cnt > 0 Then
            For Each rng In .Columns(1).SpecialCells(xlCellTypeVisible).Cells
                'タイトル行以外で処理する
                If rng.Row > 1 Then
                    '抽出範囲の1列目の情報をダイアログ表示
                    Msgbox rng.Parent.Cells(rng.row,1).Value
                End If
            Next
        Else
            Msgbox "データなし"
        End If
    End With

ForEach文が1行ずつ見ているところ。
抽出範囲の1列目ってのが、Columns(1).SpecialCells(xlCellTypeVisible).Cells。

4) 絞り込んだデータの取得(B) ワークシート関数でドバッと集計

    Dim cnt As Long
    Dim target_rng As Range
    
    Set target_rng = Activesheet.Range("A1:E100")

    'target_rngの1行目がタイトル行なので、-1 する。
    With target_rng
        cnt = .Columns(1).SpecialCells(xlCellTypeVisible).Cells.Count -1
        If cnt > 0 Then
             '抽出範囲の2列目の合計を取得(タイトル行は数値でないこと)
             Msgbox  "合計:" & Cstr(Application.WorksheetFunction.Sum(.Columns(2).SpecialCells(xlCellTypeVisible).Cells))
        Else
            Msgbox "データなし"
        End If
    End With

他の内容は割と簡単に調べられるので省略。

改めてExcelVBAって奥深いなぁ・・・って思った。
ってか、マクロの記録でこういうロジックを残してくれたら、悩まずに済むのに!といつも思う。

ExcelVBA覚書 マクロの記述を削除する

Excelファイルを作成するときに、マクロの入ったシートをコピーして作成すると、マクロが残っちゃって困ってしまった。
おまけに、一部のシートしかコピーしてないから、モジュールとかにあるプロシージャとかがないってエラーになってしまう・・・

で、マクロは保存する前に削除しちゃいましょう!なロジックを見つけてきた。

元ネタはこちら
Excelでお仕事! マクロを除いた配布用ブックを作成する。

'======================================================
'ブック上にあるマクロを全部削除しちゃうよロジック
'======================================================
Public Sub DeleteMacroIn(wb As Workbook)
    
    Dim obj As Object    '正しい型はVBComponent
    Dim lines As Long 
    
    For Each obj In wb.VBProject.VBComponents
        With obj.CodeModule
            lines = .CountOfLines
            If lines > 0 Then .DeleteLines 1, lines 
        End With
    Next obj

End Sub

Excel覚書 Excelを複数バージョン入れているとき

ファイルをWクリックしたときにどっちのバージョンのEXCELを開いてほしいか指定する方法。

コマンドプロンプトで以下を実行

自分の環境はExcel2013(32bit)なので、

“C:\Program Files (x86)\Microsoft Office\OFFICE15\excel.e
xe” /regserver

ファイルパスは32bitと64bitでは違うし、パスを指定してインストールしたらこれとは違うので、自分の環境にあわせて変える。

Excel2003はOFFICE11、2010はOFFICE14、2013はOFFICE15。

SQLServer覚書 縦→横 (FOR XML)

SQLServerで縦並びになっているデータを横並び(カンマ区切り)にして出力したくなった。

【TABLE1】
CD | KOMOKU
1 | A
1 | B
2 | C
2 | D
【TABLE2】
CD | CNAME
1 | あ
2 | い

ってあった場合、

CD | CNAME | KOMOKU
1 | あ | A,B
2 | い | C,D

って出したい!ってことで、調べた結果、

select 
 TABLE2.*
 ,replace((select KOMOKU  AS [data()] 
 from TABLE1
 where (TABLE1.CD=TABLE2.CD)
 for xml path('')
 ),' ',',') AS KOMOKU
 from TABLE2

ってな感じに記述するとのこと。
KOMOKUにスペースが含まれているとこれってうまくいかないんだよねぇ・・・とか思いつつ、自分の場合は特に問題なかったのでOK。
[data()] ってのがアトミック値にするっていうことらしいがよくわからんかった。
(分解できないとか、処理速度が速いとか書いてあったけど、結局よくわからん・・・)