カテゴリー: Development

ExcelVBA覚書 Shellとかリモート実行とか

忘れる前にメモっとく。
VBAからコマンド実行を非同期で行う時に使うShell関数。
第2引数にvbMinimizedNoFocusを設定すると、コマンドプロンプトは非表示になって、裏側で走る仕組みにできるそうな。

Dim cmd_buf As String
Dim res     As Double

cmd_buf = "cmd /c sqlcmd -S {DB-Source} -U {DB-UserId} -P {DB-UserPw} -d {DB-Name} " _
         & " -Q ""EXEC {プロシージャ名} {*}, '{*}' """ 
res = Shell(cmd_buf, vbMinimizedNoFocus)

{}書きは環境に合わせて変更
{*}はストアドの引数で、文字列の場合はシングルクォーテーションで囲む。

Shellの戻り値はタスクIDで、「0」がかえってきたら動いていないということらしい。
非同期なので勝手に動いて、勝手に終わる。

終わったらウィンドウをアクティブにするとかすると、終わったことがわかるらしい。

If res > 0 Then Call AppActivate(res)

と、こんな感じかね。
ふむふむ、しかしだね、裏側で勝手に動いているわけで、下手にPCシャットダウンしちゃうと、処理が途中で終わっちゃう!ってところが怖い。

で、次。
リモート環境にあるバッチファイルを実行する方法。

'server:リモートサーバ(user_id/user_pwでログイン)
'exe_name:実行ファイル, exe_position:実行ファイルのパス
Public Function ExecuteRemoteExe(server As String, user_id As String, user_pw As String _
                                , exe_name As String, exe_position As String) As Boolean

    Const AUTHENTICATION_LEVEL_PKT_PRIVACY = 6
    
    Dim obj_locator As Object
    Dim obj_server  As Object
    Dim obj_process As Object
    Dim res As Long
    Dim pid As Long
    
    On Error GoTo ErrExecute
    
    Set obj_locator = CreateObject("WbemScripting.SWbemLocator")
    Set obj_server = obj_locator.ConnectServer(server, "root\cimv2", user_id, user_pw)
    obj_server.Security_.authenticationLevel = AUTHENTICATION_LEVEL_PKT_PRIVACY
    
    Set obj_process = server.get("Win32_Process")
    res = obj_process.Create(exe_position, Null, Null, pid)

    Select Case res
        Case 0
            ExecuteRemoteExe = True
        Case 2
            Msgbox "アクセスできませんでした。"
        Case 9
            Msgbox "パスが正しくありません。" 
        Case 21
            Msgbox "パラメータが正しくありません。"
        Case Else
            Msgbox "バッチを実行できませんでした。"  
    End Select
        
ErrExecute:
    
    Set obj_process = Nothing
    Set obj_server = Nothing
    Set obj_locator = Nothing
    
End Function

これでリモートのバッチファイルを実行することができる。
できるんだけど、これもこっち側のPCを切ってしまうと、バッチも終わってしまう・・・
なんとかならんか。

ExcelVBA覚書 Resize

モノを整理するのにモノを買う・・・
という、断捨離しているはずなのにモノを増やす、愚かしいことをやってしまった。

いや、何にせよ、今のままじゃイカン!と、虚無感たっぷりに自分に活を入れつつ。

ExcelのRangeオブジェクトのメソッドにあるResize。起点セルと行列数を指定して範囲指定するのに使っているようだ。
こんなメソッドがあるとは全く知らなかったが、ネットサーフィンしてたらたまたま見つけたので試してみる。

参照サイト:Range,Cells」と「Resize」のセル範囲指定を比べてみる

パターン1:ごくごく一般的な範囲指定

With ThisWorkbook.Worksheets(1)
    .Range("C2:E4").Select
End With

パターン2:一般的なはずな範囲指定

With ThisWorkbook.Worksheets(1)
    .Range(.Cells(2, 3), .Cells(4, 5)).Select
End With

パターン3:「Resize使って同じことしてみるよ」な範囲指定

With ThisWorkbook.Worksheets(1)
    .Cells(2,3).Resize(3,3).Select
End With

パターン4:「Offset使って同じことしてみるよ」な範囲指定

With ThisWorkbook.Worksheets(1)
    .Range(.Cells(2,3),.Cells(2,3).Offset(3,3)).Select
End With

パターン1から4まで、すべて同じ範囲が指定される。(つまり、セルC2~E4)
なるほど、少なくともパターン4よりは、パターン3の方がきれいだ。
パターン2と3、どちらを使うかは、周辺のロジック次第。

なお、参照サイトには「Rangeが参照しているシートがアクティブになっていないとエラーになる」と書いてあるが、実はどのメソッドでも同じ。Selectメソッド自体、アクティブシート上にでないと動かないので、ResizeやOffsetとは関係がないと思う。

PostgreSQL覚書 DBLINK設定方法

昔やったのに忘れてしもーた・・・
と、Webで調べたDBLINK。

「CREATE EXTENSION DBLINK;とやったら使えるよ!」みたいなサイトがあったからやってみたら、postgreさんに「なんじゃい!」って怒られたので、過去のドキュメントを引っ張り出してきた。

「そもそも、モジュールあらへんやん、あんた」みたいな話なので、
コマンドプロンプトを立ち上げて、

cd /d C:\Program Files\PostgreSQL\9.0
bin\psql -U {USER_ID} -d {DBNAME} < share\contrib\dblink.sql でPWを入れてDBLINKを使えるようにすると、関数としてDBLINKが使えるようになった。

PostgreSQL覚書 年間日別レコードを作る

1年分の日付データを作りたい。もちろん一括で。
できないものかと探すと、あった。

SELECT arr.i AS date1
 FROM
 generate_series(
   cast('2016-1-1' as timestamp)
 , cast('2016-1-1' as timestamp) +  '12 months' + '-1days'
 , '1days'
 ) as arr(i)

Series Generating Functions(集合を返す関数)・・・

よくわかりヘンが、generate_seriesの1つめの引数をスタートとして、2つ目の引数まで1データずつレコードを作成してくれる便利なもの。
1,2の引数がINT型であれば、3つ目の引数はなく、+1しながらレコードが作られる。
1,2の引数がTIMESTAMP型であれば、3つ目の引数にインターバル設定をして、インターバル設定分プラスしながらレコードが作られる。(上の例だと1日ずつ)

参考URL:Postgresqlでカレンダーを使わずに日付を列挙する方法(generate_series)

ExcelVBA覚書 シート保護

シートを保護する、解除するというのは、

Worksheets(“Sheet1”).Protect
Worksheets(“Sheet1”).Unprotect

という、メンバー(サブプロシージャ)でできるのだが、Protectするときの引数に、「UserInterfaceOnly」というのがあり、これをTrueにしておけば、シート保護の解除をしなくても、マクロでシート上のデータを変えられるそうな。

Worksheets("Sheet1").Protect UserInterfaceOnly:=True

なので、いままでわざわざシート保護を解除して処理をしていた自分が情けなくなった。
(何年VBA使ってんだよ!と自分を責めてみる。)

まだまだ知らないことがあるなぁ。

この引数を指定しない場合は、ちゃんと保護解除をしてから触ること。

ExcelVBA覚書 IsMissing関数

Optionalで指定した引数は省略可能だが、設定されて呼び出されているかどうかを確認する関数がIsMissingだそう。
しかしながら、この変数の型がVarient型のときにしか機能しないというお粗末もの。

Private Sub Test (Optional a As Varient)
    If Not IsMissing(a) Then
        '設定されているとき
    Else 
        '設定されていないときのロジック
    End If
End Sub

下のようにLong型にすると、a=0と判断されてしまうので×。

Private Sub Test (Optional a As Long)
    If Not IsMissing(a) Then
        '全部こちらに入ってしまう
    Else 
        'こっちに来ない
    End If
End Sub

ということで、省略時の初期値を設定しておき、条件分岐をする。

Private Sub Test (Optional a As Long = -1)
    If a>0 Then
        '省略されていないとき
    Else 
        '省略されたとき
    End If
End Sub

ExcelVBA覚書 ハイパーリンク

指定したURLを開く。(イベント用)

' url : リンク先
Private Sub GotoLink(url As String)
    On Error Resume Next
    ThisWorkbook.FollowHyperlink Address:=url
End Sub

セルに指定したURLのハイパーリンクを設定する。

' rng  : 設定セル , description : 表示文言, url : リンク先
Private Sub SetHyperlink(rng As Range, description As String, url As String)
    On Error Resume Next
    rng.Hyperlinks.Add Anchor:=rng , Address:="", SubAddress:=url, TextToDisplay:=.description
End Sub

ハイパーリンクを削除する。

' rng  : 設定セル 
Private Sub DeleteHyperlink(rng As Range)
    On Error Resume Next
    rng.Hyperlinks.Delete
End Sub

Java覚書 月加算・月末

月末を出すときは、加算前の日を1日にしておいて、加算後-1日するのが自分の常套手段。
(どの言語でも大体このロジックは使えるし)

import java.util.Calendar;

public class Main {
    public static void main(String[] args) throws Exception {

        // 指定日からnヶ月後の月末を出力
        int y = 2012;
        int m = 12;
        int d = 14;
        int n = 2;
        
        Calendar cal = Calendar.getInstance();

        cal .set(y,m - 1, d);
        dispDate(calendar);
    
        cal .set(y,m - 1, 1);
        cal .add(Calendar.MONTH, n + 1);
        cal .add(Calendar.DATE, -1);
        dispDate(cal);
    }

  private static void dispDate(Calendar cal ){
    int year = cal .get(Calendar.YEAR);
    int month = cal .get(Calendar.MONTH) + 1;
    int day = cal .get(Calendar.DATE);

    StringBuffer sb = new StringBuffer();
    sb.append(year + "年" + month + "月" + day + "日");
    
    System.out.println(new String(sb));

    }
}

Windows IE8ダウンロード元

念のためにメモ。

Microsoft Internet Explorer 8 をインストールする方法

Windows XP
http://download.microsoft.com/download/0/5/7/05716044-2806-40DA-8332-D3ED79BC8F68/IE8-WindowsXP-x86-JPN.exe
Windows Vista
http://download.microsoft.com/download/A/1/B/A1B7D65F-9473-4EB6-A845-68FC5CE4ADDA/IE8-WindowsVista-x86-JPN.exe
Windows Vista (64 bit 版)
http://download.microsoft.com/download/2/2/F/22F4B780-44AF-4265-A3AD-F77865BDD40B/IE8-WindowsVista-x64-JPN.exe

PostgreSQL覚書 主キーの設定・解除

主キーの設定と解除のSQL。

ALTER TABLE {TABLE} ADD CONSTRAINT {KEY} PRIMARY KEY ({COLUMNS});
ALTER TABLE {TABLE} DROP CONSTRAINT {KEY};

{TABLE}:テーブル名
{KEY}:主キー名
{COLUMNS}:列名(複数の場合はカンマで区切って指定)

PostgreSQL覚書 動的な日付計算

これは一般的な1か月後

SELECT current_timestamp + interval  '1months'

動的に算出する場合の例

SELECT current_timestamp + CAST(a.add_months || 'months' AS interval)
FROM table_a as a

CASTを簡単に書くと

SELECT current_timestamp + (a.add_months || 'months')::interval
FROM table_a as a

という感じ。

3Dデータを作ってみる

会社で3Dプリンター(約10万)を買った。
で、3Dプリンターの試し打ちの最中なのだが、結構やれることが限られているように思われる。
(安もんだからかな)

まず、設定値の調整が半端なく大変。(安もんだからかな)
1色使いだから、結局塗装ツールがいる。
んで、造形物をきれいにするためのやすりも必要。

ってなことで、いろいろ苦労は絶えないのだが、まずはデータを作らねば。

ってことでAutodeskのお世話になる。
http://www.123dapp.com/

Autodeskの製品を使うのは、たしかコンピュータ学校でCADの授業で使って以来ぐらいなので、かれころ10年、15年ぶりか。
しかし、無料アプリがいっぱいあって、うれしい限り。

はじめは、「Tinkercad」っていうWebブラウザ上で動くアプリを使っていたのだけれど、図形の組み合わせでしか作れないようなので、「Autodesk 123D Design」というアプリを使って、ちょこまか作ってみる。

大変だけど、意外に楽しい。
もう、進捗しづらくなったオンラインゲームなんかそっちのけで、やっていたのだが、
ふとした拍子にアプリが強制終了、とか、
保存してもMyProjectsに保存されていません。とか、
まぁ、いろいろあった。

で、何か欲しいカバーとかないかなぁ・・・と考えてたら、あったよ!
MP3プレイヤーのカバー。
早速、3Dデータをエンヤコラ、どっこいせ!と作ってみたので、プリンターで早く出してみたいところ。

Excel覚書 dqyファイルの記述

dqyファイルは便利だ。
わざわざ、PhpAdminやらManagerやら開けずにSQL文を実行して、データが確認できる。

自分がよく使うPostgreSQLとSQLServerのクエリファイルの書き方メモ。

PostgreSQL

XLODBC
1
Driver=PostgreSQL Unicode;Server={IPアドレス等};Port={PortNo};Database={DB名};Uid={UserID};Pwd={Password};
SELECT TOP 100 * FROM batch_log ORDER BY create_date desc

SQLServer

XLODBC
1
Driver=SQL Server;Server={IPアドレス等};Uid={UserID};Pwd={Password};Database={DB名};Connect Timeout=15;
SELECT * FROM batch_log ORDER BY create_date desc LIMIT 1;

SELECT文は改行すると動作しないときがあるので注意

Ruby覚書 Install

さてさて、redmineの勉強でもしてみるか・・・と思ったら、Ruby On Railsが必要とのこと。
早速入れてみる。
作業手順を簡単に説明してくれるサイトが見つからず、途中嫌になったが、探し方がまずかっただけだった。
手順は、
1) RubyInstallerでRubyをインストール
2) DevKitsを入れる
  DevKitのinitとinstallでbundlerが入る
3) gemでrailsをインストール

なんだ、簡単だったんだ。

1)では環境変数PATHにインストール先のbinが設定されること
2)は、コマンドプロンプトで、DevKitsファイルの展開先に「cd /d」して、
「ruby dk.rb init」
「ruby dk.rb install」
インストールしてbundlerが入ったら、
3)そのままコマンドプロンプトで、「gem install rails –no-ri –no-rdoc」を実行

とりあえず今日はここまで。

SQLServer覚書 カンマ区切り文字列→行データ

カンマ区切りのデータを縦に並べるプロシージャを作ってみた。


CREATE PROCEDURE csvToRows
    @csv VARCHAR(MAX)
AS
BEGIN

    DECLARE @exit_flg       TINYINT       = 0
    DECLARE @i              INT           = 1
    DECLARE @imax           INT
    DECLARE @buf            VARCHAR(MAX)
        
    BEGIN TRY
        -- はじめにNULLや空白を回避
        IF RTRIM(LTRIM(ISNULL(@csv,'')))='' 
        BEGIN
            RAISERROR (N'NULLはダメ!', 18, 1)
        END

        CREATE TABLE #w_temp (
            buf            VARCHAR(MAX)
        )
        
        SET @buf = @csv
        WHILE @exit_flg = 0
        BEGIN
            SET @buf = SUBSTRING(@buf, @i  , LEN(@buf))
            SET @imax = CHARINDEX(',', @buf)
            IF @imax = 0
            BEGIN
                INSERT INTO #w_temp
                SELECT SUBSTRING(@buf, 1, LEN(@buf)) 
                SET @exit_flg = 1
            END
            ELSE
            BEGIN
                INSERT INTO #w_temp
                SELECT SUBSTRING(@buf, 1, @imax - 1) 
                SET @i = @imax + 1
            END
        END
        SELECT 'OK' AS result, NULL AS err_msg, buf FROM #w_temp

    END TRY
    BEGIN CATCH
        
        SELECT 'NG' AS result, ERROR_MESSAGE() AS err_msg
        
    END CATCH
END

jQuery覚書 Datetimepicker

日付と時刻両方設定できるJavaScriptのカレンダーを探していたところ

DateTimePicker

なるものを発見。jQueryのPlug-inとのこと。

早速ダウンロードして、サンプルを参考に設定していく。
いとも簡単に、日付と時刻が設定できた。分間隔もOptionで設定できる。

で、これって日付設定もできるんだよねぇ・・・(要は時刻設定は要らない!の場合)
って「timpicker:false」にするとできたことはできたんだけど、日付をクリックしてもカレンダーが消えてくれないのね。

困ったわぁ~と昼下がりに物思いにふける主婦の体(テイ)で、細かく説明を読んでみる。
すると、日付を選択したときのイベントみたいなものがあるではないか!

よし!
「onSelectDate」で、カレンダーを閉じる処理を追加しちゃえ!

ということで、以下のような設定で、うまいこと日付選択時もカレンダーが消えてくれましたとさ。

// HTML側は <input type="textbox" id="kaishibi" value /> 
$('#kaishibi').datetimepicker({
	dayOfWeekStart : 0 ,
	lang:'ja' ,
	value:'' ,
	format:'Y/m/d',
	formatDate:'Y/m/d',
	timepicker: false ,
	onSelectDate:function(ct,$i){
		$('#kaishibi').datetimepicker('hide')
	},
});

SQLServer覚書 RAISERROR

「SQLSeverの2014ぐらいでないとTHROWが使えない」ってことで、RAISERRORでエラーを発生させてCATCH句に強制的に移動するように。

したはずなんだけど、いかないのは何故?

って、原因は重要度に設定する値にあった。

重要度=1~10の場合、CATCH句へ飛ばない。

RAISERROR (N'エラーメッセージ', 10, 1);

重要度=11~18の場合、CATCH句へ飛び、18までなら普通のユーザでも指定できる。

RAISERROR (N'エラーメッセージ', 18, 1);

重要度=19~の場合、sysadminとか権限のあるユーザでないと、そもそも設定できない。

RAISERROR (N'エラーメッセージ', 19, 1);

ということで、11~18の値を設定することで、無事CATCH句に飛びましたとさ!