2010-04-16 4 views
3

저는 몇 시간 동안이 작업을 해왔으며 해결책을 찾지 못했습니다. 두 개의 인벤토리 목록이 있는데 하나는 스프레드 시트이고 다른 하나는 데이터 테이블입니다. 인벤토리가 누락되었는지 확인하려면 스프레드 시트를 데이터 표와 대조해야합니다. 스프레드 시트는 내가 데이터베이스에있는 것과 일치해야합니다. 예를 들어 스프레드 시트가 마스터와 유사하므로 DB에 재고가없는 경우 목록을 추가하고 보고서를 작성해야합니다.두 가지 컬렉션 목록 비교

스프레드 시트를 반복하면서 스프레드 시트 루프의 각 인벤토리에 대해 데이터 표를 통해 목표를 달성 할 수 있다고 생각했지만 잘못된 것으로 판명되었습니다. 어떻게 내가 이걸 할 수있는 아이디어?

public void Reconcile() 
{ 
    ObjectDataSource ods = new ObjectDataSource(); 
    ods.ID = "ods"; 
    ods.TypeName = ""; 
    ods.SelectMethod = "GetAssets"; 
    ods.TypeName = "dsAssetsTableAdapters.AssetsTableAdapter"; 
    ods.SelectParameters.Clear(); 

    ReportDataSource rds = new ReportDataSource("dsAssets_Assets", ods); 

    reportViewer1.LocalReport.DataSources.Clear(); 
    reportViewer1.LocalReport.DataSources.Add(rds); 

    string _list = ""; 
    string _list_missing_SN = ""; 

     string filename = Server.MapPath("XLS/reconcile.xls"); 
     string sheetname = GetExcelSheetNames(filename)[0].ToString(); 

     String sConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;" + 
      "Data Source=" + filename + ";" + 
      "Extended Properties=Excel 8.0;"; 
     OleDbConnection objConn = new OleDbConnection(sConnectionString); 
     objConn.Open(); 
     OleDbCommand objCmdSelect = new OleDbCommand("SELECT * FROM [" + sheetname + "]", objConn); 
     OleDbDataAdapter objAdapter1 = new OleDbDataAdapter(); 
     objAdapter1.SelectCommand = objCmdSelect; 
     DataSet objDataset1 = new DataSet(); 
     objAdapter1.Fill(objDataset1, "XLData"); 

     string m_AssetManagement = System.Configuration.ConfigurationManager.ConnectionStrings["Asset_Management"].ToString(); 

     List<string> SN_list = new List<string>(); 

     SqlDataReader Assets_rd; 
     SqlCommand cmdMyAssets = new SqlCommand(); 
     cmdMyAssets.Connection = new SqlConnection(m_AssetManagement); 
     cmdMyAssets.CommandType = CommandType.StoredProcedure; 
     cmdMyAssets.CommandText = "sp_Assets_Hardware_Select_by_Serial_Number"; 

     try 
     { 
      cmdMyAssets.Connection.Open(); 
      Assets_rd = cmdMyAssets.ExecuteReader(); 
      string strString; 
      while (Assets_rd.Read()) 
      { 
       strString = Assets_rd.GetSqlString(0).ToString().Trim() + "^" + Assets_rd.GetInt32(1).ToString().Trim() + "^" + Assets_rd.GetInt32(2).ToString().Trim();      
       SN_list.Add(strString); 
      } 
     } 
     catch (SqlException dbError) 
     { 
      Trace.Write("Database unavailable with Message: ", dbError.Message); 
      Trace.Write("Stack Trace: ", dbError.StackTrace); 
      throw; 
     } 

     bool record_match = false; 
     foreach (DataRow drXCL in objDataset1.Tables[0].Rows) 
     { 
      if (drXCL.ItemArray[1].ToString() != string.Empty) 
      {      
       try 
       { 
        string[] assetInfo = null; 
        assetInfo = SN_list[0].Split('^'); 
        if (assetInfo[0].Contains(drXCL.ItemArray[1].ToString())) 
        { 
         _list += "|" + drXCL.ItemArray[1].ToString(); 
        } 
        else 
        { 
         _list_missing_SN += drXCL.ItemArray[1].ToString().Trim() + "<br>"; 
        }      
       } 
       catch (Exception SqlEx) 
       { 
        // Throw Sqw Exception 
        clAppExceptions.buildEmailNotification(SqlEx.Message.ToString()); 
       } 
      } 
      else 
      { 
       //_list += "|*** NO SERIAL NUMBER ***";      
      }   
     } 
    if (_list_missing_SN != "") 
    { 
     Page.ClientScript.RegisterClientScriptBlock(this.Page.GetType(), "myAlert", "<script language='javascript'>alert('Following Serial Numbers were not on the spreasheet: " + _list_missing_SN + "');</script>"); 
    } 
    _list += "|"; 

    ods.SelectMethod = "GetAssetsBySerialNumbers"; 
    ods.SelectParameters.Add("list", _list); 

    reportViewer1.LocalReport.ReportPath = Server.MapPath("~/Reports/Asset_List.rdlc"); 

    ReportParameter rpCategory = new ReportParameter("ReportParameter", "These assets are gone."); 
    ReportParameter[] _rpCategory = { rpCategory }; 
    reportViewer1.LocalReport.SetParameters(_rpCategory); 

    reportViewer1.LocalReport.Refresh(); 
} 
+2

어떤 DB를 사용하고 있습니까? –

답변

1

내가 배열로 마스터리스트를 로딩하여 첫번째 어레이의 위치에 대응 bools의 제 2 어레이를 만들 것이다 :

감사 에릭 여기

는 방법이다. 그런 다음 데이터 테이블을 반복하면서 요소를 찾으면 bool을 true로 바꿉니다. 찾을 수없는 경우 해당 요소를 찾을 수없는 배열에 저장하십시오. 데이터 테이블 루프가 끝나면 2 개의 목록을 생성 할 수 있습니다. 첫 번째 목록은 데이터 테이블의 항목이지만 마스터 목록의 항목은 아닙니다 ... 찾을 수없는 배열입니다. 두 번째 목록은 bool 배열을 반복하여 만들어지며 false 값은 마스터 목록 요소가 데이터 테이블에서 발견되지 않았 음을 의미합니다.

그런 다음 일치 시키거나 일치시키지 않는 정보 나 다른 정보를 포함하도록 확장 할 수 있습니다.

+0

그것은 내가 상상했던 것의 일종이지만 작동하지 않았습니다. 스프레드 시트의 각 행에 대해 db의 전체 테이블을 반복하여 하나의 레코드를 제외한 모든 레코드에 대해 일치 항목을 생성하지 않습니다. 그리고 스프레드 쉬트와 DB에 4K 이상의 행이 있으면, 난장판이 엉망이 될 것입니다! – Eric

+0

두 개의 정렬 된 배열에 레코드를 추가합니다. 색인을 생성 할 때 수행 할 배열이 일치하는 인스턴스를 찾습니다. 어레이가 다른 위치를 찾았 으면 오프 레코드를 분리하고 계속하십시오. 파일 diff와 동일합니다. – JDMX

1

다른 접근 방식을 제안합니다. 데이터베이스의 데이터를 복사하여 다른 워크 시트의 스프레드 시트에 넣고 일치 기능을 사용할 수 있습니다. 스프레드 시트에서 데이터를 가져 와서 새 테이블에 넣을 수도 있습니다. 그런 다음 쿼리를 사용하여 불일치를 찾습니다. 이것이 한 번이 아니라면 프로그래밍 솔루션이 필요하다고 생각하지 않습니다. 이 일종의 응용 프로그램에 필요한 경우 내 대답을 무시하십시오.

+0

안녕하세요. 불행히도 모든 것은 프로그래밍 방식으로 완료되어야합니다. – Eric

1

이것이 어떤 용도로 사용되는지 모르겠지만 IEnumerable sequeneces에 두 개의 목록이 있으면 LINQ를 사용하여 간단한 작업을 수행 할 수 있습니다.

나는 내가이 목적을 위해 사용 IEnumerable을 위해 쓴 확장 방법이 있습니다

public static IEnumerable<T> NotIn<T>(this IEnumerable<T> inputSequence, IEnumerable<T> secondSequence) 
    { 
     return secondSequence == null ? new List<T>(inputSequence) : inputSequence.Where(element => !secondSequence.Contains(element)); 
    } 

내가 제대로 내가 같은 일을 수행 기본 LINQ 기능을 찾는 결국 기억 경우지만, 물론, 그것이 무엇인지 잊어 버렸습니다

+4

? http://msdn.microsoft.com/en-us/library/bb300779.aspx – dtb

+0

감사합니다. LINQ를 사용하지 않아서이 문제를 조사해야합니다. – Eric

+0

예. Except 메서드는 똑같은 작업을 수행합니다 –

1

방금 ​​빠른 해결책을 찾고 있다면 Excel에서 모든 것을 다 할 것입니다. Excel을 DB 및 링크 목록에 쉽게 연결할 수 있습니다.

  1. 은 Excel 파일

  2. 확인하는 수식을 삽입 (가 항상 DB에 연결되어있어 이러한 방법)에 DB를 연결하면 마스터 목록에서 (부품, 키 등) DB에서 귀하의 목록에 존재합니다.

    Excel에서 목록을 연결하는 방법을 보려면 link을 사용하십시오.

+0

제안 해 주셔서 감사합니다.하지만 모두 프로그래밍 방식으로 수행해야합니다. – Eric

0

간단하게 ... ADO.Net은 아마도이 문제에 대한 가장 간단한 접근 방법입니다. DataTable에 스프레드 시트의 값을 입력하면 (OleDb 사용을 원할 경우) OleDb 또는 올바른 ADO.Net 클라이언트를 사용하여 데이터베이스에서 정보를 가져올 수 있습니다.) 그런 다음 위치 또는 마지막으로 본 시간과 같은 필드에 대한 값을 데이터베이스로 다시 업데이트 할 수 있습니다. 이러한 채우기 및 업데이트 명령은 쿼리 또는 저장된 procs 일 수 있습니다.

테이블 스키마와 같은 세부 정보를 제공하면 추가 답변을 추가로 확장 할 수 있습니다.

편집 ...

이미 같은 데이터 세트에서 둘 다 넣어 외부 조인을 할 것 DataView를 쿼리를 작성할 수 닷넷의 DataTable에있는 소스 중 하나가있는 경우. 외부 조인을 사용하면 일치하는 값과 일치하지 않는 값을 볼 수 있습니다.

업데이트

은 ...

죄송는 다시이에 얻을 너무 오래했다. (나는 새로운 일을 시작 했으므로 바빴다.) 나는 두 개의 스프레드 쉬트를 사용하고 있지만, 다른 데이터베이스와 심지어 다른 ADO.Net 제공자 사이에서도 동일한 개념을 사용할 수 없다는 이유가 없다. 이 예제의 기본 개념은 데이터베이스에 LastSeen 타임 스탬프를 만드는 것입니다. 그런 다음 거기에없는 것을 찾는 대신 최신 inventroy를 데이터베이스에 게시 한 다음 업데이트되지 않은 항목을 쿼리합니다.

var inventoryFile = "Inventory.xlsx"; //ID,Item 
var databaseFile = "Database.xlsx"; //ID,Item,Type,SN,LastSeen 

var connectionFormatter = "Provider=Microsoft.ACE.OLEDB.12.0;" + 
    "Data Source=\"{0}\";Mode=ReadWrite;" + 
    "Extended Properties=\"Excel 12.0 Xml;HDR=Yes;\";"; 

var inventoryConnectionString = string.Format(connectionFormatter, 
               inventoryFile); 
var databaseConnectionString = string.Format(connectionFormatter, 
              databaseFile); 

using (var inventoryConnection = 
       new OleDbConnection(inventoryConnectionString)) 
using (var databaseConnection = 
       new OleDbConnection(databaseConnectionString)) 
{ 
    if (inventoryConnection.State != ConnectionState.Open) 
     inventoryConnection.Open(); 

    if (databaseConnection.State != ConnectionState.Open) 
     databaseConnection.Open(); 

    var lastSeenCmdString = "SELECT MAX(LastSeen) FROM [Sheet1$]"; 
    var lastSeenCommand = new OleDbCommand(lastSeenCmdString, 
              databaseConnection); 
    var lastSeen = lastSeenCommand.ExecuteScalar(); 

    var inventorySelectCmdString = "SELECT ID, Item FROM [Sheet1$]"; 
    var inventoryCmd = new OleDbCommand(inventorySelectCmdString, 
             inventoryConnection); 
    var table = new DataTable(); 
    var idCol = table.Columns.Add("ID", typeof(int)); 
    var itemCol = table.Columns.Add("Item", typeof(int)); 

    var inventoryDataAdapter = new OleDbDataAdapter(inventoryCmd); 
    var databaseDataAdapter = new OleDbDataAdapter(); 

    var updateLastSeenCmdString = 
        "UPDATE [Sheet1$] SET LastSeen=NOW() WHERE Item=?"; 
    var updateCmd = new OleDbCommand(updateLastSeenCmdString, 
            databaseConnection); 
    var parameter = updateCmd.Parameters.Add("Item", 
              OleDbType.Integer, 
              0, 
              "Item"); 

    databaseDataAdapter.UpdateCommand = updateCmd; 

    inventoryDataAdapter.Fill(table); 

    table.AcceptChanges(); 
    foreach (var row in table.Rows.OfType<DataRow>()) 
     row.SetModified(); 

    databaseDataAdapter.Update(table); 

    var notSeenCmdString = "SELECT ID,Item,Type,SN,LastSeen " + 
          "FROM [Sheet1$]" + 
          "WHERE LastSeen <= ?"; 
    var notSeenCmd = new OleDbCommand(notSeenCmdString, 
             databaseConnection); 
    notSeenCmd.Parameters.Add("LastSeen", OleDbType.Date).Value = lastSeen; 

    databaseDataAdapter.SelectCommand = notSeenCmd; 

    var missingInventory = new DataTable(); 
    databaseDataAdapter.Fill(missingInventory); 

    foreach (var row in missingInventory.Rows.OfType<DataRow>()) 
     Console.WriteLine("ID: {0} Item:{1} Type:{2} SN:{3} LastSeen:{4}", 
          row.ItemArray); 
} 
+0

Matthew, 너 여기 뭔가있는거야. 나는 나의 질문에 충분한 정보를주지 않았다고 생각한다. 이 파일은 Excel 파일을 읽고 데이터베이스와 비교하고 OleDbAdapter를 사용하여 데이터 집합을 채우고 foreach 루프를 사용하여 데이터 집합을 파싱하는 순수 응용 프로그램입니다. 그런 다음 모든 인벤토리 레코드를 SqlDataReader로 가져옵니다. 여기까지 내가 지금까지 해왔지만, 내가 말한대로 작동하지 않습니다. – Eric

+0

상단의 편집을 참조하십시오. – Eric

+0

@Eric, 죄송합니다.이 예제를 게시하는 데 오랜 시간이 걸렸습니다. 바라기를 당신은 해결책을 찾아 냈다. 그렇지 않다면 나는 이것이 도움이되기를 바랍니다. –

0

궁극적으로 다양한 옵션이 있습니다. 올바른 결정을 내리려면 몇 가지 질문에 답해야합니다.

  1. 얼마나 자주이 작업을 수행해야합니까?
  2. 활용할 수있는 자원 수준은 무엇입니까?
  3. 이 작업을 얼마나 빨리 실행해야합니까?
  4. 얼마나 많은 데이터를 비교해야합니까?

이 질문에 대한 답변을 얻은 후에는보다 정확한 해결책을 제안 할 수 있습니다.

+0

네, 그 사실을 깨닫고 모든 대답을 가지고 있지만 이것을 구현하는 기술이 다소 부족합니다. – Eric