29961


2つのdataTableからlinqを使ってデータを抽出しています。  
1つは1万件強、もう1つは4万件ほどデータが入っています。  
1万件程度であれば3,4分で終わるのですが、4万件になったとたんに1時間以上、処理がかかります。

同じ処理を通っているのにこの差は何でしょうか？  
[LINQ
複数テーブルjoinと複合条件とwhere](https://ja.stackoverflow.com/questions/29884/linq-%E8%A4%87%E6%95%B0%E3%83%86%E3%83%BC%E3%83%96%E3%83%ABjoin%E3%81%A8%E8%A4%87%E5%90%88%E6%9D%A1%E4%BB%B6%E3%81%A8where)  
こちらで質問した処理を書いています。

[code]

    List<DataClass> dataList = new List<DataClass>(); 
    　　DataClass kdcc = new DataClass(); 
    　　　　foreach (var item in em) {
    　　　　　　　　　　kdcc.ID = item.ID; 
    　　　　　　　　　　kdcc.DATE = item.DATE; 
    　　　　　　　　　　kdcc.NO = item.NO;
    　　　　　　　　　　kdcc.DSP_NO = item.DSP_NO;
    　　　　　　　　　　kdcc.BI = item.BI; 
    　　　　　　　　　　kdcc.NAME_ID = item.NAME_ID.ToString(); 
    　　　　　　　　　　dataList.Add(kdcc);
    　　　　　　　　　　 } 
    
[/code]

これでlinqの結果は取得できるのですが、foreachの部分はもう少し早く処理できないものでしょうか？

追記

@pgrho さん  
前回教えていただいたlinq  

`

[code]

    from em in Table3
    join c1 in table4 on em.TOUROKU_1 equals c1.TOUROKU_NO
    join c2 in table4 on em.TOUROKU_2 equals c2.TOUROKU_NO
    select new
    {
       em.ID,
       em.DATE,
       em.NO,
       em.DSP_NO,
       BI = Table1.Where(v => em.UR == v.NoFlg && v.RTth != "0").Select(v => "R" + v.RTth).FirstOrDefault()
           + Table1.Where(v => em.UL == v.NoFlg && v.LTth != "0").Select(v => "R" + v.LTth).FirstOrDefault()
           + Table1.Where(v => em.DR == v.NoFlg && v.RTth != "0").Select(v => "r" + v.RTth).FirstOrDefault()
           + Table1.Where(v => em.DL == v.NoFlg && v.LTth != "0").Select(v => "l" + v.LTth).FirstOrDefault(),
       NAME_ID = Table2.Where(v => em.ID == v.ID
                                && c1.NAME_1 == v.TOUROKU_NO
                                && v.TOUROKU_NO != "0"
                                && em.DATE >= v.START_DATE
                                && em.DATE < v.END_DATE)
                        .Select(v => v.BCD)
                        .FirstOrDefault() // b1
                + Table2.Where(v => em.ID == v.ID
                                && c1.NAME_2 == v.TOUROKU_NO
                                && v.TOUROKU_NO != "0"
                                && em.DATE >= v.START_DATE
                                && em.DATE < v.END_DATE)
                        .Select(v => v.BCD) // b2
                        .FirstOrDefault() 
                        //TODO  +where文を追加
                    `
    
[/code]

上のlinqをこのように分けてみたのですが..最初のqueryで時間がかかっています。  
  
`

[code]

     BCD = Table2.Where(v => em.ID == v.ID
                         && v.TOUROKU_NO != 0
                         && em.DATE >= v.START_DATE
                         && em.DATE < v.END_DATE)
                        .Select(v => v.BCD)
                        .FirstOrDefault()
    
[/code]

`  
の部分がネックなような気がします。  
  
以下、私が考えた分けたlinqです。

`

[code]

     var query = from em in Table3
                    select new {
                         ID = em.ID,
                         TOUROKU_NO = Table2.Where(a => em.ID == a.ID)
                         .Select(a =>a.TOUROKU_NO)
                         .FirstOrDefault(),
                         BCD = Table2.Where(v => em.ID == v.ID
                             && v.TOUROKU_NO != 0
                             && em.DATE >= v.START_DATE
                             && em.DATE < v.END_DATE)
                            .Select(v => v.BCD)
                            .FirstOrDefault()
                    }
    
    
        　var query2 = from em in Table3
                            join c1 in table4  on em.TOUROKU_1 equals c1.TOUROKU_NO
                            join c2 in table5 on em.TOUROKU_2 equals c2.TOUROKU_NO
                            join c4 in query on em.ID equals c4.ID
                       select new {
                         ID = em.ID,
                         DATE = em.DATE,
                         NO = em.NO,
                         NAME_ID = query.Where(v => em.ID == v.ID
                             && c1.NAME_1 == v.TOUROKU_NO)
                            .Select(v => v.BCD)
                            .FirstOrDefault() // c1
    
                        + query.where(v => em.ID == v.ID
                                          && c1.NAME_2 == v.TOUROKU_NO)
                                          .Select(v => v.BCD)
                                          .FirstOrDefault()
                        //TODO NAME_16まで同じ処理
                             };
    
    //ここはほとんど変わらず
    
           var query3 = from em in Table3
                    select new {
                      em.ID,
                      em.DATE,
                      em.NO,
                      em.DSP_NO,
                      em.KUBUN,
                      BUI = Table1.Where(v => em.UR == v.NoFlg && v.RTth != "0").Select(v => "R" + v.RTth).FirstOrDefault()
               + Table1.Where(v => em.UL == v.NoFlg && v.LTth != "0").Select(v => "R" + v.LTth).FirstOrDefault()
               + Table1.Where(v => em.DR == v.NoFlg && v.RTth != "0").Select(v => "r" + v.RTth).FirstOrDefault()
               + Table1.Where(v => em.DL == v.NoFlg && v.LTth != "0").Select(v => "l" + v.LTth).FirstOrDefault()
                    };
    
    
                          var resultJoin = from q3 in query3
                         join q2 in query2 on  q3.KANJA_ID equals byoumei.KANJA_ID
                         select new {
                           ID = q3.KANJA_ID,
                           DATE = q3.RAIIN_DATE,
                           NO = q3.RAIIN_NO,
                           DSP_NO = q3.BUI_DSP_NO,
                           BI = q3.BUI,
                           NAME_ID = query2.Where(v => q3.DATE == v.DATE && q3.NO == v.NO).Select(v => v.NAME_ID).FirstOrDefault(),
                           KUBUN = q3.KUBUN
                         };
                 `
    
[/code]

一応、考えて書いてみましたが、考え方としてはあってますでしょうか？  
  
ｺﾒﾝﾄいただけると幸いです。  


CC BY-SA 3.0
2016-10-28T02:44:16.250
29954
2016-10-31T07:52:19.590
2017-04-13T12:52:39.113
-1
19310
question
c# .net linq
linqの結果をforeachで取得しているのですが、処理速度を上げるにはどうすればいいですか


> 1万件程度であれば3,4分で終わるのですが、4万件になったとたんに1時間以上、処理がかかります。

処理時間が比例していない、つまりO(n)でないということは意図していない個所で時間を要していると思われます。第三者に尋ねるのではなく、プロファイラを用いて原因個所を特定すべきです。

例えば`DataClass`が`class`か`struct`かでも処理速度は変わってきます。それ以前に`class`の場合はdataListには最終レコードの内容しか含まれないので、意図しない結果、処理速度以前の問題の可能性もあります。


CC BY-SA 3.0
2016-10-28T04:00:57.393
29958
2016-10-28T04:00:57.393
4236
29954
answer


先の質問の`Table2.TOUROKU_NO`のレコード数が`Table3`の件数に比例して増えている場合、処理にかかる時間は大雑把に言ってn^2からnlognのオーダーになると考えられます。ですので、件数が増加すると飛躍的に速度が遅くなることは考えられることです。

高速化のためには`Table2`のクエリーを分離し、`DataClass`に代入する直前でC#側の処理により値を計算した方が良いでしょう。例えば、

[code]

    NAME_ID = Table2.Where(v => em.ID == v.ID
                            && c1.NAME_1 == v.TOUROKU_NO
                            && v.TOUROKU_NO != "0"
                            && em.DATE >= v.START_DATE
                            && em.DATE < v.END_DATE)
    
[/code]

上の服問い合わせから`c1.NAME_1 ==
v.TOUROKU_NO`の条件を除けばすべての`em`に関連する`Table2`のレコードを取得できると思いますので、これらを別の`SELECT`で取得することが出来れば、両者をLINQ
to Objectなどで結合するのは高々4万×4万×16回程度の処理で済みます。これなら悪くても数十秒程度で計算できると思います。

まずTable3に対するクエリーはカスタム結合を使用して以下のような形になるかと思います。最後に`.ToList()`を付加しているので、このクエリーは即時実行されます。

[code]

    // 質問のquery
    var query1 = from em in Table3
                from v in Table2 ON
                where v => em.ID == v.ID
                     && v.TOUROKU_NO != 0
                     && em.DATE >= v.START_DATE
                     && em.DATE < v.END_DATE
                select new {
                    ID = em.ID,
                    START_DATE = v.START_DATE,
                    END_DATE = v.END_DATE,
                    TOUROKU_NO = v.TOUROKU_NO,
                    BCD = v.BCD,
                };
    var list = query1.ToList();
    
[/code]

本体のクエリーは`NAME_ID`が不要になりますが、かわりに`NAME_1`などを追加する必要があります。

[code]

    // 質問のquery3
    var query = from em in Table3
                join c1 in table4  on em.TOUROKU_1 equals c1.TOUROKU_NO
                join c2 in table5 on em.TOUROKU_2 equals c2.TOUROKU_NO
                select new {
                  em.ID,
                  em.DATE,
                  em.NO,
                  em.DSP_NO,
                  em.KUBUN,
                  BUI = Table1.Where(v => em.UR == v.NoFlg && v.RTth != "0").Select(v => "R" + v.RTth).FirstOrDefault()
                       + Table1.Where(v => em.UL == v.NoFlg && v.LTth != "0").Select(v => "R" + v.LTth).FirstOrDefault()
                       + Table1.Where(v => em.DR == v.NoFlg && v.RTth != "0").Select(v => "r" + v.RTth).FirstOrDefault()
                       + Table1.Where(v => em.DL == v.NoFlg && v.LTth != "0").Select(v => "l" + v.LTth).FirstOrDefault(),
                  c1.NAME_1,
                  c1.NAME_2,
                  // TODO:以下省略
                };
    
[/code]

最後に本体のクエリーを実行し、メモリ上の`list`から都度検索を行います。

[code]

    foreach(var item in query)
    {
        DataClass kdcc = new DataClass(); 
    
        var bCore = list.Where(v => em.ID == v.ID
                                && v.TOUROKU_NO != "0"
                                && em.DATE >= v.START_DATE
                                && em.DATE < v.END_DATE);
    
        var b1 = bCore.Where(v => item.NAME_1 == v.TOUROKU_NO).Select(v => v.BCD).FirstOrDefault();
        var b2 = bCore.Where(v => item.NAME_2 == v.TOUROKU_NO).Select(v => v.BCD).FirstOrDefault();
        // TODO:以下省略
    
        dataList.Add(kdcc);
    }
    
[/code]

なおこの方針で実行速度が問題になる場合、

  1. `list`の取得(`.ToList()`)が遅い
  2. `query`の取得(`foreach`の先頭行)が遅い
  3. `NAME_ID`の計算(`foreach`の各行)が遅い

などの原因が考えられます。3はソート順の最適化などで多少改善できるかと思いますが、`SELECT`自体が低速である場合は一時テーブルやストアドプロシージャの使用を検討した方が良いかもしれません。


CC BY-SA 3.0
2016-10-28T04:54:07.203
29961
2016-10-31T07:52:19.590
2016-10-31T07:52:19.590
5750
5750
29954
answer