技術とか戦略とか

IT技術者が技術や戦略について書くブログです。

n対nマッチングのロジック(C#のサンプルコード付き)

以前に、以下の記事にて、マッチング処理のロジックについて書かせていただきました。

マッチング処理のロジック – サイゼントの技術ブログ


以前の記事では1対1マッチングと1対nマッチングについて説明しました。
今回の記事では、より複雑なn対nマッチングについて補足します。


1対1マッチングは、マスタデータの1つのキー項目に対して、トランザクションデータの0~1つのレコードが対応するものでした。
1対nマッチングは、マスタデータの1つのキー項目に対して、トランザクションデータの0~複数のレコードが対応するものでした。
n対nマッチングは、マスタデータ側も1つであるとは限らず、トランザクションデータの1つのキー項目に対して、マスタデータの0~複数のレコードが対応するケースもある、というものを指します。


n対nマッチングでは、以前に参照したトランザクションデータのレコードが、再び参照される可能性があります。
ファイルに対してランダムにアクセスすることでこれを実現できますが、処理が複雑になるため、今回はファイルは順次読み込みのままで、読み込んだトランザクションデータのレコードを一時的に退避するロジックを提示します。


フローチャートと例は以下の通りとなります。


フローチャート

【例】
・要件
商品名が管理されている商品マスタと、商品の販売履歴(トランザクション)をファイル形式で読み込み、商品名と販売日を別ファイルで出力したい。


・商品マスタのフォーマット
カンマ区切りの固定長ファイル。
商品コードと商品副コードでレコードを一意に特定できるようにデータをセットする。

商品コード(7桁)
カンマ(1桁)
商品副コード(2桁)
カンマ(1桁)
商品名(20桁)

・販売履歴のフォーマット
カンマ区切りの固定長ファイル。
商品コード・販売日でレコードを一意に特定できるようにデータをセットする。

商品コード(7桁)
カンマ(1桁)
販売日(8桁)
カンマ(1桁)
販売個数(5桁)
カンマ(1桁)
販売金額(9桁)

・出力ファイルのフォーマット
商品名(20桁)
カンマ(1桁)
販売日(8桁)

・プログラムのフォルダ構成
execute.bat
matching.cs
files┬master.csv
     └transaction.csv

ソースコード(execute.bat)
@echo off

C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe matching.cs
matching.exe
del matching.exe

pause

ソースコード(matching.cs)
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

namespace Program
{
    class Program
    {
        // EOFフラグ
        static bool isSrmEof = false;
        static bool isSrtEof = false;
        
        static void Main(string args)
        {
            // ファイルオープン
            StreamReader srm = new StreamReader
                (@"files\master.csv", Encoding.UTF8);
            StreamReader srt = new StreamReader
                (@"files\transaction.csv", Encoding.UTF8);
            StreamWriter sw = new StreamWriter
                (@"files\matched.csv", false, Encoding.UTF8);
            
            // 現キー
            string tmpNowKey;
            
            // トランレコード退避用配列
            // COBOLの場合は、十分な長さのOCCURS句を定義する、一時ファイルのOPENとCLOSEを繰り返す、等で対応
            ArrayList tmpTranRecordList;
            
            // 先読みRead
            string
mRecord;
            string tRecord;
            mRecord = mRead(srm);
            tRecord = tRead(srt);
            
            // マッチング処理のループ
            while (!isSrmEof || !isSrtEof)
            {
                // masterのみの場合
                if ((!isSrmEof && isSrtEof) ||
                    (string.Compare(mRecord[0],tRecord[0]) < 0))
                {
                    // 何もしない
                    // master読み込み
                    mRecord = mRead(srm);
                }
                
                // マッチした場合
                else if ((!isSrmEof && !isSrtEof) &&
                         (string.Compare(mRecord[0],tRecord[0]) == 0))
                {
                    // 現キー退避
                    tmpNowKey = mRecord[0];
                    
                    // トランレコード退避用配列初期化
                    tmpTranRecordList = new ArrayList();
                    
                    // transactionが次のキーに進むまでループ
                    while ((!isSrtEof) &&
                           !(string.Compare(tmpNowKey,tRecord[0]) < 0))
                    {
                        // トランレコード退避
                        tmpTranRecordList.Add(tRecord[1]);
                        
                        // transaction読み込み
                        tRecord = tRead(srt);
                    }
                    
                    // masterが次のキーに進むまでループ
                    while ((!isSrmEof) &&
                           !(string.Compare(tmpNowKey,mRecord[0]) < 0))
                    {
                        // 退避したトランレコードを順次結合しファイル出力
                        for (int i = 0; i < tmpTranRecordList.Count; i++)
                        {
                            sw.WriteLine(mRecord[2] + "," + tmpTranRecordList[i]);
                        }
                    
                        // master読み込み
                        mRecord = mRead(srm);
                    }
                }
                
                // transactionのみの場合
                else if ((isSrmEof && !isSrtEof) ||
                         (string.Compare(mRecord[0],tRecord[0]) > 0))
                {
                    // エラー出力
                    Console.WriteLine("Error:" + tRecord[0] + " is tran only.");
                    
                    // transaction読み込み
                    tRecord = tRead(srt);
                }
            }
            
            // ファイルクローズ
            srm.Close();
            srt.Close();
            sw.Close();
        }
        
        // MasterファイルRead
        static string
mRead(StreamReader srm)
        {
            if (srm.Peek() == -1)
            {
                isSrmEof = true;
                return null;
            }
            else
            {
                string str = srm.ReadLine();
                return str.Split(',');
            }
        }
        
        // TransactionファイルRead
        static string[] tRead(StreamReader srt)
        {
            if (srt.Peek() == -1)
            {
                isSrtEof = true;
                return null;
            }
            else
            {
                string str = srt.ReadLine();
                return str.Split(',');
            }
        }
    }
}

・商品マスタのレコード(files\master.csv)
0000001,00,hoge                
0000002,00,fuga                
0000004,01,piyo-Red            
0000004,02,piyo-Blue           
0000005,01,negi-Miku           
0000005,02,negi-Rin            


・販売履歴のレコード(files\transaction.csv)
0000001,20180401,00100,00010000
0000001,20180402,00200,00020000
0000003,20180401,00001,00001000
0000004,20180401,00002,00002000
0000004,20180402,00004,00004000
0000005,20180401,01000,00100000


・バッチ実行結果(標準出力)
Microsoft (R) Visual C# Compiler version 4.8.9032.0
for C# 5
Copyright (C) Microsoft Corporation. All rights reserved.

This compiler is provided as part of the Microsoft (R) .NET Framework, but only supports language versions up to C# 5, which is no longer the latest version. For compilers that support newer versions of the C# programming language, see http://go.microsoft.com/fwlink/?LinkID=533240

Error:0000003 is tran only.
続行するには何かキーを押してください . . .


・バッチ実行結果(files\matched.csv)
hoge                ,20180401
hoge                ,20180402
piyo-Red            ,20180401
piyo-Red            ,20180402
piyo-Blue           ,20180401
piyo-Blue           ,20180402
negi-Miku           ,20180401
negi-Rin            ,20180401