2018年11月16日金曜日

レコード単位の計算(ireports)

なんてことない処理のはずがはまったので備忘録

要は、明細のA,B,Cの項目を合算して、Dに出したいというだけ。
手段はこれ以外あると思いますが、デザイナ環境と実行環境で動作が異なったため若干はまりました。
結果を残しておきます。

実装
KINGAKU_A,KINGAKU_B,KINGAKU_Cの計算をVariableで定義
表示フィールドにVariable名を設定


<variable class="java.lang.Double" incrementtype="Column" name="V_ROW_KINGAKU_GOUKEI" resettype="Column">
    <variableexpression>
    &lt;![CDATA[($F{KINGAKU_A}.equals(null)?0:new Double($F{KINGAKU_A}))+
    ($F{KINGAKU_B}.equals(null)?0:new Double($F{KINGAKU_B}))+
    ($F{KINGAKU_C}.equals(null) ? 0 : new Double($F{KINGAKU_C}))]]&gt;
</variableexpression>
</variable>

<textfield isblankwhennull="true" pattern="#,##0">
    <reportelement height="9" printwhengroupchanges="GroupName" width="48" x="695" y="0">
    <box leftpadding="3" rightpadding="3">
    <textelement markup="none" textalignment="Right" verticalalignment="Middle">
        <span fontname="IPA明朝">
    </span></textelement>
    <textfieldexpression class="java.lang.Double">&lt;![CDATA[$V{V_ROW_KINGAKU_GOUKEI}]]&gt;</textfieldexpression>
</box></reportelement></textfield>


2018年11月14日水曜日

Accessにてレコード超過によりデータが作成できなかった件 (未完結)

原因究明できていないため、参考までとしてください。

Accessの2GB制限と実際に投入できるデータサイズに対する実証および考察です。

ことの経緯


社内SE作業で、10,000件×10,000件の組み合わせデータを作成する必要があり、Excelではできず、Accessに頼ることにしました。

Excelにある2つのシート(各10,000件)をAccessへインポート
その後、クエリで2つのテーブルをCROSS JOINで新テーブルへINTO(1億件INSERT)

すると、謎のエラー。

accdbファイルを見ると、2GBを超えていました。Accessは2GBまでしかサポートしていないため、エラーとなったようです。

「ま~データ多いからな~」と思いつつ、今はMySQLへリンクテーブル張ってそこへ流し込んでいる段階なのですが、時間があるのでこの記事を書く事にしました。

2GBって何バイト?


改めて計算したところ、20億バイト。


これがテーブル定義なので、20Byte × 1億 件 でざっくり計算しても超えてしまいますね。

なので、母数を減らすことにしました。

10,000件 × 2,500件

これであれば、500MBに収まる試算です

でも同じエラーが出るよ???

レコードの容量計算を行うための情報収集。
ディスクのブロックサイズによって利用されるディスク領域が異なる。と。NTFSの情報を参照します。

fsutil fsinfo ntfsinfo c:

4096と出ました。つまり、4KB
今回の直接の原因にはならなそうです。

ここまでで一旦まとめ

今回の作業の目的はマトリックスデータを作って利用することであり、Accessを使ってどうこうという事ではなかったため、これ以上の深追いはしないことにしました。

考えられることとしては
1)Access内部で一時領域にディスクを利用していてそれでオーバーする
2)テーブルのインデックスなど、付加情報も容量見積に入れる必要あり
3)SELECT INTO (つまり手抜き)なので、結果作成されるテーブルのフィールド型が想定と違う? →その後データ件数を減らして実証しましたが、想定通り、元のテーブルの型を引き継いでいました

あたりが考えられますが、その詳細については今後必要に応じて調べていこうと思います

緯度経度周りのUtil(プログラム)

最近利用頻度が高くなってきたので備忘録
1.緯度経度間の距離を求める
2.緯度経度の60進数表記から10進数表記へ変換

Excelで作業していたのでvbaです
評価検証用として作成(他より拝借)したので精度は検証できていませんがおおよそいけそうです
2018.12.15追記) 60 to 10変換にて各項目の桁数が少ない場合に計算が合わない件を修正
Excel上で利用する場合、この関数をモジュールに組み込んでから
=LatLng60To10(A1)などとします


Option Explicit

Const GRS80_A = 6378137#
Const GRS80_E2 = 6.69438002301188E-03
Const GRS80_MNUM = 6335439.32708317
 
Const PAI = 3.14159265358979
 
Function deg2rad(deg As Double) As Double
 
    deg2rad = deg * PAI / 180#
 
End Function

'国土地理院サイトにて計算結果を目視確認によりテスト
'https://vldb.gsi.go.jp/sokuchi/surveycalc/surveycalc/bl2stf.html
'***********************************************************************
' [説 明] 2地点間の直線距離を求める関数
' [引 数] lat1:地点1緯度 lng1:地点1経度 lat2:地点2緯度 lng2:地点2経度
' [戻り値] 距離差(m)
'***********************************************************************
Public Function calcHubenyToMeter(lat1 As Double, lng1 As Double, lat2 As Double, lng2 As Double) As Double
 
    Dim my As Double
    Dim dy As Double
    Dim dx As Double
    Dim sin As Double
    Dim w As Double
    Dim m As Double
    Dim n As Double
    Dim dym As Double
    Dim dxncos As Double
     
    my = deg2rad((lat1 + lat2) / 2#)
    dy = deg2rad(lat1 - lat2)
    dx = deg2rad(lng1 - lng2)
     
    sin = Math.sin(my)
    w = Math.Sqr(1# - GRS80_E2 * sin * sin)
    m = GRS80_MNUM / (w * w * w)
    n = GRS80_A / w
     
    dym = dy * m
    dxncos = dx * n * Math.Cos(my)
     
    'メートルで取得するので、キロメートルに変換し小数第一位までにする
    calcHubenyToMeter = Round(Math.Sqr(dym * dym + dxncos * dxncos), 2)
 
End Function

'LatLng60To10("35.39.30.959")
'35.6585997222222
'LatLng60To10("139.44.43.594")
'139.745442777778
'LatLng60To10("035.40.51.100")
'35.6808611111111

'LatLng60To10("35.39.09.959")
'35.6527663888889
'LatLng60To10("35.39.9.959")
'35.6527663888889

Public Function LatLng60To10(ByVal target As Variant) As Double

    Dim arr() As String
    Dim cal As Double
    Dim tmpSec As String
    Dim calsec As Double
    arr = Split(target, ".")

    If IsNull(target) Then
        Exit Function
    End If
    
    If UBound(arr) < 1 Then
        LatLng60To10 = ""
        Exit Function
    End If
    arr(0) = Right("00" & arr(0), 2)
    arr(1) = Right("00" & arr(1), 2)
    arr(2) = Right("00" & arr(2), 2)
    If UBound(arr) > 2 Then
        arr(3) = Right("000" & arr(3), 3)
        tmpSec = arr(2) & arr(3)
    Else
        tmpSec = arr(2) & "000"
    End If
    
    calsec = CDbl(tmpSec) / 1000
    
    cal = CDbl(arr(0)) + (CDbl(arr(1) / 60)) + CDbl(calsec / 3600)
    LatLng60To10 = CStr(cal)

End Function