2017年10月12日木曜日

Access VBA でイベントに関数を登録

MicroSoft Access の話題です。

フォームに大量のボタンを配置した場合など、それぞれのボタンのクリックイベントを書いていると結構邪魔くさい。

例えば、月単位のカレンダーを表示して、特定の日付をクリック(もしくはダブルクリック)したら、その日の詳細を表示するフォームがポップアップするなどのプロシージャを書きたい場合など。
実際には押された日付を取得できれば、あとは同じ処理になるので、プロシージャ自体は非常に簡単だけど、
普通にかいたら、最低でも31個のClickイベントを書かなければならない。
これは非常に邪魔くさい。

そこで、すべてのコントロールを選択(SHIFTを押しながら選択)して、「クリック時」イベントに「=ユーザー関数」を書けば、一発で登録できる。

コントロールの名前を「日付」にしておけば、
「Screen.ActiveControl.Name」でそのコントロールの名前を取得できるので、
押された日付が取得できる。

実際には
--------------------------------------------------------------
Function UF_PCall()
    Call R_Main
End Function

sub R_Main()
    V_条件 = Screen.ActiveControl.Name
    Docmd.OpenForm "F_PopUp",,,日付 = " & V_条件
End Sub
--------------------------------------------------------------

こんな感じのプロシージャを書いておいて、
フォームの日付フィールドを複数選択して、
そのクリック(もしくはダブルクリック)イベントに、
「=UF_PCall()」
と書けば、一発登録完成です。

今更ながら、イベントプロシージャに関数を書くというのは、目から鱗でした。

応用編として、コントロールの名前が取得できるなら、
    me.Control(取得した名前).Value
などとすれば、値を取得することも可能になるし、
    set obj_CTRL =  me.Control(取得した名前)
とすれば、あとは、
    obj_CTRL.Value
でも、
    obj_CTRL.BackColor
でも、そのコントロールのプロパティーを簡単に操作できますね。











2017年9月4日月曜日

Life Card の改悪

前回 Life Card のポイントで Amazonギフト券と交換すると、ちょっとお得になると書いたら、ちょうどそのすぐあとに、Life Card が改悪を行ったようで、今はお得になりません。
今は一律1000ポイントで5000円分のギフト券に交換できる1種類だけになりました。

Life Card は高還元率で有名なクレジットカードだったのですが、続々と改悪があって、一番の売り文句だった、「誕生日月は5倍」というのも、3倍に減りましたし、
高速道路のETC使用やEdyのチャージももポイント対象外になってしまいました。

年間150万円以上の使用で、ポイント2倍も無くなり、
年間200万円以上の使用で、ポイント2倍になりました。
ただし、年間100万円以上の使用で、ポイント1.8倍というのが追加になりました。

でも、総合的に魅力半減です。
特に私の場合、高速道路をよく使うので、ポイントが付かないのは響きます。

早急に新しいカードを検討しなければなりません。


2017年6月2日金曜日

LifeカードのポイントをAmazonギフト券に交換

普段クレジットカードは Life Card をメインカードにしています。
公共料金などもこれで支払ってるので、ポイントが結構貯まります。

今回、その貯まったポイントを Amazonギフト券に交換しました。

交換レートは、LifeCard 3,600ポイントで Amazonギフト券20,000円を選択。
現金系に交換するより10%お得ですね。

金曜日の21時ごろにLifeCardのWEBから交換手続きを開始。
生年月日やカードの暗証番号などの入力が必要。
でも、いたって簡単。

入力完了後、送信ボタンを押して、待つこと約5分で、
Amazonギフト券番号が記載されたメールが送られてきた。

今度は、AmazonのWEBにログインして、送られてきた番号を入力すると、
その瞬間にチャージされました。
こちらもいたって簡単。

これなら、買いたいものが見つかった時に、すぐに交換して使える。

しかもチャージしたギフト券の有効期限は、なんと2027年。
ほぼ10年使える。

予想では半年ぐらいで失効するのかと思って、
交換をためらっていましたが、
こんなに長いならもうちょっと大きな金額で交換したほうが良かったか。

交換レートは累進なので、
 3,600P=20,000円(10%)
 8,800P=50,000円(12%)
15,000P=100,000円(25%)

25%OFFは感動ものですが、こんなにポイント貯めれるのかな?



2017年5月9日火曜日

RemoteFX って使えない

リモートデスクトップはすごく便利に使っています。

Windows7 のノートパソコンから 別室にあるWindows7 のデスクトップパソコンをリモートしたり、Windows 2008 Server r2 サーバー機のリモート管理を行ったりしています。

ところが、今回二要素認証を導入しなければならないことになり、ちょっと問題が発生しました。
全てのパソコンにUSBメモリー風のデバイスを接続して、対応するパスワードを入れないとログインすることができなくなりました。

このUSBメモリー風デバイスは一人1個しかない。
リモートで別のパソコンを操作するということは、2台のパソコンにログインするということ。
ということは、デバイスが2個必要となるので、事実上リモート接続が不可能になってしまいました。

ここで色々彷徨ったら、RemoteFXなるものを導入すれば、操作する側(自分の目の前)のパソコンに接続したUSBデバイス(ローカルデバイス)を、操作される側(別室にある)のデバイスとして認識させることができるという情報をつかんだ。

もともと、DVDドライブやプリンターなどはこちら側に繋いだものを利用できるので、それが拡張されたという感覚ですね。

得られた情報では、Windows7 なら 32bit でも64bitでもOKで、SP1が適用されていることが条件となっている。

これを使えば完璧!ということで、色々試行錯誤をしたのに、どうしても設定できない。

もうちょっと彷徨ったら、なんと RemoteFX は、
Windows7 でも「Enterprise」か「Ultimate」エディションしか使えない!

そんなエディションありえへん!
無駄な時間を費やしてしまった。

しかも、結局リモートデスクトップと二要素認証のジレンマは解消できず。
こりゃ困った。




ClickOnce がとつぜん

ClickOnece で組んであったシステムが突然以下のようなエラーを吐いて動かなくなってしまった。

複数台の端末で同じ実行環境を作ってあった内の数台でダメ。
サーバー自体で起動した場合は普通に起動する。

怪しいのは1か月ぐらい前に、ウィルス対策ソフトを入れ替えたこと。
マカフィーからカペルスキーに。

でも、すべての端末は同じ環境なのに、なぜ?
しかも、ダメな端末も朝一は正常起動していたのが、途中で端末の電源を入れなおした後からダメになったような気がする。

やはりカペルスキーか?
ということで、ダメな端末の内の1台で、カペルスキーをアンインストールして実行してみたけど、たっぱりダメ。

一晩ネットを彷徨ったら、やはりウィルス対策ソフトで ClickOnece がダメになった例が報告されていることを発見。

そこで、カペルスキーのログを確認したところ、ありましたありました。
システムの実行ファイルを隔離してやがった。

まだカペルスキーを削除していない端末は、カペルスキーのインターフェースを使って、隔離ファイルを復元し、実行ファイルのあるフォルダをスキャン対象から除外する設定を施したら、普通に起動するようになった。

カペルスキーをアンインストールしてしまった端末は、隔離されたファイルを復元する方法がないので、別の端末のログを参考に実行ファイルを本来のフォルダにコピーして復元。
ところが、この実行ファイルはカペルスキーが確実にウィルス扱いするので、コピーする際に、大元のClickOnece配信サーバーから一旦中間サーバーを経由してダメ端末にコピーしようとすると、その中間サーバーにカペルスキーが居るので即座に駆除(隔離)してしまうので、手動でコピーしようとした瞬間に駆除されるというのを数回繰り返してしまった。
別のルートを使ってなんとかコピーして、正常起動を確認。

正常起動してた端末でも同じようにカペルスキーが入っていて、同じ辞書ファイルが配信されているのに、なぜ正常起動していたのかは不明。
もしかしたら、辞書ファイルの配信が上手くいってなかったのかも。(それはそれでもっと問題ありかも)

また、配信サーバーには偶然カペルスキーを入れていなかったので、大元の構造は破壊されていなかったのはラッキーでした。

マカフィーの時はそんなことなかったのに、カペルスキーにしてから約1か月でアウトとは、カペルスキーの技術力に問題ありなんじゃないの?

今回起動しなかったのはほんの2~3台だったのと、このシステムを利用している端末が10数台だけだったので軽症で済んだけど、ネットワーク自体は100台以上規模なので、もしもっと重要なファイルをカペルスキーが勝手に駆除しちゃうと、大打撃となる可能性があります。

----------------------------- Error LOG -------------------------------------
PLATFORM VERSION INFO
Windows : 6.1.7601.65536 (Win32NT)
Common Language Runtime : 4.0.30319.18444
System.Deployment.dll : 4.0.30319.18408 built by: FX451RTMGREL
clr.dll : 4.0.30319.18444 built by: FX451RTMGDR
dfdll.dll : 4.0.30319.18408 built by: FX451RTMGREL
dfshim.dll : 4.0.41209.0 (Main.041209-0000)

SOURCES
Deployment url :serverURL
Server : Microsoft-IIS/8.5
X-Powered-By : ASP.NET
Application url :serverURL
Server : Microsoft-IIS/8.5
X-Powered-By : ASP.NET

IDENTITIES
Deployment Identity : appname.Application.app, Version=1.9.0.2, Culture=ニュートラル, PublicKeyToken=4cf906, processorArchitecture=msil
Application Identity : appname.Application.exe, Version=1.9.0.2, Culture=ja, PublicKeyToken=4cf906, processorArchitecture=msil, type=win32

APPLICATION SUMMARY
* Online only application.
* Trust url parameter is set.
ERROR SUMMARY
Below is a summary of the errors, details of these errors are listed later in the log.
* Activation of ServerURL resulted in exception. Following failure messages were detected:
+ Activation failed.
+ アクセスが拒否されました。 (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))

COMPONENT STORE TRANSACTION FAILURE SUMMARY
No transaction error was detected.

WARNINGS
There were no warnings during this operation.

OPERATION PROGRESS STATUS
* [2017/04/18 10:04:40] :

ERROR DETAILS
Following errors were detected during this operation.
* [2017/04/18 10:05:12] System.Deployment.Application.DeploymentException (Activation)
- Activation failed.
- Source: System.Deployment
- Stack trace:
--- Inner Exception ---
System.UnauthorizedAccessException
- アクセスが拒否されました。 (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))
- Source: System.Deployment
- Stack trace:
at System.Deployment.Application.NativeMethods.CorLaunchApplication(UInt32 hostType, String applicationFullName, Int32 manifestPathsCount, String[] manifestPaths, Int32 activationDataCount, String[] activationData, PROCESS_INFORMATION processInformation)
at System.Deployment.Application.ComponentStore.ActivateApplication(DefinitionAppId appId, String activationParameter, Boolean useActivationParameter)

2017年3月18日土曜日

sp_executesql でパラメーターを変更しながらSQL文を実行

SQLServer の話題です。

ストアドで動的に作成したSQL文を実行したい場合に有効な手段です。

Execute sp_executesql @実行したいSQL文,
                                 @SQL文の中で使うパラメーターの型宣言,
                                 @パラメーターそれぞれの内容

という構文になります。

実際には、
-------------------------------------------------------------------
Declare @strSQL nvarchar(1000)
Declare @prm nvarchar(1000)
Declare @A_MAX

Set @strSQL = 'Select @prm_MAX = MAX(ColA) as A_MAX From T_tableA Where ColB = @prm_B'
Set @prm = '@prm_MAX int OUTPUT ,@prm_B nvarchar(200)'

execute sp_executesql @strSQL,@prm,@prm_MAX = @A_MAX OUTPUT,@prm_B = '東京都'

Select @A_MAX as ColAの最大

-------------------------------------------------------------------

主となるSQL文(@strSQL)内で、パラメーターを2個使う例です。

1個は ColAの最大値を求めて、表示するための @prm_MAX、
もう1個は、条件で使用するための @prm_B

@prm は主となるSQL文内で使うため専用の宣言を行うパートです。
executesql 専用 Declare って感じですね。

この例では、@prm_MAX を外に出しますので、「OUTPUT」を付けます。

execitesql の最後のパートは、SQL文内で使うパラメーターの実際の値を書きます。
ここでも、外に出すパラメーターには「OUTPUT」を付けます。
ここで勘違いしやすいのが、赤で塗ってある部分です。
普通の変数に値を代入する感覚では、「@A_MAX = @prm_MAX」となるのですが、ここでは、SQL文内のパラメーターを中心に考えるので、逆に書くことになり、
「@prm_MAX = @A_MAX」で正解です。
これで、@A_MAX に@prm_MAX の内容が代入されます。なんか変な感じですね。

@prm_B は簡単ですね。

最終的に、SQL文内で使ったパラメーターはあくまでも内部専用のパラメーターですから、外部に出してくるための@A_MAX は事前に Declare で宣言が必要です。

当たり前ですが、SQL文内にあるパラメーターの個数と、宣言文内での個数と、実際の値を与えるパートの個数が一致しないとエラーになります。








2017年1月28日土曜日

SQLServer 2008 から Merge が使える

久しぶりに本題のSQLServer です。

今まで知りませんでした。
MERGE文が使えるんですね。

SQL と言えば、言わずもがな「Select」「Insert」「Update」「Delete」の4文が基本です。
例えば、Table_A と、ほぼ同じ構造を持った Table_B があって、
必ずTable_Aが最新になっているとします。

最新になっているというのは、

1、レコードが増えている。
2、レコードが減っている。
3、レコードが更新されている。

とうことが考えられます。

この状態をTable_Bに反映させる。
例えば、夜間バッチでTable_Bを最新状態にする必要がある場合が想定されます。

今までなら、
Table_Bに不足しているレコードを特定して、Insert文を発行。
Table_Aには無いレコードを特定して、Delete文を発行。
Table_AにもTable_Bにもあるが、更新されているレコードに対して、Update文を発行。
と3回の比較処理を走らせる必要があるので、
大量のレコードの場合に、莫大なコストが必要になることがありました。

これを1回の比較で処理してしまうのが、Merge です。
まあ、言葉の意味のままのステートメントです。

私の場合、ORACLEにあるテーブルをそのままSQLServerに毎日夜間バッチでコピーしたいのですが、総レコード数は40万件以上あるのに、更新または追加されるのは、わずか1000件程度。
その1000件を最新に保つために、 Truncate で全消し、Insert で全書きをしていましたが、あまりにも非効率なのと、ディスクにも負担をかけると思います。

そこで考えたのが、リンクサーバーでORACLEに接続して、OpenQuery を発行します。
その際、更新年月日を参照して、前回処理日以降だけに絞る Where句を付けておきます。
OpenQuery なので、条件が入っても、ORACLE側で高速に処理されて返ってきます。
これで1000件程度のレコードが返ってくる。

これを一旦 Select Into ##tempTable で仮テーブルに保管して、SQLServer 側の本来保管したいテーブルと比較して、 MERGE 処理を行うことにしました。

毎日数分かかっていた処理が、数秒で完了するようになりました。

MERGE の構文自体は、他の方がたくさん例を挙げていただいているので、私は省略です。

でも、1行で書けるといっても、結構手間の必要な構文になります。

比較は1回ですが、マッチした場合の動作(通常は UpDate になるかな。)と、アンマッチの場合の動作を(私の場合は、Insert ですが、 場合によっては、 Delete になる。)それぞれ書かないとダメです。
まあ、当たり前と言えば当たり前ですけどね。

特に、 UpDate の場合は、カラムを全部書かないと(しかも、元と先の両方を書かないと)ダメなので、カラム数の多いテーブルだと、長ーい構文になっちゃいます。