2023年1月21日土曜日

【社内DX案件紹介】データの期間によってレコードを分割しファイル出力する

 ■社内DXの依頼内容

・2つのインプットファイルがあり、その中の複数シートのデータを、1つのアウトプットファイル、1シートに集約しXLSファイルとして出力したい

・処理漏れや誤処理厳禁のため、処理後に処理漏れ確認および誤処理検知できるような仕様としたい

A社B社2つのファイルを集約し、出力する


■社内DXの条件

1.2つのインプットファイル(A社ファイル,B社ファイル)は、2シート目から順に転記

2.インプットファイル、“対象期間(前)”と“対象期間(後)”の月数が、12か月以内のものは、そのまま転記し、13か月以上のものは"1月~12月”の表示形式で最大12か月の期間で行を分ける。(1行の内、期間は年を跨がない)

(例)「対象期間(前):2021年5月」「対象期間(後):2023年3月」の場合

対象期間(前)対象期間(後)
1行目20215202112
2行目20221202212
3行目2023120233

3.ファイルパス、ファイル数、ファイル名は固定(拡張子は.xlsx or .xls)。

4.シート数、シート名は変動有り(データ記載は2シート目~は固定)


■諸元

・インプットファイルを開く

・2シート目からの情報を取得する

・対象期間(前)と対象期間(後)の期間が12か月以内かどうか判断し、転記する
 なお13か月以上の場合は年単位で分割し転記する

・転記は検証用シート「処理結果」とアウトプット用シート「output」にそれぞれ転記することとし、「処理結果」で処理漏れがないか確認できるようにする

・アウトプット用シート「output」を新規ブックにコピーし.xls形式にて保存する


■作成方法

1.2シート目からデータのある2つのファイルを準備する。

  なおデータの配置は以下とします。
  ・A~D列:番号
  ・E列   :氏名
  ・F列   :対象期間(前)の年
  ・G列   :対象期間(前)の月
  ・H列   :対象期間(後)の年
  ・I列    :対象期間(後)の月

A社ファイル

B社ファイル

2.実際にマクロを書いていく

Sub Main()

Dim strFP   As String       '/--フォルダパス--/'
Dim strFN   As String       '/--ファイル名--/'
Dim mWB     As Workbook     '/--マクロのワークブック--/'
Dim iWB     As Workbook     '/--インプットファイルのワークブック--/'
Dim iWS     As Worksheet    '/--インプットファイルのワークシート--/'


Dim cWS As Integer          '/--シート数格納--/'
Dim s As Integer            '/--シート数カウンタ--/'
Dim n As Integer            '/--年周回カウンタ--/'
Dim y As Long               '/--インプットファイル 行カウンタ--/'
Dim yI As Long              '/--インプットファイル 行数格納--/'
Dim yO As Long              '/--アウトプットファイル 行数格納--/'
Dim yR As Long              '/--処理結果 行数格納--/'

Dim strNo1 As String        '/--番号1Block目--/'
Dim strNo2 As String        '/--番号2Block目--/'
Dim strNo3 As String        '/--番号3Block目--/'
Dim strNo4 As String        '/--番号4Block目--/'
Dim strName As String       '/--氏名--/'
Dim lngFromY As String      '/--対象期間(前)(年)--/'
Dim intFromM As String      '/--対象期間(前)(月)--/'
Dim dateFrom As Date        '/--対象期間(前)(年月)--/'
Dim lngToY As String        '/--対象期間(後)(年)--/'
Dim intToM As String        '/--対象期間(後)(月)--/'
Dim dateTo As Date          '/--対象期間(後)(年月)--/'
Dim lngDiffY As Long        '/--対象期間(年)--/'
Dim lngDiffM As Long        '/--対象期間(月)--/'

Set mWB = ActiveWorkbook
strFP = mWB.Sheets("メイン").Cells(2, 2).Value

'/--データが残ってたりしないよう、先にデータを消去--/'
mWB.Sheets("処理結果").Select
mWB.Sheets("処理結果").Range(Rows(3), Rows(Rows.Count)).Clear
mWB.Sheets("output").Select
mWB.Sheets("output").Range(Rows(2), Rows(Rows.Count)).Clear

yR = 3
yO = 2

strFN = Dir(strFP & "\*.xls*")

Do Until strFN = ""
    Select Case strFN
        '/--見つけたファイルがマクロファイルの場合は何もしない--/'
        Case mWB.Name
        
        '/--見つけたファイルがマクロファイル以外の場合--/'
        Case Else
            Set iWB = Workbooks.Open(strFP & "\ " & strFN)
            cWS = iWB.Worksheets.Count
            For s = 2 To cWS
                Set iWS = iWB.Sheets(s)
                strNo1 = ""
                strNo2 = ""
                strNo3 = ""
                strNo4 = ""
                strName = ""
                lngFromY = 0
                intFromM = 0
                lngToY = 0
                intToM = 0
                
                yI = iWS.Cells(Rows.Count, 1).End(xlUp).Row
                For y = 2 To yI
                    '/--インプット情報を取得--/'
                    strNo1 = iWS.Cells(y, 1)
                    strNo2 = iWS.Cells(y, 2)
                    strNo3 = iWS.Cells(y, 3)
                    strNo4 = iWS.Cells(y, 4)
                    strName = iWS.Cells(y, 5)
                    lngFromY = CLng(iWS.Cells(y, 6))
                    intFromM = CInt(iWS.Cells(y, 7))
                    lngToY = CLng(iWS.Cells(y, 8))
                    intToM = CInt(iWS.Cells(y, 9))
                    
                    '/--データの期間を計算--/'
                    dateFrom = DateSerial(lngFromY, intFromM, 1)
                    dateTo = DateSerial(lngToY, intToM, 1)
                    lngDiffM = DateDiff("m", dateFrom, dateTo)
                    
                    '/--検証用シートに共通項目だけ転記--/'
                    With mWB.Sheets("処理結果")
                        .Cells(yR, 1).Value = iWB.Name
                        .Cells(yR, 2).Value = iWS.Name
                        .Cells(yR, 3).Value = y
                        .Cells(yR, 4).Value = strNo1
                        .Cells(yR, 5).Value = strNo2
                        .Cells(yR, 6).Value = strNo3
                        .Cells(yR, 7).Value = strNo4
                        .Cells(yR, 8).Value = strName
                        .Cells(yR, 9).Value = lngFromY
                        .Cells(yR, 10).Value = intFromM
                        .Cells(yR, 11).Value = lngToY
                        .Cells(yR, 12).Value = intToM
                    End With
                    
                    '/--データ期間ごとの処理--/'
                    Select Case lngDiffM
                        Case Is < 12   '/--同月含む12か月以内の場合--/'
                            '/--検証用シートに1レコード分だけ転記--/'
                            With mWB.Sheets("処理結果")
                                .Cells(yR, 13).Value = lngFromY
                                .Cells(yR, 14).Value = intFromM
                                .Cells(yR, 15).Value = lngToY
                                .Cells(yR, 16).Value = intToM
                            End With
                            '/--アウトプット用シートに1レコード分だけ転記--/'
                            With mWB.Sheets("Output")
                                .Cells(yO, 1).Value = strNo1
                                .Cells(yO, 2).Value = strNo2
                                .Cells(yO, 3).Value = strNo3
                                .Cells(yO, 4).Value = strNo4
                                .Cells(yO, 5).Value = strName
                                .Cells(yO, 6).Value = lngFromY
                                .Cells(yO, 7).Value = intFromM
                                .Cells(yO, 8).Value = lngToY
                                .Cells(yO, 9).Value = intToM
                            End With
                            yR = yR + 1
                            yO = yO + 1
                            
                        Case Else      '/--同月含む13か月以上の場合--/'
                            
                            '/--何年跨いでいるかの期間を取得--/'
                            lngDiffY = DateDiff("yyyy", dateFrom, dateTo)
                            
                            '/--年跨ぎ分の周回処理--/'
                            For n = 0 To lngDiffY
                                '/--検証用シートに対象期間(年)+n年の情報を転記--/'
                                With mWB.Sheets("処理結果")
                                    .Cells(yR, n * 4 + 13).Value = lngFromY + n
                                    If n = 0 Then
                                        .Cells(yR, n * 4 + 14).Value = intFromM
                                    Else
                                        .Cells(yR, n * 4 + 14).Value = 1
                                    End If
                                    .Cells(yR, n * 4 + 15).Value = lngFromY + n
                                    If n = lngDiffY Then
                                        .Cells(yR, n * 4 + 16).Value = intToM
                                    Else
                                        .Cells(yR, n * 4 + 16).Value = 12
                                    End If
                                End With
                                
                                '/--アウトプット用シートに対象期間(年)+n年の情報を転記--/'
                                With mWB.Sheets("Output")
                                    .Cells(yO, 1).Value = strNo1
                                    .Cells(yO, 2).Value = strNo2
                                    .Cells(yO, 3).Value = strNo3
                                    .Cells(yO, 4).Value = strNo4
                                    .Cells(yO, 5).Value = strName
                                    .Cells(yO, 6).Value = lngFromY + n
                                    If n = 0 Then
                                        .Cells(yO, 7).Value = intFromM
                                    Else
                                        .Cells(yO, 7).Value = 1
                                    End If
                                    .Cells(yO, 8).Value = lngFromY + n
                                    If n = lngDiffY Then
                                        .Cells(yO, 9).Value = intToM
                                    Else
                                        .Cells(yO, 9).Value = 12
                                    End If
                                    yO = yO + 1
                                End With
                            Next n
                            yR = yR + 1
                    End Select
                    
                Next y
                Set iWS = Nothing
            Next s
    End Select
    iWB.Close
    Set iWB = Nothing
    strFN = Dir()
Loop

'/--アウトプット用シートを新規ブックにコピーして保存--/'
Application.DisplayAlerts = False
Sheets("Output").Select
Sheets("Output").Copy
ActiveWorkbook.SaveAs Filename:=strFP & "\output.xls", FileFormat:=xlExcel8
ActiveWindow.Close
Application.DisplayAlerts = True

Set mWB = Nothing

End Sub


3.マクロ処理結果

処理結果のシートには検証のデータが、

検証用のシート


Outputのシートには実際にファイル出力されるデータが転記され

出力されるシート


インプットファイルと同じフォルダにoutput.xlsというファイルが出力されます。

出力されたファイル


■マクロ解説

1.データの期間を計算

'/--データの期間を計算--/'
dateFrom = DateSerial(lngFromY, intFromM, 1)
dateTo = DateSerial(lngToY, intToM, 1)
lngDiffM = DateDiff("m", dateFrom, dateTo)

・dateFrom = DateSerial(lngFromY, intFromM, 1)

年(lngFromY)、月(intFromM)、日(1)で日付のシリアル値(1900/1/1からの経過日数)を日付型のdateFromへ格納しています。
lngFromY:対象期間(前)(年)
intFromM:対象期間(前)(月)
同じようにdateTo = DateSerial(lngToY, intToM, 1)で対象期間(後)の年月日も日付型で格納しています。

・lngDiffM = DateDiff("m", dateFrom, dateTo)

dateFromからdateToまでの期間を単位で計算し、数値型lngDiffMへ格納しています。


2.13か月以上の処理

lngDiffMが12以上(同月はlngDiffM=0となるので、13か月目は12となります。)は13か月以上として処理します。

'/--何年跨いでいるかの期間を取得--/'
lngDiffY = DateDiff("yyyy", dateFrom, dateTo)

'/--年跨ぎ分の周回処理--/'
For n = 0 To lngDiffY
    '/--アウトプット用シートにfrom対象期間+n年の情報を転記--/'
    With mWB.Sheets("Output")
        .Cells(yO, 1).Value = strNo1
        .Cells(yO, 2).Value = strNo2
        .Cells(yO, 3).Value = strNo3
        .Cells(yO, 4).Value = strNo4
        .Cells(yO, 5).Value = strName
        .Cells(yO, 6).Value = lngFromY + n
        If n = 0 Then
            .Cells(yO, 7).Value = intFromM
        Else
            .Cells(yO, 7).Value = 1
        End If
        .Cells(yO, 8).Value = lngFromY + n
        If n = lngDiffY Then
            .Cells(yO, 9).Value = intToM
        Else
            .Cells(yO, 9).Value = 12
        End If
        yO = yO + 1
    End With
Next n

・lngDiffY = DateDiff("yyyy", dateFrom, dateTo)

dateFromからdateToまでの期間を単位で計算し、数値型lngDiffMへ格納しています。
これにより何年跨いだかが判明します。

・For n = 0 To lngDiffY

跨いだ年数分周回させます。

・.Cells(yO, 6).Value = lngFromY + n

1年目はn=0なので「lngFromY:対象期間(前)(年)」の値が入り、
2年目はn=1となり「lngFromY」の1年後の年が入ります。
このようにnが+1されるごとに1年後の値が入るようになります。

・If n = 0 Then
.Cells(yO, 7).Value = intFromM
 Else
.Cells(yO, 7).Value = 1
 End If

1年目(n=0)は「intFromM:対象期間(前)(月)」の値が入りますが、2年目以降は対象期間(前)(月)は1月であるべきなので、n>0は1が入るようにしています。

・If n = lngDiffY Then
.Cells(yO, 9).Value = intToM
 Else
.Cells(yO, 9).Value = 12
 End If

最終年目(n=lngDiffY)は「intFromM:対象期間(後)(月)」の値が入りますが、最終年-1年目までは対象期間(後)(月)は12月であるべきなので、n<lngDiffYは12が入るようにしています。

・yO = yO + 1

意外と忘れがちですが、13か月以上の場合は年毎に分割(インプット1に対してアウトプット複数)なので、この周回の中で「yO:Outputシートの行」を+1していきます。


このようにDateSerial、DateDiffを組み合わせ期間を取得、その期間の中で年と月をどのよう表現すればよいか考えると条件にある

2.インプットファイル、“対象期間(前)”と“対象期間(後)”の月数が、12か月以内のものは、そのまま転記し、13か月以上のものは"1月~12月”の表示形式で最大12か月の期間で行を分ける。(1行の内、期間は年を跨がない)

を達成できるようになります。


↓よろしければクリックをお願いします!

TOPページへ戻る


■社内DX案件紹介

【社内DX案件紹介】アンケートの集計-1-

【社内DX案件紹介】アンケートの集計-2-

【社内DX案件紹介】架電内容ごとの情報振り分け-1-

【社内DX案件紹介】架電内容ごとの情報振り分け-2-

【社内DX案件紹介】2つのファイルの内容を比較する

【社内DX案件紹介】封入物の重さによって印刷会社を変える

【社内DX案件紹介】値の入っているシートのみ印刷する


■社内DXの進め方

【社内DXの進め方】ブログの目的(会社が求めるDX)

【社内DXの進め方】社内DXの推進について

【社内DXの進め方】社内DXは具体的に何をすればよいのか?

【社内DXの進め方】なぜ今Excelか?


2023年1月11日水曜日

【ASN.1】PER(Packed Encoding Rules)コーディングとは

※注意! 本内容は独学で得た知識のため、内容に誤りがある場合があります。
     予め誤りがある前提でご覧ください。 

■PERコーディングとは

PERコーディングとは、極力データサイズを小さくするためにできた符号化規則で「プリアンブル(省略可)・データ長(省略可)・値(省略可)」の3種類で構成されています。

またオクテット列ではなく、ビット列で構成されているため情報が圧縮されてより少ないデータ量で転送が可能になります。

プリアンブルとしては主に「Extensionbit」「OptionalBitmap」「DefaultBitmap」があり、データ長として「DataLength」、値として「Value」となっています。

構成省略可否説明
Extensionbit省略可拡張部(...)があるかどうかを1Bitで判定
OptionalBitmap省略可オプション項目(OPTIONAL)数分のBitsで判定
DefaultBitmap省略可デフォルト項目(DEFAULT)数分のBitsで判定
DataLength省略可データを表すのに必要なBit数を定義。SIZEや要素数で判定
Value省略可表示される情報

・Extensionbit

将来拡張される可能性がある場合があるため、拡張される場合を想定して「...」を付与することで定義される。

この「...」がある場合は最初の1BitをExtensionbitとして追加情報があるかどうか判断される。

例えば'60'Hという情報が転送されてきたとして、ASN.1の情報として

Data ::= SEQUENCE {
	value	Value
}

Value ::= INTEGER(0..15)

である場合は、

HEX |Bits|Contents                      |Value|Parameter|Link
----+-----------------------------------|-----+---------+------
    |    |Data SEQUENCE Data            |     |value    |Value
  60|0110|Value INTEGER Data            |    6|        6|
    |0000|Padding                       |     |         |

となるので

Data
	value = 6

という結果が返ります。

しかし、以下のように

Data ::= SEQUENCE {
	value	Value,
	...
}

Value ::= INTEGER(0..15)

Extensionbit(...)がある場合は、

HEX |Bits|Contents                      |Value|Parameter|Link
----+-----------------------------------|-----+---------+------
  60|0___|Data SEQUENCE Extensionbit    |    0|False    |
    |    |Data SEQUENCE Data            |     |value    |Value
    |_110|Value INTEGER Data            |   12|       12|
    |0___|                              |     |         |
    |_000|Padding                       |     |         |

となるので

Data
	value = 12

という結果が返ります。


・OptionalBitmap

情報において必須項目(Mondatry)とオプション(Optional)の2種類があります。

ASN.1においてもそれは同様でオプション項目には以下のようにOPTIONALが付与され、OptionalBitmapにてオプション項目が有効か無効か判定されます。

例えば'60 24'Hという情報が転送されてきたとして、ASN.1の情報として

Data ::= SEQUENCE {
	value1	Value,           -- Mondatry
	value2	Value OPTIONAL,  -- Optional
	value3	Value OPTIONAL,  -- Optional
	value4	Value OPTIONAL,  -- Optional
}

Value ::= INTEGER(0..15)

である場合は

HEX |Bits|Contents                      |Value|Parameter|Link
----+-----------------------------------|-----+---------+------
  60|011_|Data SEQUENCE OptionalBitmap  |'011'|off,on,on|
    |    |Data SEQUENCE Data            |     |value1   |Value
    |___0|Value INTEGER Data            |    0|        0|
    |000_|                              |     |         |
    |    |Data SEQUENCE Data            |     |value3   |Value
    |___0|Value INTEGER Data            |    1|        1|
  24|001_|                              |     |         |
    |    |Data SEQUENCE Data            |     |value4   |Value
    |___0|Value INTEGER Data            |    2|        2|
    |010_|                              |     |         |
    |___0|Padding                       |     |         |

となるので、

Data
	value1 = 0
	value3 = 1
	value4 = 2

という結果が返ります。


・DefaultBitmap

パラメータとして標準値が決まっている場合、敢えてその値をBitsから取り出さなくてもDEFAULTを付与することで判定用の1bitで標準値を取り出すことが可能になります。

例えば'80'Hという情報が転送されてきたとして、ASN.1の情報として

Data ::= SEQUENCE {
	value	Value
}

Value ::= INTEGER(0..15)

である場合は

HEX |Bits|Contents                      |Value|Parameter|Link
----+-----------------------------------|-----+---------+------
    |    |Data SEQUENCE Data            |     |value    |Value
  80|1000|Value INTEGER Data            |    8|        8|
    |0000|Padding                       |     |         |

となるので、

Data
	value = 8

という結果が返ります。

しかしここで以下のように

Data ::= SEQUENCE {
	value	Value DEFAULT 99
}

Value ::= INTEGER(0..15)

DEFAULTを付与すると

HEX |Bits|Contents                      |Value|Parameter|Link
----+-----------------------------------|-----+---------+------
  80|1___|Data SEQUENCE DefaultBitmap   |  '1'|on       |
    |    |Data SEQUENCE Default Data    |     |       99|
    |_000|Padding                       |     |         |
    |0000|                              |     |         |

となるので、

Data
	value = 99

という結果が返ります。


・DataLength

データ長を定義する場合SIZE(a)のように固定長である場合とSIZE(a..b)のように可変長である場合があります。

SIZE(a)の場合はaを表すことができるBit数を確保すれば良いですがSIZE(a..b)のように可変長である場合はそのサイズを測るBit数の判定が必要となります。

例えば'10'Hという情報が転送されてきたとして、ASN.1の情報として

Value ::= INTEGER(4..19)

である場合は、19-4である15を表すことができる4Bits(’1111’B)が必要になります。

しかしここで重要なのは初期値(Value=0のとき)が4なので、

HEX |Bits|Contents                      |Value|Parameter|Link
----+-----------------------------------|-----+---------+------
  10|0001|Value INTEGER Data            |    1|    1+4=5|
    |0000|Padding                       |     |         |

となります。そうなると値としては

value = 5

という結果が返ります。


↓よろしければクリックをお願いします!

TOPページに戻る


■関連ページ

【ASN.1】ASN.1(Abstract Syntax Notation One)とは

【ASN.1】ASN.1の型とPERのデコード

2022年12月30日金曜日

【社内DX案件紹介】封入物の重さによって印刷会社を変える

 コスト削減において、もっとも大きいな削減は人件費となりますが、通常の業務を少しDXすることでコスト削減を図ることが可能です。


■社内DXの依頼内容

お客様に送る郵送物を、封入物の重さによって印刷会社1・2・3に分類しコスト削減を図りたい。

全ての封入物の重さから印刷会社を1~3で分類するマクロ


■社内DXの条件

・封入物はA~Eの5種類

 封入物A:2g

 封入物B:3g

 封入物C:5g

 封入物D:10g

 封入物E:15g


・郵送物の重さによって印刷会社を変える

 15g未満:印刷会社1

 35g未満:印刷会社2

 35g以上:印刷会社3


・リストは横方向に順不同で封入物が記載されている

封入物が記載された顧客リスト


■諸元

・封入物のリストから重さを検索する


・検索には連想配列(Dictionaryオブジェクト)を使用して検索を高速化する


・封入物の総重量によって印刷会社を1~3で設定


■作成方法

1.シート「リスト」に顧客情報と封入物のリストを用意(今回は項番1~100でセット)

シート「リスト」 封入物が順不同でセットされた100名分の顧客リスト

2.シート「封入物リスト」に封入物名と重さのリストを用意

シート「封入物リスト」 封入物A~Eとその重さが記載されたリスト

3.実際にマクロを書いていく

Sub Insatsu_List()
    Dim y As Long           '/--行周回用のカウンタ--/'
    Dim x As Integer        '/--列周回用のカウンタ--/'
    
    Dim dicFunyu As Object  '/--封入物の連想配列用オブジェクト--/'
    Dim strKey As String    '/--検索用キー格納--/'
    Dim lngOmosa As Long    '/--重さを格納--/'
    Dim strKaisha As String '/--印刷会社名--/'
    
    '/--オブジェクトを連想配列(Dictionary)としてセット--/'
    Set dicFunyu = CreateObject("Scripting.Dictionary")
    
    '/--連想配列作成--/'
    For y = 2 To 6
        strKey = Sheets("封入物リスト").Cells(y, 1).Value
        lngOmosa = Sheets("封入物リスト").Cells(y, 2).Value
        '/--連想配列に検索キー(strKey)と値(lngOmosa)を格納--/'
        dicFunyu.Add strKey, lngOmosa
    Next y
    
    '/--リスト内のデータに重さ、印刷会社を記入--/'
    For y = 2 To 101
        lngOmosa = 0
        For x = 3 To 7
            strKey = Sheets("リスト").Cells(y, x).Value
            lngOmosa = lngOmosa + dicFunyu.Item(strKey)
        Next x
        
        Sheets("リスト").Cells(y, 8) = lngOmosa
        
        Select Case lngOmosa
            Case Is < 15
                strKaisha = "1"
            Case Is < 35
                strKaisha = "2"
            Case Is >= 35
                strKaisha = "3"
        End Select
        
        Sheets("リスト").Cells(y, 9) = strKaisha
        
    Next y
    
    Set dicOmosa = Nothing
    
    
End Sub


■マクロ解説

1.連想配列の作成

    '/--オブジェクトを連想配列(Dictionary)としてセット--/'
    Set dicFunyu = CreateObject("Scripting.Dictionary")
    
    '/--連想配列作成--/'
    For y = 2 To 6
        strKey = Sheets("封入物リスト").Cells(y, 1).Value
        lngOmosa = Sheets("封入物リスト").Cells(y, 2).Value
        '/--連想配列に検索キー(strKey)と値(lngOmosa)を格納--/'
        dicFunyu.Add strKey, lngOmosa
    Next y

・Set dicFunyu = CreateObject("Scripting.Dictionary")

dicFunyuというオブジェクトをDictionaryとしてセットします。

※Officeの365とか、なぜか旧Verと参照設定が異なる場合があるので、基本参照設定を使わないで作成しています


dicFunyu.Add strKey, lngOmosa

連想配列に検索キー(strKey)と値(lngOmosa)を代入します。

ルールとしては必ず検索キー1に対して値1となること

Excel関数のVlookupとかみたいに検索キー1つに対して複数の値をとることはできません。

※アイデア次第でやる方法はいくつかありますが別の機会に。


2.重さの取得

            strKey = Sheets("リスト").Cells(y, x).Value
            lngOmosa = lngOmosa + dicFunyu.Item(strKey)


・strKey = Sheets("リスト").Cells(y, x).Value

strKey に封入物名を代入します。


・lngOmosa = lngOmosa + dicFunyu.Item(strKey)

検索キー(strKey)からItem(値)を取り出します。


いかがでしょうか。

このように、少しのアイデアで十分に社内DXが実現できるようになります。


↓よろしければクリックをお願いします!

TOPページへ戻る


■社内DX案件紹介

【社内DX案件紹介】アンケートの集計-1-

【社内DX案件紹介】アンケートの集計-2-

【社内DX案件紹介】架電内容ごとの情報振り分け-1-

【社内DX案件紹介】架電内容ごとの情報振り分け-2-

【社内DX案件紹介】2つのファイルの内容を比較する

【社内DX案件紹介】データの期間によってレコードを分割しファイル出力する

【社内DX案件紹介】値の入っているシートのみ印刷する


■社内DXの進め方

【社内DXの進め方】ブログの目的(会社が求めるDX)

【社内DXの進め方】社内DXの推進について

【社内DXの進め方】社内DXは具体的に何をすればよいのか?

【社内DXの進め方】なぜ今Excelか?

2022年12月27日火曜日

【社内DX案件紹介】2つのファイルの内容を比較する

 ■社内DXの依頼内容

100行50列のデータが入っている2つのファイルの内容を比較し、一致しないセルに色を付けたい


■社内DXの条件

・A、Bのファイルは同じフォーマットで作られている

・情報は100行50列の5000個のセルがあり、セルは空欄の場合もある

・A、Bそれぞれのセルの値が同じか調べ、異なる場合は片方のファイルのセルだけ色を付ける

・セルに色付けするファイルのみマクロで作成してもよい


■諸元

・マクロは使わず、条件付き書式で実施

・B側のファイルに新たにコピペシートを追加し、Aの値を張り付ける

・Bのファイルには条件付き書式にてコピペシートの値と比較して色を付ける

■作成方法

1.B2~AY101までのセルに値を入れる(今回は1~2をランダムで代入する)

AとBのファイルの値を比較

もしランダムの値を入れるサンプルを使いたい場合はマクロでこれを入れて下さい。
Sub RNDM()
Dim y As Long
Dim x As Integer
Dim l As Integer
Dim h As Long

l = 1
h = 2

Randomize

For y = 2 To 101
    For x = 2 To 51
        Cells(y, x).Value = Int((h - l + 1) * Rnd + l)
    Next x
Next y

End Sub


2.Aファイル側に新たにコピペシートを作成し、Bのシート内容をコピーする

Bファイルの内容をコピペシートに反映


3.シート「コピペシート」に条件付き書式を設定する

・値の範囲(B2~AY101)を選択する

・[ホーム]-[条件付き書式]-[新しいルール]

条件付き書式の進め方

・「数式を使用して、書式設定するセルを決定」を選択

・「次の数式を満たす場合に値を書式設定(O)」に次の値を入れる
 =B2<>Aファイル!B2
=B2<>Aファイル!B2

・「書式(F)...」をクリックし、着色する色を決める(今回は黄色)
セルの書式設定で黄色を選択

・OKで決定
黄色で決定

・Aファイルとコピペシートで値が異なるセルのみ黄色でハッチングされる
Aファイルとコピペシートで値が異なるセルのみ黄色でハッチング


このように、特にマクロを使わずとも、2つのファイルの比較を実施することができます。

↓よろしければクリックをお願いします!

TOPページへ戻る


■社内DX案件紹介

【社内DX案件紹介】アンケートの集計-1-

【社内DX案件紹介】アンケートの集計-2-

【社内DX案件紹介】架電内容ごとの情報振り分け-1-

【社内DX案件紹介】架電内容ごとの情報振り分け-2-

【社内DX案件紹介】封入物の重さによって印刷会社を変える

【社内DX案件紹介】データの期間によってレコードを分割しファイル出力する

【社内DX案件紹介】値の入っているシートのみ印刷する


■社内DXの進め方

【社内DXの進め方】ブログの目的(会社が求めるDX)

【社内DXの進め方】社内DXの推進について

【社内DXの進め方】社内DXは具体的に何をすればよいのか?

【社内DXの進め方】なぜ今Excelか?


2022年12月20日火曜日

【ASN.1】ASN.1(Abstract Syntax Notation One)とは

ASN.1とは携帯やパソコン間通信等の電気通信で良く使用されるデータ構造の記述方法です。

この記法を送受側合わせておくことでより少ないデータ量で情報の送受信が可能になります。

これにはどんなメリットがあるかというと大きく以下2点です。

低コスト(少ないパケット代ですむ。運営側は帯域が多少狭くても大丈夫になる)

高速(送りたいデータ量が少ない=データサイズが小さい=送り終わるまでの時間が速い=高速)


■ASN.1の送受信イメージ

例えば、対戦格闘ゲームで有名な手からビームがでるコマンド「↓\→+P」を通信で相手に送るとします。

↓\→+Pで手からビームが出るイメージ

この時にルールとして

↑:0x00

↓:0x01

←:0x02

→:0x03

\:0x04(左上)

\:0x05(右下)

/:0x06(左下)

/:0x07(右上)

+:0x08

P:0x09

K:0x0a

とすると「↓\→+P」を表現するのに普通は「01 05 03 08 09」の5Byteを相手に送る必要があります。


これをASN.1(PER)で表現すると

まずは以下のルールを送受信側で用意しておきます。

Key_Command ::= SEQUENCE {
	command SEQUENCE(SIZE(1..10)) OF Command
}

Command ::= ENUMERATED {
	ue,
	shita,
	hidari,
	migi,
	hidariue,
	migishita,
	hidarishita,
	migiue,
	plus,
	p,
	k
}

そうすると「41 53 89」の3Byteだけ送るだけで済みます。

そして受信側は受け取った「41 53 89」をデコードし、

HEX |Bits|Value
----+-----------------------------------|-----+--------
    |    |Key_Command SEQUENCE Data     |     |command
  41|0100|command SEQUENCE_OF DataLength|    4|4+1=5
    |    |command SEQUENCE_OF Data      |     |Command
    |0001|Command ENUMERATED Data       |    1|Shita
    |    |command SEQUENCE_OF Data      |     |Command
  53|0101|Command ENUMERATED Data       |    5|migishita
    |    |command SEQUENCE_OF Data      |     |Command
    |0011|Command ENUMERATED Data       |    3|migi
    |    |command SEQUENCE_OF Data      |     |Command
  89|1000|Command ENUMERATED Data       |    8|plus
    |    |command SEQUENCE_OF Data      |     |Command
    |1009|Command ENUMERATED Data       |    9|p

こんな感じの情報を受け取ることになります。

Key_Command
	command
		shita
	command
		migishita
	command
		migi
	command
		plus
	command
		p
このように、定型文など決まった情報を使用する際にはASN.1を使用してやり取りを行うことで、低コスト・高速な通信が可能になります。

↓よろしければクリックをお願いします!

TOPページに戻る


■関連ページ

【ASN.1】PER(Packed Encoding Rules)コーディングとは

【ASN.1】ASN.1の型とPERのデコード


2022年12月15日木曜日

【社内DX案件紹介】架電内容ごとの情報振り分け-2-

 前回の続きで今回は実際にマクロを書いていきます。

前> 【社内DX案件紹介】架電内容ごとの情報振り分け-1-


今回作るマクロ

前回のおさらいですが、

依頼内容は

データベースの情報を条件ごとに振り分ける

という話をしました。

データベースの情報を架電内容ごとに振り分ける

実際の処理としてはシンプルで

1.シート「抽出データ」のデータを取得する

2.取得したデータを条件に合ったシートへ転記する

の2点。
処理の流れ 条件だけ赤字


ただこの「条件」の作り方が今回のポイントでIF文ではなく、各カラムの情報①~④を繋げて判定する方法で行きたいと思います。
分類の条件

まずは全体像です。
本当はデータベースからデータをとって分類し、最後エクセルのファイルで出力するのですが、今回はシート「抽出データ」にある架電内容のデータを各シート「パターンA」~「パターンL」に情報を転記するマクロとしました。
マクロのエクセルシート


次にマクロの全体です。
Sub Sample2()

Dim intSheet As Integer         '/--シート番号--/'
Dim strSheetName As String      '/--シート名--/'
Dim lngMaxY As Long             '/--シート「抽出データ」の最大行数--/'
Dim lngY As Long                '/--行周回用カウンタ--/'
Dim lngMaxY2 As Long            '/--転記先シートの最大行数--/'
Dim intMaxX As Integer          '/--シート「抽出データ」の最大列数--/'
Dim intX As Integer             '/--列周回用カウンタ--/'
Dim strKaden As String          '/--架電理由--/'
Dim strKoutei As String         '/--工程名称--/'
Dim strNAT As String            '/--NAT--/'
Dim strIshi As String           '/--意思確認--/'
Dim strHantei As String         '/--条件判定--/'

'/--シート「抽出データ」の最大行数・最大列数を取得--/'
lngMaxY = Sheets("抽出データ").Cells(Rows.Count, 1).End(xlUp).Row
intMaxX = Sheets("抽出データ").Cells(1, Columns.Count).End(xlToLeft).Column

'/--カラム名を各シートに転記--/'
For intSheet = 2 To ActiveWorkbook.Sheets.Count
    For intX = 1 To intMaxX
        Sheets(intSheet).Cells(1, intX).Value = Sheets("抽出データ").Cells(1, intX).Value
    Next intX
Next intSheet

'/--シート「抽出データ」の最大行数分周回--/'
For lngY = 2 To lngMaxY
    strSheetName = ""
    strKaden = "0"
    strKoutei = "0"
    strNAT = "0"
    strIshi = "0"
    strHantei = "0000"

    strKaden = Sheets("抽出データ").Cells(lngY, 1).Value
    strKoutei = Sheets("抽出データ").Cells(lngY, 15).Value
    strNAT = Sheets("抽出データ").Cells(lngY, 19).Value
    strIshi = Sheets("抽出データ").Cells(lngY, 20).Value
    
    '/--架電理由の条件格納--/'
    Select Case strKaden
        Case "自動更新"
            strKaden = "1"
        Case "OB対象外"
            strKaden = "1"
        Case "審査"
            strKaden = "1"
        Case "電話・資料請求"
            strKaden = "2"
        Case "窓口(新規)"
            strKaden = "3"
        Case "不明"
            strKaden = "4"
        Case "乗換"
            strKaden = "4"
        Case "利用率高"
            strKaden = "4"
        Case "未成年"
            strKaden = "4"
        Case "ハイクラス"
            strKaden = "5"
        Case Else
            strKaden = "9"
    End Select
    
    '/--工程名称の条件格納--/'
    Select Case strKoutei
        Case "電話・本人宛NAT"
            strKoutei = "1"
        Case "電話・事前不備有"
            strKoutei = "2"
        Case "電話・本人宛不備"
            strKoutei = "3"
        Case "電話・同意"
            strKoutei = "4"
        Case "電話・資料請求"
            strKoutei = "5"
        Case Else
            strKoutei = "9"
    End Select
    
    '/--NATの条件格納--/'
    If strNAT = "" Then
        strNAT = "1"
    Else
        strNAT = "9"
    End If
    
    '/--意思確認の条件格納--/'
    If strIshi = "1" Then
        strIshi = "1"
    Else
        strIshi = "9"
    End If
    
    '/--各条件を結合--/'
    strHantei = strKaden & strKoutei & strNAT & strIshi
    
    '/--条件によるパターン判定--/'
    Select Case Left(strHantei, 2)
        Case "11"
            Select Case strHantei
                Case "1111"
                    strSheetName = "パターンA"
                Case Else
                    strSheetName = "パターンL"
            End Select
        Case "12"
            strSheetName = "パターンB"
        Case "13"
            strSheetName = "パターンC"
        Case "14"
            strSheetName = "パターンD"
        Case "15"
            strSheetName = "パターンE"
        Case "21"
            strSheetName = "パターンG"
        Case "22"
            strSheetName = "パターンH"
        Case "23"
            strSheetName = "パターンF"
        Case Else
            Select Case Left(strHantei, 1)
                Case "3"
                    strSheetName = "パターンI"
                Case "4"
                    strSheetName = "パターンJ"
                Case "5"
                    strSheetName = "パターンK"
                Case Else
                    strSheetName = "パターンL"
            End Select
    End Select
    
    '/--転記先シートの最大行数を取得--/'
    lngMaxY2 = Sheets(strSheetName).Cells(Rows.Count, 1).End(xlUp).Row + 1
    
    '/--転記先シートへデータを転記--/'
    For intX = 1 To intMaxX
        Sheets(strSheetName).Cells(lngMaxY2, intX).Value = Sheets("抽出データ").Cells(lngY, intX).Value
    Next intX
        
Next lngY

End Sub

それぞれのパートで説明すると
'/--シート「抽出データ」の最大行数・最大列数を取得--/'
lngMaxY = Sheets("抽出データ").Cells(Rows.Count, 1).End(xlUp).Row
intMaxX = Sheets("抽出データ").Cells(1, Columns.Count).End(xlToLeft).Column
ここはシート「抽出データ」の1列目の値がある最終行1行目の値がある最終列を取得しています。
まず
.Cells(Rows.Count, 1).End(xlUp).Row
ですが、
Row.Count:Excelの最後の行(1048576)
1      :1列目
.End(xlUp):最後の行から上方向に
.Row    :値のある行番号をとる
こんな意味となります。

.Cells(1, Columns.Count).End(xlToLeft).Column
ここもほとんど同じで
Columns.Count:Excelの最後の列(16384)
.End(xlToLeft)  :最後の列から左方向に
.Column    :値のある列番号をとる
こんな意味となります。

'/--架電理由の条件格納--/'
Select Case strKaden
    Case "自動更新"
        strKaden = "1"
    Case "OB対象外"
        strKaden = "1"
    Case "審査"
        strKaden = "1"
    Case "電話・資料請求"
        strKaden = "2"
    Case "窓口(新規)"
        strKaden = "3"
    Case "不明"
        strKaden = "4"
    Case "乗換"
        strKaden = "4"
    Case "利用率高"
        strKaden = "4"
    Case "未成年"
        strKaden = "4"
    Case "ハイクラス"
        strKaden = "5"
    Case Else
        strKaden = "9"
End Select
ここは①架電理由を1~5、9で分類してstrKadenに入れています。
同様にstrKoutei(工程名称)、strNAT(NAT)、strIshi(意思確認)も下図の条件に合わせて分類していきます。
分類の条件 赤字でパターン番号付与

'/--各条件を結合--/'
strHantei = strKaden & strKoutei & strNAT & strIshi

'/--条件によるパターン判定--/'
Select Case Left(strHantei, 2)
    Case "11"
        Select Case strHantei
            Case "1111"
                strSheetName = "パターンA"
            Case Else
                strSheetName = "パターンL"
        End Select
    Case "12"
        strSheetName = "パターンB"
    Case "13"
        strSheetName = "パターンC"
    Case "14"
        strSheetName = "パターンD"
    Case "15"
        strSheetName = "パターンE"
    Case "21"
        strSheetName = "パターンG"
    Case "22"
        strSheetName = "パターンH"
    Case "23"
        strSheetName = "パターンF"
    Case Else
        Select Case Left(strHantei 1)
            Case "3"
                strSheetName = "パターンI"
            Case "4"
                strSheetName = "パターンJ"
            Case "5"
                strSheetName = "パターンK"
            Case Else
                strSheetName = "パターンL"
        End Select
End Select

ここからは実際にA~Lのパターン分けになります
①②③④を繋げたstrHanteiがどうだったか?をSelect文で分類していますが、
①②③④の値が
1111・・・パターンA
12xx・・・パターンB(xはどの値でもよい)
13xx・・・パターンC
14xx・・・パターンD
15xx・・・パターンE
23xx・・・パターンF
21xx・・・パターンG
22xx・・・パターンH
3xxx・・・パターンI
4xxx・・・パターンJ
5xxx・・・パターンK
それ以外・・パターンL
となります。

ここでポイントですが、パターンA、I~K以外は①②で判定できる。という点です。

なので基本は
Select Case Left(strHantei, 2)
で左2文字分で判定し、
Left(strHantei, 2)の値が”11”の時は
Select Case strHantei
    Case "1111"
        strSheetName = "パターンA"
    Case Else
        strSheetName = "パターンL"
End Select
で4文字全てを条件とし、逆にLeft(strHantei 2)の値が”23”以降であれば
Select Case Left(strHantei, 1)
    Case "3"
        strSheetName = "パターンI"
    Case "4"
        strSheetName = "パターンJ"
    Case "5"
        strSheetName = "パターンK"
    Case Else
        strSheetName = "パターンL"
End Select
で左1文字だけで判定することでパターンA~Lの全てを分類できるようになります。

'/--転記先シートの最大行数を取得--/'
lngMaxY2 = Sheets(strSheetName).Cells(Rows.Count, 1).End(xlUp).Row + 1

'/--転記先シートへデータを転記--/'
For intX = 1 To intMaxX
    Sheets(strSheetName).Cells(lngMaxY2, intX).Value = Sheets("抽出データ").Cells(lngY, intX).Value
Next intX
で最後は転記先のシートの最大行数を取得して、その1つ下(+1)したところに、その行の値を転記して1行分が終了します。
あとはそれをシート「抽出データ」の全レコードで実施して終了になります。

これで案件2の紹介は終了します。


↓よろしければクリックをお願いします!

2022年12月13日火曜日

【社内DX案件紹介】架電内容ごとの情報振り分け-1-

 今回も実際に依頼された社内DX案件を紹介します。


■案件2 架電内容ごとの情報振り分け

依頼内容

社内のデータベースにあるコールセンターのデータを特定の条件で振り分けてもらいたいというもの。

今はスタッフが手作業で振り分けていたようだが、それをVBAで何とかしたい。というもの。

つまりこれを

データベースのデータを手作業で振り分け

こうしたいということ

データベースの情報を自動で振り分け

実際のデータはこんな感じなのですが、

約20列のデータベースのデータ

これを

・架電理由

・工程名称

・NAT

・意思確認

データベースの条件を取得する箇所

の値が以下の条件に当てはまるようなパターン処理が必要となる。

12パターンの条件分岐

というわけで今回はこんな処理をなるべくシンプルになるようなマクロを作成します。

今回作るマクロ

シート「抽出データ」にある情報をパターン毎に分類し、各シートへ転記するマクロ

実際の処理の流れは非常に簡単で

1.シート「抽出データ」のデータを取得する

2.取得したデータを条件に合ったシートへ転記する

処理のながれ

この2点だけです。

ただ、この”条件”が非常に厄介です。

処理のながれ 条件だけ赤字

先ほどの分類の条件をもう一度確認しますがこんな感じです。

分類の条件 再掲

これをフローチャートで表すとこんな感じで複雑です。

条件のフローチャート

更にこれをマクロで書くとこうなります。

    strKaden = 架電理由の値を格納

    strKoutei = 工程名称の値を格納

    strNAT = NATの値を格納

    strIshi = 意思確認の値を格納

とすると、

If strKaden = "自動更新" Or strKaden = "OB対象外" Or strKaden = "審査" Then
	If strKoutei = "電話・本人宛NAT" Then
		If strNAT = "" And strIshi = "1" Then
		strSheetName = "パターンA"
		Else
		strSheetName = "パターンL"
		End If
	ElseIf strKoutei = "電話・事前不備有" Then
		strSheetName = "パターンB"
	ElseIf strKoutei = "電話・本人宛不備" Then
		strSheetName = "パターンC"
	ElseIf strKoutei = "電話・同意" Then
		strSheetName = "パターンD"
	ElseIf strKoutei = "電話・資料請求" Then
		strSheetName = "パターンE"
	Else
		strSheetName = "パターンL"
	End If
ElseIf strKaden = "電話・資料請求" Then
	If strKoutei = "電話・本人宛NAT" Then
		strSheetName = "パターンG"
	ElseIf strKoutei = "電話・事前不備有" Then
		strSheetName = "パターンH"
	ElseIf strKoutei = "電話・本人宛不備" Then
		strSheetName = "パターンF"
	Else
		strSheetName = "パターンL"
	End If
ElseIf strKaden = "窓口(新規)" Then
	strSheetName = "パターンI"
ElseIf strKaden = "不明" Or strKaden = "乗換" Or strKaden = "利用率高" _ 
		Or strKaden = "未成年" Then
	strSheetName = "パターンJ"
ElseIf strKaden = "ハイクラス" Then
	strSheetName = "パターンK"
Else
	strSheetName = "パターンL"
End If

かなり見づらいコードになりますね。


なので、今回は少しやり方を変えて、

架電理由:①

工程名称:②

NAT:③

意思確認:④

として、①②③④の値からパターン分けしていきたいと思います。

分類の条件 各番号によるパターン化

どういう考え方かというと、ASN.1の列挙型(ENUMERATED)に近いのですが、

①架電理由=自動更新・・・1

②工程名称=電話・本人宛NAT・・・1

③NAT=空欄・・・1

④意思確認=1・・・1

である場合、①②③④は”1111”となります。

この①②③④が”1111”の時はパターンAと判定する。

①②③④が"12xx"の時はパターンB、"15xx"の時はパターンEとすることで、複雑な条件式を作らなくてもよくなり、また条件が増えた場合においても修正が簡易となります。

※xは不問(どんな値でもよい)


このような場合はどのように書いていくのか、次回は実際にコードを書いてみたいと思います。

↓よろしければクリックをお願いします!

次 > 【社内DX案件紹介】架電内容ごとの情報振り分け-2-


TOPページに戻る



■社内DX案件紹介

【社内DX案件紹介】アンケートの集計-1-

【社内DX案件紹介】アンケートの集計-2-

【社内DX案件紹介】2つのファイルの内容を比較する

【社内DX案件紹介】封入物の重さによって印刷会社を変える

【社内DX案件紹介】データの期間によってレコードを分割しファイル出力する

【社内DX案件紹介】値の入っているシートのみ印刷する


■社内DXの進め方

【社内DXの進め方】ブログの目的(会社が求めるDX)

【社内DXの進め方】社内DXの推進について

【社内DXの進め方】社内DXは具体的に何をすればよいのか?

【社内DXの進め方】なぜ今Excelか?