none
サーバー内の複数テーブルから、一個のデータテーブルを作りたいのですがうまくいきません。 RRS feed

  • 質問

  • メインは、T_Salesというテーブルです(サーバー内のテーブル)。 ここから、データテーブルを作るのですが、ここには担当者のID(数値、Staff_ID)しかありません。 担当者名は、別テーブルにあります(T_M_Staff)。 T_M_Staff に、ID と氏名(StaffName)があります。

    この二個のテーブルから、dtsalesというデータテーブルを作成したいのです。 ここには、Staff_ID だけでなく、担当者名(StaffName)も含ませます。 で、これをもとに、Datagridviewを表示します。

    他のテーブルからの担当者名(StaffName)表示は、For  --- Next を使って別コードで作成し普通に作動していますが、90,000件近いデータ表示だと15秒近く(12~14秒)かかり、ちょっと不便です。 

    それで、別方法が上記の方法ですが、コードがうまくいきません。 

    間違い・修正を指摘していただけますか。

    サーバー:SQL2000, VB2010 です。

    'SQL認証接続
            Dim St As String
            Dim Con As New System.Data.SqlClient.SqlConnection
            Dim sql As System.Data.SqlClient.SqlCommand
            Dim ServerName As String = GC_SVRName   'サーバー名(またはIPアドレス)
            Dim UserID As String = GC_UserID      'ユーザーID
            Dim Password As String = GC_Password   'パスワード
            Dim DatabaseName As String = GC_DatabaseName
            St = "Server=" & ServerName & ";"
            St &= "User ID=" & UserID & ";"
            St &= "Password=" & Password & ";"
            St &= "Initial Catalog=" & DatabaseName
            Con.ConnectionString = St
            sql = Con.CreateCommand
            Con.Open()
            Dim adapter = New SqlClient.SqlDataAdapter()
            Dim ds = New DataSet
            adapter.SelectCommand = sql

             'T_Sales/T_M_Staff(サーバーのテーブル)から、データテーブル(dtsales)作成
                Dim sb As New System.Text.StringBuilder
                sb.Append("SELECT T_Sales.Sales_ID")  

           ----略

                sb.Append(" , T_M_Staff.StaffName")   ’この列を追加したいのです! 
                sb.Append(" FROM T_Sales)"
                sb.Append(" Inner Join T_M_Staff On T_Sales.STaff_ID = T_M_Staff.Staff_ID"  ’ここに間違いあり?

                ----略
            
                If sql.Parameters.Count = 0 Then 
             ----略
                    sb.Append(" WHERE ------- ")

                End If
                sb.Append(" ORDER BY")
                sb.Append(" Sales_ID")

                sql.CommandText = sb.ToString()

                adapter.SelectCommand = sql
                adapter.Fill(ds)
                dtsales = ds.Tables(0)

    (注)サーバー内のテーブル
       T_Sales--- STaff_ID(数値)は、ありますが、StaffName(担当者名)はありません、名前はT_M_Staffに格納。
       T_M_Staff---- Staff_ID と これに対応する名前(StaffName)が格納。

    以上です、よろしくお願いします。

    YKsaila

    2012年9月17日 1:25

回答

  • 遅い原因はご想像の通り、For--Nextです。しかもこの中でデータテーブルを操作しているため、その部分がかなりレスポンスの悪さに影響しているものと思われます。
    ざっとコードを読んだだけですが、おそらくSelect文一つで全て書けると思います。もし、無理でもストアドプロシージャで記述することにより、かなりレスポンスが良くなると思います。
    さて、一つのSelect文にしてしまう方法ですが、まず、case句を活用することを覚えられると良いと思います。例えば、where句の場合、以下のように書けます。

    where TradeType = case when @TradeType = '' then TradeType else @TradeType end

    TradeTypeが空白の場合、TradeType  = TradeType となりますから、Where句ではTradeType により絞り込みがないのと同じになります。

    同様に計算についても、SalesAmountの値で場合分けして計算すれば良いでしょう。

    また、会社名や支店コードなどは最初からテーブルをjoinするなどして取得してしまえば良いと思いますが、そのようにできない理由があるのでしょうか?

    SQL文は動作確認してからコードに落とすのが基本です。まず、SQL Server Management Studio等で動作するSQL文を作成してから、コード内に記述して下さい。
    しかし、本来はこのように項目が多かったり、抽出条件が多いようなものは、ストアドプロシージャとして作成するのがベターです。ストアドプロシージャ内ではSQL文をそのまま書けますから、読みにくくなるSQL文を文字列連結して組み立てる必要もありません。(ただし、文字列で組み立てなければならない場合もある。この場合も当然ながらSQLインジェクションに注意)

    SQLで抜いたものを加工せずに使う努力をすることが、まず最初にトライしなければならないことです。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/


    2012年9月19日 9:07
    モデレータ
  • >Format関数等は列全体では有効でないようなのです(個別ではできますが)。

    >列全体で、少数表示桁数を一括で指定したいのです。

    列全体というのは、全ての行においてという意味でしょうか? であれば、以下で適切に書式指定子を指定すると小数点以下の桁が揃えられます。適切にとは、書式指定子でゼロプレースホルダーを使ってみて下さい。

    DataGridViewのセルに表示するテキストの書式(フォーマット)を指定する
    http://dobon.net/vb/dotnet/datagridview/format.html

    書式を指定して数値を文字列に変換する
    http://dobon.net/vb/dotnet/string/inttostring.html

    >この場合、T_M_Staff のStaff_IDを、T_Salesの異なる二個のデータへは連結できないと考えているのですが(自己矛盾が発生しますから)?

    できますよ。テーブルにエイリアスを使えば良いのです。例えば、

    select *, staff1.StaffName as E_StaffName, staff2.StaffName from T_Sales as sales
                inner join T_M_Staff as staff1 on staff1.Staff_ID = sales.Staff_ID
                inner join T_M_Staff as staff2 on staff2.Staff_ID = sales.Operate_Staff_ID

    その他、以下の方法でもできます。

    select *, (select StaffName from T_M_Staff  where Staff_ID = sales.Staff_ID) as E_StaffName,
                 (select StaffName from T_M_Staff  where Staff_ID = sales.Operate_Staff_ID) as StaffName
            from  T_Sales as sales

    #上記でasは省略できます。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/

    • 回答としてマーク yksaila 2012年9月24日 15:41
    2012年9月24日 8:40
    モデレータ
  • trapemiyaさんへ

    ご教示、ありがとうございます。

    (Ⅰ)>列全体で、少数表示桁数を一括で指定したいのです。

    これは、すでに私の掲示コードの中にありました。 なぜ、気付かなかったのでしょうか? 

    自分で探してきたものなのに、不思議です! 疲れていたのでしょうね、きっと。

    'DataGridViewの列表示指定
    Me.DataGridView1.Columns("Profit").DefaultCellStyle.Format = "c"          '(例):¥23,000と表示
    Me.DataGridView1.Columns("Profit_Rate").DefaultCellStyle.Format = "0.00"  ’少数第二位まで表示

    (Ⅱ)T_M_Staff のStaff_IDを、T_Salesの異なる二個のデータへ連結する方法について:

    >できますよ。テーブルにエイリアスを使えば良いのです。

    全く知りませんでした、大変参考になりました。 ありがとうございます。 以下、やったことを掲示します。

    Case (When---then---else---) End 句を、SQL文の中で使用しました。だいぶ、すっきりしました。
    下記の4個の方法を試しました。 それで、約90,000件のデータ表示の所要時間を調べてみました。

    方式(A,B):Select文の中に、全て収める。データテーブル作成後に列の追加をしない。For--Nextなし。

    方式(C,D):Select文の中に、ほとんどを収める。但し、一個だけ、データテーブル作成後に列を追加(E_StaffName)。 FOR NEXTあり。

    結果をまとめますと、INNER JOIN句が多くなると所要時間が増えるようです。 FOR -- NEXTの使用と同じくらい。

    方式B,Cが、よさそうです。 FOR NEXTの使用を回避する点と、コードがすっきりしている点から、Bが少し勝るのでしょうね。

    所要時間8秒は、まだ長いので、これからストアドプロシージャー(ストアドファンクション)に挑戦してみます。

    再度、有難うございました。 本当に、助かっています。

    YKsaila

    以下、参考コードです。 変更部分のみです。
    (A)21秒 -- INNER JOIN 3個使用
                sb.Append("SELECT sales.Sales_ID") 
                sb.Append(" , sales.SYS_Update_Date")
                -------
                ------
                sb.Append(" , case when sales.SalesAmount=0 then 0 when sales.CostAmount>sales.SalesAmount then -1" _
                       & " else (sales.SalesAmount- sales.CostAmount)/sales.SalesAmount*100 end as Profit_Rate")

                sb.Append(" , sales.SalesAmount-sales.CostAmount as Profit")
                ------
                sb.Append(" , staff2.StaffName as StaffName")   ' ここでは、OperateStaffName!
                sb.Append(" , Client.ClientCode as Clientcode") 
                sb.Append(" , Client.BranchNo as BranchNo")  
                sb.Append(" , Client.ShortName as ShortName") 
                sb.Append(" , staff1.StaffName as E_StaffName")
               
                sb.Append(" From T_Sales as sales")
                sb.Append(" Inner join T_M_Staff as staff1 on staff1.Staff_ID = sales.Staff_ID")
                sb.Append(" Inner join T_M_Staff as staff2 on staff2.Staff_ID = sales.Operate_Staff_ID")
                sb.Append(" Inner join T_M_Client as Client on Client.Client_ID = sales.Client_ID")


    (B)8秒--INNER JOIN なし
                sb.Append("SELECT sales.Sales_ID") 
                -------
                sb.Append(" , case when sales.SalesAmount=0 then 0 when sales.CostAmount>sales.SalesAmount then -1" _
                       & " else (sales.SalesAmount- sales.CostAmount)/sales.SalesAmount*100 end as Profit_Rate")

                sb.Append(" , sales.SalesAmount-sales.CostAmount as Profit")

          ------           
                sb.Append(" , (select StaffName from T_M_Staff where T_M_Staff.Staff_ID = sales.Operate_Staff_ID) as StaffName") ' ここでは、OperateStaffName!
                sb.Append(" , (select StaffName from T_M_Staff  where T_M_Staff.Staff_ID = sales.Staff_ID) as E_StaffName")

                sb.Append(" , (select ClientCode from T_M_Client where T_M_Client.Client_ID = sales.Client_ID) as Clientcode")
                sb.Append(" , (select BranchNo from T_M_Client where T_M_Client.Client_ID = sales.Client_ID) as BranchNo") 
                sb.Append(" , (select ShortName from T_M_Client  where T_M_Client.Client_ID = sales.Client_ID) as ShortName")
                sb.Append(" From T_Sales as sales")


    (C)8秒  For --- Next使用(E_StaffName) & INNER JOIN 2個
                sb.Append("SELECT sales.Sales_ID")  '0
                -------
                sb.Append(" , case when sales.SalesAmount=0 then 0 when sales.CostAmount>sales.SalesAmount then -1" _
                       & " else (sales.SalesAmount- sales.CostAmount)/sales.SalesAmount*100 end as Profit_Rate")

                sb.Append(" , sales.SalesAmount-sales.CostAmount as Profit")
          --------
                sb.Append(" , staff2.StaffName as StaffName")   ' ここでは、OperateStaffName!
                sb.Append(" , Client.ClientCode")
                sb.Append(" , Client.BranchNo") 
                sb.Append(" , Client.ShortName")

                'sb.Append(" , staff1.StaffName as E_StaffName")---ここは、For--Next使用
                 sb.Append(" FROM T_M_Staff as staff2 Inner Join (T_Sales as sales Inner Join T_M_Client as Client On Client.Client_ID = sales.Client_ID )On sales.Operate_Staff_ID = Staff2.Staff_ID")
     
    (D)11秒 For--Next使用(E_StaffName) & INNER JOIN 2個
          sb.Append("SELECT T_Sales.Sales_ID")
                ------
                sb.Append(" , case when T_Sales.SalesAmount=0 then 0 when T_Sales.CostAmount > T_Sales.SalesAmount then -1" _
                          & " else (T_Sales.SalesAmount-T_Sales.CostAmount)/T_Sales.SalesAmount*100 end as Profit_Rate")

                sb.Append(" , T_Sales.SalesAmount-T_Sales.CostAmount as Profit")
                -----
                sb.Append(" , T_M_Staff.StaffName")   ' ここでは、OperateStaffName!
                sb.Append(" , T_M_Client.ClientCode")
                sb.Append(" , T_M_Client.BranchNo")  
                sb.Append(" , T_M_Client.ShortName")
                'sb.Append("  FROM T_Sales")
                sb.Append(" FROM T_M_Staff Inner Join (T_Sales Inner Join T_M_Client On T_M_Client.Client_ID = T_Sales.Client_ID )On T_Sales.Operate_Staff_ID = T_M_Staff.Staff_ID")



    • 回答としてマーク yksaila 2012年9月24日 15:42
    • 編集済み yksaila 2012年9月24日 15:44
    2012年9月24日 15:37
  • trapemiyaさんへ

    あれから、再度いろいろやってみました。

    以下の方法で、90,000件--5秒に短縮しました。

    Inner Join 2回使用+(select StaffName from T_M_Staff  where T_M_Staff_ID = T_Sales.Staff_ID) as E_StaffName

    以下、変更したコードです。'****A,****Bを参照してください。

     '90,000件--5秒(二か所変更)  Inner Join 2回('***B) + '****A
                sb.Append("SELECT T_Sales.Sales_ID")  '0
                sb.Append(" , T_Sales.SYS_Update_Date")  '1
                ---------略
                sb.Append(" , case when T_Sales.SalesAmount=0 then 0 when T_Sales.CostAmount > T_Sales.SalesAmount then -1" _
                          & " else (T_Sales.SalesAmount-T_Sales.CostAmount)/T_Sales.SalesAmount*100 end as Profit_Rate")
                sb.Append(" , T_Sales.SalesAmount-T_Sales.CostAmount as Profit")
          ---------略
                sb.Append(" , T_M_Staff.StaffName")  '23  ' ここでは、OperateStaffName!

                '****A          
                sb.Append(" , (Select StaffName from T_M_Staff  where T_M_Staff.Staff_ID = T_Sales.Staff_ID) as E_StaffName")

                sb.Append(" , T_M_Client.ClientCode")  '23
                sb.Append(" , T_M_Client.BranchNo")   '24
                sb.Append(" , T_M_Client.ShortName")  '23

                '****B
                sb.Append(" FROM T_M_Staff Inner Join (T_Sales Inner Join T_M_Client On T_M_Client.Client_ID = T_Sales.Client_ID )On T_Sales.Operate_Staff_ID = T_M_Staff.Staff_ID")

        90,000件表示で、5秒---微妙な時間ですね。 我慢すべき時間でしょうか?

    ちなみに、私のPC環境(自宅)は、以下のようになっています:

    1.サーバー:MS Windows Server 2003 R2(SQL2003)  ---(冒頭のSQL2000 は書き間違いです。)

      CPU--2.60GHZ  メモリー(?)--1.00GBRAM

    2.クライアントPC:Win.7 Ultimate Service Pack1

        CPU--2.93GHZ(Intel Core i3 Cpu 530)      RAM(実装メモリ)--4.00GB(2.80GB使用可能)

      32ビット-オパレーティングシステム

      ハード構成--Win7用(97.6GB、内54.1GB空き)、Vista用(97.6GB、内41.2GB空き)、E(936GB,653GB空き)

    YKsaila     




    • 回答としてマーク yksaila 2012年9月25日 0:24
    • 編集済み yksaila 2012年9月25日 0:28
    2012年9月25日 0:01

すべての返信

  •  select文は一見、よさそうに見えます。
    うまくいかないとはどのようにうまくいかないのでしょうか?うまくいかない場合、それを修正するために、どのようにうまく行かないのかという情報は誰にとっても非常に大切な情報です。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/

    2012年9月17日 3:26
    モデレータ
  • もし上記の方法にこだわりがないのでしたら、SQLServer2000上にViewを作ってみてはいかがでしょうか?

    速度もそちらのほうが早いと思います。さらに問題が起きた時の原因の切り分けもやりやすくなります。

    2012年9月17日 5:35
  • trapemiyaさんへ

    お世話になります。

    上記select文の一部を消しますと、うまく動きます(目的のものは出ませんが)

        (A)  sb.Append(" , T_M_Staff.StaffName")   ’この列を追加したいのです! 
                   sb.Append(" FROM T_Sales)"
            (B)   sb.Append(" Inner Join T_M_Staff On T_Sales.STaff_ID = T_M_Staff.Staff_ID"  

    AB両方に「’」---- 動きます。

    A,Bのどれかを復活させますと、エラーが出ます。 

    Bに問題がないとすれば、Aを消してBだけ残した場合には正常動作のはずでしょうが、やはりこの場合もダメです。

    ということは、Bのコードの書き方に問題があるのでしょうか?

    エラー表示は、下記のものがでます:

    'System.Data.SqlClient.SqlException' の初回例外が System.Data.dll で発生しました。
    'System.NullReferenceException' の初回例外が Main_Menu.exe で発生しました。

    'オブジェクト参照がオブジェクト インスタンスに設定されていません。</Message><StackTrace>  Line: 515

           'DataGridViewの列表示順指定()
            With DataGridView1
                .Columns("ClientCode").DisplayIndex = 9   '***  Line515
                ----略
            End With

    以上です。

    YKsaila

    2012年9月18日 4:54
  • これは、SQLサーバー上でクエリを動かすという意味でしょうか?

    どちらにしても、これは未経験です。 「SQLServer2000上にViewを作成」で検索すれば、ヒントは得られるでしょうか?

    この方法も知っておかなければならないことですね。

    上記の方法が解決しましたら挑戦したいです。 なにか、ヒントをいただければ、ありがたいです。

    YKsaila

    2012年9月18日 5:00
  • とりあえず、「うまくいきません」とか「エラーが出ます」とかでなくもっと具体的に書いた方が良いです。
    たとえば、「どの」行で「どういった」エラーが出る等
    (今回はどういったについては追加で提示されていますが・・・)

    ちなみに確認ですが、ID項目名が「STaff_ID」だったり「Staff_ID」だったりするのは正しいですか?
    ※大文字小文字を識別しない様になっていれば一緒ですが、危険な香りがします。

    続いて今現在出ているエラーについてですが、
    上記に書いている様なソースの「どの」行でのエラーかがわからないと明確な回答は出来ません。

    デバック実行してエラーが発生したポイントで停止し、StackTrace等を見てみるとあっけなく解決したりしませんかね。

    2012年9月18日 5:19
  •  SELECT 文の一部を削れば例外が発生しなくなるということなので、SELECT 文が間違っているであろうという推測なんですかね?

     以前のスレッドにもどなたかが書かれていた思うのですが、どの様な期待に対して、どの様な結果となったか、というのは、デバッグする上で重要な情報です。確かに、「デバッグ実行」が全てではありませんが、期待通りでない動作をするというバグを期待通りに動くようにするという「デバッグ作業」は、現にプログラム コードができてしまっている以上、必ず行わなければなりません。その上で、「情報を集める」というのは、デバッグ作業の一番初めです。他人にデバッグ作業の一部を委任しようというのですから、集めた情報を伝えることが必要です。

     SQL から遠ざかって久しいのですが、SQL Server にはコマンドを実行するツールはついていないのでしたっけ?あらら、SQL Server は、奇麗にアンインストールしちゃってるので、試せないわ。

     たしか、管理ツールからか、cmd から実行できたと思うので、そういったインターラクティブに実行できるツールから SELECT 文を実行して、SQL Server が何がダメだといっているのか、情報を取ってください。コードをいじるのはその後です。


    'オブジェクト参照がオブジェクト インスタンスに設定されていません。

     まったく別の所にエラーがあるんじゃないですか?「.Columns("ClientCode").DisplayIndex = 9 '***  Line515」この行にブレークポイントを貼ってデバッグ実行し、止まったら右クリックして「クイックウォッチ」を選択、「DataGridView1.Columns("ClientCode")」を入力して「再評価」してみてください。Nothing じゃないですか?

     「バグ」というのは、実現したい実行結果と、実際に実行した結果が異なる状態である事を言います。デバッグというのは、異なっているこれら二つを一致させる作業全体を指します。ですから以前、「ただ、デバッグすれば、すべて解決するものでもないと考えますが?」というお言葉に対し、「どういう意味ですか?」と問いました。その時には「プログラミングのための知識が足りていない」というお答えでしたので、それもそうだと思いました。
     それもそうです。が、もう、コードを書いてしまいました。そのコードは、期待する動作と一致していません。この、期待する動作と一致しないコードを期待する動作にするためには、デバッグするしかありません(あるいは、今書いてあるコードは捨てて、新しく作り直すか)。ただし、「デバッグ実行」すれば全て解決するわけではありません。期待する結果と実際の結果、この二つの差異を観察し、調べ、その他の情報も参照しながら、どこにその原因があるのかを推理し、原因を突き止め、一致するように修正すること。これが「デバッグ」です。デバッグしなければ、何も解決しません。知識を増すためにも、まずは情報収集をしてください。読み手は、実際には要らなかった情報を捨てることはできます。しかし、提供されなかった情報を作り出すことはできません。無駄になっても良いので、手に入る全ての情報を出してください。

    この様な結果を得たいと思って、
    どんなものを調べたら、
    これこれの情報が得られた。
    それを、この様に解釈して、
    この様にコード化した。
    実際に動かしたところ、得られた結果はどうだった。
    期待する結果との違いは、これこれ。
    画面上に表示されるメッセージは、これ。


    Jitta@わんくま同盟

    2012年9月18日 12:18
  • yksaila さま よろしく。

    どうも、単純な VB コード上の問題ではなさそうですね。
    投稿の流れを追うと、いくつが疑問があります。

    1. (A)(B) で内部結合を指示するとエラーで、これを外すとコンパイラーが通る らしいこと。

    2. ヌル参照のエラーがある らしいこと。

    3. .Columns("ClientCode").DisplayIndex = 9 と Fill で指定していなそうな項目名がある らしいこと。

     2.の原因が 3.なら 1.でコンパイラーは通らない筈。

    問題切り分けの為、まず、3. を片付けて下さい。
    次に、 DB 上のT_Sales.STaff_ID と T_M_Staff.Staff_ID のテータ型は合っているか、確認して下さい。

     

    yksaila さま ついかです。

    投稿を入れてから、Jitta さまの投稿に気付きました。
    良き投稿者を掴みましたね。 こんなに丁寧に書いてくれる人そうはいませんよ。
    よ~く噛み締めて読んで下さい。

    ところで、VB の IDE で SQLServer に接続できます。
    ここで、Connect を確立して、IDE 上から DB に SQL 文を発行し、色々確認できるはずです。
    詳細は Visual Studio のヘルプを参照して下さい。

    • 編集済み ShiroYuki_Mot 2012年9月18日 13:25 ついか挿入
    2012年9月18日 13:04
  • trapemiyaさんへ

    ありがとうございます。 なお、他の投稿者の方への返信も、今回はここで代用させてください。 

    半分は、つかめました。 残りは、明日以降に挑戦します。 

    まず、実験として;

    "  Dim sb As New System.Text.StringBuilder "以下のコードの代わりに、下記(A_01)コードを入れ替えてみました。

    項目は、省いてありますが。 正常に動作します! サーバー内の二個のテーブルから、一個のデータテーブルが作成出来ることは確認できました。

    この実験の理由は、 "  Dim sb As New System.Text.StringBuilder "が二個のテーブル結合に適応していないのかも、と考えたからです(ま、多分、これは私の間違いでしょうが)。 

    というより、この方法(StringBuilder)は、私は詳しくない(初体験)ので、既知の方法でやってみたのです。 動きます!

    で、これからは、StringBuilder方式では、なぜできないのかを考えたいです。 そもそも、出来ないのか、または単なる私のコードの間違いなのか、です。 見た限りでは、コードに全く誤りはなさそうです。 でも、先に書きました通り、コード自体を認識していない気がします。 A・Bなしでは正常なのですから、StringBuilder方式での上記コードのAB以外の個所は正常ということになります。 では、なぜ、A・Bがいけないのか、ということになります。 明日(19日)以降に考えます。

    以下は、正常に動作しました代用コードです:

    (A_01)

      Dim dtsales As New DataTable  'T_Salesのデータテーブル作成
            Dim TN As Integer
            TN = CInt(Me.CB_E_TradeType.SelectedValue)
            Dim sq As String
            sq = "SELECT T_Sales.Sales_ID, T_Sales.SYS_Update_Date, T_Sales.TradeType,      T_Sales.Staff_ID, T_M_Staff.StaffName,T_Sales.Operate_Staff_ID" _
                & " FROM (T_Sales Inner Join T_M_Staff On T_Sales.Operate_Staff_ID = T_M_Staff.Staff_ID)" _
                & " Where T_Sales.TradeType = " & TN & ""
            adapter.SelectCommand = New SqlClient.SqlCommand(sq, Con)
            adapter.SelectCommand.CommandType = CommandType.Text
            adapter.Fill(ds)
            dtsales = ds.Tables(0)

            '▼値の表示
            DataGridView1.DataSource = dtsales

    (注:T_Sales.Operate_Staff_ID は、これが正しいです。 T_Sales.Staff_ID は仮でした、これはエラーには関係ありません )

    これ(A_01)だと、できて、最初のコードはダメ、というのも不思議です。 

    明日は、この解明に取りかかります。

    みなさん、ご教示・ご忠告等、ありがとうございます! 今は、じっくり読む暇がないので、明日(19日)以降に読みます。

    解明の参考にします。 基本的に出来ることは確認できましたので、あとはStringBuilder方式コードでの欠陥探し、です。

    YKsaila

    2012年9月18日 15:47
  • こんにちは。

    そもそも、

    sql.CommandText = sb.ToString()

    この最終的に作られたSQL ステートメントの確認は終わっているのでしょうか?
    これが間違いないという事にならないと、なんとも・・・

    2012年9月19日 1:08
  • エラー表示は、下記のものがでます:

    'System.Data.SqlClient.SqlException' の初回例外が System.Data.dll で発生しました。
    'System.NullReferenceException' の初回例外が Main_Menu.exe で発生しました。

    'オブジェクト参照がオブジェクト インスタンスに設定されていません。</Message><StackTrace> Line: 515

    'DataGridViewの列表示順指定()
    With DataGridView1
    .Columns("ClientCode").DisplayIndex = 9 '***  Line515
     ----略

    最初のご質問でエラーの内容を示されていなかったので、おそらくエラーメッセージに着目されず、単にエラーが発生したという認識だけで終わっているということはないでしょうか? デバッグする上で、エラーの内容に着目することはとても大切です。どのような場合にSqlExceptionが発生し、どのような場合にNullReferenceExceptionが発生するのかがわかれば、エラーの発生原因をある程度絞ることができます。例えば、SQL文が誤っており、SQL Serverが処理できない場合は、SqlExceptionが発生します。逆に言えば、SqlExceptionが発生しなければ、SQL文はSQL Serverで処理されて結果が返ってきたが、それ以降のその結果を処理する部分に誤りがあるということです。結果が正しく返ってきていないかもしれませんし、結果が正しく返って来ていてもそれを扱うコードに誤りがあるのかもしれません。ですからこの場合、次に確認するのは正しい結果が返って来ているかということになります。このようにエラーメッセージをうまく活用し、どんどん消去法で問題を絞り込んでいきましょう。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/


    2012年9月19日 3:16
    モデレータ
  • yksaila さま よろしく。

    過去のご質問の「事前に用意された複数項目(20個)から ...」の時と同じではありませんか?

    私も含め、その時からの投稿者の方々は、まさかまた同じミスはない という前提で、今回、投稿なさっているようです。
    最初、組み立てた SQL文 に間違いがあるのでは と頭を過ぎりましたが、まさか、この前の件の直ぐ後で、チェックはしているだろうから、そんな事書くと失礼かな と止めました。

    ご提示になったコードではなく、それ以外の所でミスがあっては、正確な回答を得られる訳がありません。
    If 文等のロジック内に Bug があると思います。

    ( 組み上がった SQL文 を実行する直前に Label 等で画面に表示するよう書けば、あなたの嫌いな Debugger を触らずに済みます。 個人的には、デバック不足の感を否めません。 )

    次回以降は、感覚で質問文を作るのではなく、ちゃんと誤動作を再現できるブロックを別途作成し、それを全て投稿して下さい。
    これは、 Class を作る勉強にも繋がる筈ですから無駄ではないし、ご自分で間違いを探す良い機会にもなるでしょう。

    • 編集済み ShiroYuki_Mot 2012年9月19日 3:25 語句欠落を修正
    2012年9月19日 3:22
  • 投稿者の皆様へ

    Stringbuilder方式でも、うまくいきました。
    訪問者の方のために、成功した全コードを最後に添付します。
    エラーの原因の個所は、二か所でした。
    当然ながら、すべて私の間違いでした。
    Stringbuilder方式を疑ったのはきっかけに過ぎず、本気ではないです。きっかけが、私には必要でしたから。

    「'****」をその行(エラー個所:訂正後で正しくなっていますが)の前につけておきます。

    最初のエラーですが、Staff_IDの前に、T_SALES.は不要と勘違いしていました。
    Staff_IDは、T_Sales/T_M_Staffの両方にありますが、T_M_Staffの方は"From"以降のInner Joinの中だけなので
    Fromの前(sql文本体?)では、Staff_IDだけで良いだろうと思い込んでいたようです。
    考えてみれば、これではPCの方は、どちらのStaff_IDなの? と、迷ってしまうのでしょう。
    エラーも出ず、たしか静止したままでした(デバッグ不能??)。

    T_SALES.を加えればうまくいきます、外せばNOです。
    (但し、二個目のエラー個所が正常になっていることが条件ですが。)

    二個目のエラーは、すでにStaffName列はあるのに、作ろうとしています。
    これも、sql文では、T_M_Staff.StaffName とあるのに気付いていなかったのです。
    ここを削除してOKです。 復活させると、エラーです(エラー表示は、先の書き込み通り)。

    コード内に番号('1,'2,'3,'4,'5)をふり、順に削除したり復活させたりして点検しました。


    さて、これでうまくいったのですが(複数のテーブルを連結して一個のデータテーブル作成)、最初の問題は未解決です。
    所要時間です。今回も、一部で、For---Nextを使わざるを得ないので時間短縮は出来ていません。 
    むしろ、テーブル連結をしたので、1、2秒増えた気がします。

    連結前---約90,000件のデータで、13~14秒
    今回---- 約90,000件のデータで、14~15秒

    この時間は、待つには長すぎます。 せいぜい、2~4秒ではないでしょうか?
    90,000件のデータ数なら、仕方ないのでは? という者も家族にいますが、さてどうでしょう?
    実際のデータ数は、現実には20,000くらいでしょうが(これだと、2~3秒)、やはり余裕を持たせたいです。


    ここで、考察:
    Client_ID(顧客ID)---T_M_Client(顧客テーブル:別テーブル)にあり、ここから3件引き出しています、支店、会社名、顧客コードです。
    幸い、この3項目は一レコード内のものです。

    一方、
    Staff_ID(担当者ID)---T_M_Staffにあり、StaffNameはここに格納されています。
    T_Salesには、Staff_ID/Operate_Staff_IDの二個があり(両者とも、T_M_StaffのStaff_ID)、どちらもStaffNameを表示します。
    こちらは、一レコード内には無いですから、両方を連結することはできません。

    とすれば、データ連結を、一個のStaff_ID(テーブル、T_M_Staff) と Client_ID(つまり、テーブルT_M_Client)でして、
    もうひとつのStaff_ID だけ、For--Nextで回せば、時間短縮になるのでは? と、考えましたので、近日中にやってみます。
    報告はします。まだ、終わっていませんので。

    以上です、皆さまありがとうございました。
    あとから、書き込みをゆっくり読ませていただきます。

    YKsaila

    以下、コードです:

    '実行(検索)
        Private Sub CM_F9_Click(sender As System.Object, e As System.EventArgs) Handles CM_F9.Click
            'SQL認証接続
            Dim St As String
            Dim Con As New System.Data.SqlClient.SqlConnection
            Dim sql As System.Data.SqlClient.SqlCommand
            Dim ServerName As String = GC_SVRName   'サーバー名(またはIPアドレス)
            Dim UserID As String = GC_UserID      'ユーザーID
            Dim Password As String = GC_Password   'パスワード
            Dim DatabaseName As String = GC_DatabaseName
            St = "Server=" & ServerName & ";"
            St &= "User ID=" & UserID & ";"
            St &= "Password=" & Password & ";"
            St &= "Initial Catalog=" & DatabaseName
            Con.ConnectionString = St
            sql = Con.CreateCommand
            Con.Open()
            Dim adapter = New SqlClient.SqlDataAdapter()
            Dim ds = New DataSet
            Dim ds_client = New DataSet   'dtsalesに列追加時使用、T_M_Client
            Dim ds_staff = New DataSet    'dtsalesに列追加時使用、T_M_Staff
            Dim dtsales As New DataTable  'T_Salesのデータテーブル
            adapter.SelectCommand = sql

            'T_Salesから、dtsales作成
            Dim sb As New System.Text.StringBuilder
            Try
                'AND検索
                sb.Append("SELECT Sales_ID")  '0
                sb.Append(" , SYS_Update_Date")  '1
                sb.Append(" , TradeType")      '2
                sb.Append(" , SalesType")      '3
                sb.Append(" , ClientSalesNo")  '4
                sb.Append(" , Temp_SalesNo")   '5
                sb.Append(" , SalesDate")      '6
                sb.Append(" , SalesSumDate")   '7
                sb.Append(" , Client_ID")      '8
                sb.Append(" , ClientMemo")     '11
                '******下記の一行に問題あった(T_Sales.が必要)
                sb.Append(" , T_Sales.Staff_ID")   '12  ***(Staff_ID--T_Sales,T_M_Staffの両方にある、T_M_Staffの方のStaff_IDは使用しなくても必ず区別する。)
                sb.Append(" , SimpleMemo")     '13
                sb.Append(" , SalesAmount")    '14
                sb.Append(" , TaxAmount")      '15
                sb.Append(" , Discount")       '16
                sb.Append(" , TotalAmount")    '17
                sb.Append(" , CostAmount")     '18
                sb.Append(" , FixedAmount")    '19
                sb.Append(" , CloseDate")      '20
                sb.Append(" , Memo")           '21
                sb.Append(" , ReturnReason")   '22
                sb.Append(" , Operate_Staff_ID")  '23
                sb.Append(" , SYS_Computer_ID")   '24
                sb.Append(" , T_M_Staff.StaffName")  '23  ' ここでは、OperateStaffName!
                'sb.Append("  FROM T_Sales")
                sb.Append(" FROM T_Sales Inner Join T_M_Staff On T_Sales.Operate_Staff_ID = T_M_Staff.Staff_ID")

                If Not String.IsNullOrEmpty(Me.CB_E_TradeType.Text) Then
                    If sql.Parameters.Count = 0 Then
                        sb.Append(" WHERE ")
                    Else
                        sb.Append(" AND ")
                    End If
                    sb.Append("TradeType = @TradeType")
                    sql.Parameters.AddWithValue("@TradeType", Me.CB_E_TradeType.SelectedValue)
                End If

                If Not String.IsNullOrEmpty(Me.TX_E_ClientSalesNo.Text) Then
                    If sql.Parameters.Count = 0 Then
                        sb.Append(" WHERE ")
                    Else
                        sb.Append(" AND ")
                    End If
                    sb.Append("ClientSalesNo = @ClientSalesNo")
                    sql.Parameters.AddWithValue("@ClientSalesNo", Me.TX_E_ClientSalesNo.Text)
                End If

                If Not String.IsNullOrEmpty(Me.TX_E_Temp_SalesNo.Text) Then
                    If sql.Parameters.Count = 0 Then
                        sb.Append(" WHERE ")
                    Else
                        sb.Append(" AND ")
                    End If
                    sb.Append("Temp_SalesNo = @Temp_SalesNo")
                    sql.Parameters.AddWithValue("@Temp_SalesNo", Me.TX_E_Temp_SalesNo.Text)
                End If

                If Not String.IsNullOrEmpty(Me.TX_E_SalesDate.Text) Then
                    If sql.Parameters.Count = 0 Then
                        sb.Append(" WHERE ")
                    Else
                        sb.Append(" AND ")
                    End If
                    sb.Append("SalesDate = @SalesDate")
                    sql.Parameters.AddWithValue("@SalesDate", Me.TX_E_SalesDate.Text)
                End If

                If Not String.IsNullOrEmpty(Me.TX_E_SalesSumDate.Text) Then
                    If sql.Parameters.Count = 0 Then
                        sb.Append(" WHERE ")
                    Else
                        sb.Append(" AND ")
                    End If
                    sb.Append("SalesSumDate = @SalesSumDate")
                    sql.Parameters.AddWithValue("@SalesSumDate", Me.TX_E_SalesSumDate.Text)
                End If

                If Not String.IsNullOrEmpty(Me.CB_E_Client.Text) Then
                    If sql.Parameters.Count = 0 Then
                        sb.Append(" WHERE ")
                    Else
                        sb.Append(" AND ")
                    End If
                    sb.Append("Client_ID = @Client_ID")
                    sql.Parameters.AddWithValue("@Client_ID", Me.CB_E_Client.SelectedValue)
                End If

                If Not String.IsNullOrEmpty(Me.TX_E_ClientMemo.Text) Then
                    If sql.Parameters.Count = 0 Then
                        sb.Append(" WHERE ")
                    Else
                        sb.Append(" AND ")
                    End If
                    sb.Append("ClientMemo = @ClientMemo")
                    sql.Parameters.AddWithValue("@ClientMemo", "Me.TX_E_ClientMemo.Text")
                End If

                If Not String.IsNullOrEmpty(Me.CB_E_Staff_ID.Text) Then
                    If sql.Parameters.Count = 0 Then
                        sb.Append(" WHERE ")
                    Else
                        sb.Append(" AND ")
                    End If
                    sb.Append("Staff_ID = @Staff_ID")
                    sql.Parameters.AddWithValue("@Staff_ID", "Me.CB_E_Staff_ID.SelectedValue")
                End If

                If Not String.IsNullOrEmpty(Me.TX_E_CloseDate.Text) Then
                    If sql.Parameters.Count = 0 Then
                        sb.Append(" WHERE ")
                    Else
                        sb.Append(" AND ")
                    End If
                    sb.Append("CloseDate = @CloseDate")
                    sql.Parameters.AddWithValue("@CloseDate", "Me.TX_E_CloseDate.Text")
                End If

                If Not String.IsNullOrEmpty(Me.TX_E_Amount.Text) Then
                    If sql.Parameters.Count = 0 Then
                        sb.Append(" WHERE ")
                    Else
                        sb.Append(" AND ")
                    End If
                    sb.Append("SalesAmount = @SalesAmount")
                    sql.Parameters.AddWithValue("@SalesAmount", "Me.TX_E_Amount.Text")
                End If

                If Not String.IsNullOrEmpty(Me.TX_E_TotalAmount.Text) Then
                    If sql.Parameters.Count = 0 Then
                        sb.Append(" WHERE ")
                    Else
                        sb.Append(" AND ")
                    End If
                    sb.Append("TotalAmount = @TotalAmount")
                    sql.Parameters.AddWithValue("@TotalAmount", "Me.TX_E_TotalAmount.Text")
                End If

                If Not String.IsNullOrEmpty(Me.TX_E_SYS_Update_Date.Text) Then
                    If sql.Parameters.Count = 0 Then
                        sb.Append(" WHERE ")
                    Else
                        sb.Append(" AND ")
                    End If
                    sb.Append("SYS_Update_Date = @SYS_Update_Date")
                    sql.Parameters.AddWithValue("@SYS_Update_Date", "Me.TX_E_SYS_Update_Date.Text")
                End If

                If sql.Parameters.Count = 0 Then
                    sb.Append(" WHERE 1=0 ")

                End If
                sb.Append(" ORDER BY")
                sb.Append(" Sales_ID")

                sql.CommandText = sb.ToString()

                adapter.SelectCommand = sql
                adapter.Fill(ds)
                dtsales = ds.Tables(0)


                '以下、データテーブル(dtsales)に7列追加 (Profit,Profit_Rate,ShortName,BranchNo,ClientCode,StaffName,OperateStaffName)
                '5
                'dtsalesに、Profit列を追加
                Dim dc As DataColumn
                dc = New DataColumn("Profit", GetType(Integer))
                dc.Expression = "SalesAmount-CostAmount"   '---ここで、粗利(Profit)を計算!
                dtsales.Columns.Add(dc)  'Profit列

                'dtsalesに、Profit_Rate列を追加
                dc = New DataColumn("Profit_Rate", GetType(Decimal))
                dtsales.Columns.Add(dc)   'Profit_Rate列

                'ShortName列作成追加
                dc = New DataColumn("ShortName", GetType(String))
                dtsales.Columns.Add(dc)

                'BranchNo列作成追加
                dc = New DataColumn("BranchNo", GetType(Integer))
                dtsales.Columns.Add(dc)

                'ClientCode列作成追加
                dc = New DataColumn("ClientCode", GetType(String))
                dtsales.Columns.Add(dc)

                'E_StaffName列作成追加---以前のStaffName(営業担当者名) 。 
                'ここでは、都合上、OperateStaffName(事務担当、または入力担当者名)=StaffName としたので,変更する(追加)。
                dc = New DataColumn("E_StaffName", GetType(String))
                dtsales.Columns.Add(dc)

                '******下記の二行に問題あった---T_M_Staff.StaffNameですでに作成すみだから、ここは削除。
                'dc = New DataColumn("StaffName", GetType(String))
                'dtsales.Columns.Add(dc)

                'OperateStaffName列作成追加---上記の理由で、ここでは列追加不要。OperateStaffName(事務担当、または入力担当者名)=StaffNameとなるから。
                'dc = New DataColumn("OperateStaffName", GetType(String))
                'dtsales.Columns.Add(dc)

                'テーブル:T_M_Client ---ShortName,BranchNo,ClientCode用
                'Dim ds = New DataSet
                Dim sq As String
                sq = "SELECT T_M_Client.* FROM T_M_Client"
                adapter.SelectCommand = New SqlClient.SqlCommand(sq, Con)
                adapter.SelectCommand.CommandType = CommandType.Text
                adapter.Fill(ds_client)
                Dim dt_client As New DataTable
                dt_client = ds_client.Tables(0)
                Dim foundrows() As Data.DataRow

                'テーブル:T_M_Staff ----StaffName,OperateStaffName用
                'Dim ds = New DataSet
                'Dim sq As String
                sq = "SELECT T_M_Staff.* FROM T_M_Staff"
                adapter.SelectCommand = New SqlClient.SqlCommand(sq, Con)
                adapter.SelectCommand.CommandType = CommandType.Text
                adapter.Fill(ds_staff)
                Dim dt_staff As New DataTable
                dt_staff = ds_staff.Tables(0)


                For i = 0 To dtsales.Rows.Count - 1
                    '4'
                    '(*)粗利率(Profit_Rate)計算
                    '下記の二行のコードは、不成功のもの---SalesAmount(売上高・税抜き)が空白または「0」のとき、割り算出来ないから。
                    'dc.Expression = "((SalesAmount-CostAmount)/SalesAmount)*100"
                    'dtsales.Columns.Add(dc)   '----Profit_Rate列を追加計算
                    If CInt(dtsales.Rows(i).Item("SalesAmount").ToString) = 0 Or _
                        String.IsNullOrEmpty(dtsales.Rows(i).Item("SalesAmount").ToString) Then

                        dtsales.Rows(i).Item("Profit_Rate") = 0   'SalesAmount(売上)が、空白か0の場合

                    ElseIf CInt(dtsales.Rows(i).Item("Profit").ToString) < 0 Then
                        dtsales.Rows(i).Item("Profit_Rate") = -1   'Profit(粗利)がマイナスのとき
                    Else
                        '通常の粗利率計算
                        Dim a, b As Decimal
                        a = CInt(dtsales.Rows(i).Item("SalesAmount").ToString)
                        b = CInt(dtsales.Rows(i).Item("Profit").ToString)
                        dtsales.Rows(i).Item("Profit_Rate") = Format((b / a) * 100, "0.0")

                    End If

                    '1  
                    'ShortName/BranchNo/ClientCode列に、まとめてデータ代入(dtsalesに)
                    '(*)ShortName/BranchNo/ClientCode
                    Dim CLID, BRNo As Integer
                    Dim SHN, CLCD As String
                    CLID = CInt(dtsales.Rows(i).Item("Client_ID").ToString)
                    foundrows = dt_client.Select("Client_ID= " & CLID & "")  'T_M_Clientから、探す。

                    SHN = foundrows(0).Item("ShortName").ToString   '会社名
                    BRNo = CInt(foundrows(0).Item("BranchNo").ToString)   '支店コード
                    CLCD = foundrows(0).Item("ClientCode").ToString   '得意先コード 
                    '会社名
                    dtsales.Rows(i).Item("ShortName") = SHN
                    '支店コード
                    dtsales.Rows(i).Item("Branchno") = BRNo
                    'ClientCode
                    dtsales.Rows(i).Item("ClientCode") = CLCD


                    '3
                    '(*)E_StaffName列
                    If String.IsNullOrEmpty(dtsales.Rows(i).Item("Staff_ID").ToString) Then  'T_Sales(dtsales)の中のStaff_IDが、空白の場合があるので。
                        dtsales.Rows(i).Item("E_StaffName") = "入力なし"
                    Else
                        Dim STFID As Integer
                        Dim STN As String
                        STFID = CInt(dtsales.Rows(i).Item("Staff_ID").ToString)
                        foundrows = dt_staff.Select("Staff_ID= " & STFID & "")  'T_M_Staff(dt_staff)から、探す。

                        STN = foundrows(0).Item("StaffName").ToString
                        'Staff名
                        dtsales.Rows(i).Item("E_StaffName") = STN
                    End If

                    '今回は、以下の(2)不要---上のStaffNameをOperateStaffNameとしたから。
                    '2
                    '(*)OperateStaffName列
                    'Operate_Staff_IDが、空白の場合はないので(StaffNameの場合と異なる)以下でよい。
                    'Dim OP_STFID As Integer
                    'Dim OP_STN As String
                    'OP_STFID = CInt(dtsales.Rows(i).Item("Operate_Staff_ID").ToString)
                    'foundrows = dt_staff.Select("Staff_ID= " & OP_STFID & "")  'T_M_Staffから、探す。
                    'OP_STN = foundrows(0).Item("StaffName").ToString
                    ''OperateStaff名
                    'dtsales.Rows(i).Item("OperateStaffName") = OP_STN

                Next i


                '▼値の表示
                DataGridView1.DataSource = dtsales
                Me.TX_dataN.Text = CStr(dtsales.Rows.Count)  '検索データ数表示
                'Me.TX_dataN.Text = CStr(DataGridView1.Rows.Count-1) 'datagridviewのほうは、最後の空白行も数えるので、-1

                '列名(英語--サーバー)を日本語表記にして表示
                DataGridView1.Columns("Sales_ID").HeaderText = "ID"
                DataGridView1.Columns("SYS_Update_Date").HeaderText = "入力日付"
                DataGridView1.Columns("TradeType").HeaderText = "取引区分"
                DataGridView1.Columns("SalesType").HeaderText = "売上区分"
                DataGridView1.Columns("ClientSalesNo").HeaderText = "伝票番号"
                DataGridView1.Columns("Temp_SalesNo").HeaderText = "仮伝票番号"
                DataGridView1.Columns("SalesDate").HeaderText = "伝票日付"
                DataGridView1.Columns("SalesSumDate").HeaderText = "計上日付"
                DataGridView1.Columns("Client_ID").HeaderText = "得意先ID"
                DataGridView1.Columns("ShortName").HeaderText = "会社名"
                DataGridView1.Columns("Branchno").HeaderText = "支店コード"
                DataGridView1.Columns("ClientCode").HeaderText = "得意先コード"
                DataGridView1.Columns("ClientMemo").HeaderText = "得意先メモ"

                DataGridView1.Columns("Operate_Staff_ID").HeaderText = "事務担当者ID"
                DataGridView1.Columns("StaffName").HeaderText = "事務担当者"
                'DataGridView1.Columns("OperateStaffName").HeaderText = "入力担当者"
                DataGridView1.Columns("Staff_ID").HeaderText = "営業担当者ID"
                DataGridView1.Columns("E_StaffName").HeaderText = "営業担当者"

                DataGridView1.Columns("SimpleMemo").HeaderText = "売上簡易メモ"
                DataGridView1.Columns("SalesAmount").HeaderText = "売上金額(税抜)"
                DataGridView1.Columns("TaxAmount").HeaderText = "消費税額"
                DataGridView1.Columns("Discount").HeaderText = "値引額"
                DataGridView1.Columns("TotalAmount").HeaderText = "売上金額(税込)"
                DataGridView1.Columns("CostAmount").HeaderText = "原価"
                DataGridView1.Columns("FixedAmount").HeaderText = "卸金額"
                DataGridView1.Columns("Profit").HeaderText = "粗利額"
                DataGridView1.Columns("Profit_Rate").HeaderText = "粗利率"

                DataGridView1.Columns("CloseDate").HeaderText = "締日"
                DataGridView1.Columns("Memo").HeaderText = "備考"
                DataGridView1.Columns("ReturnReason").HeaderText = "返品理由"
                DataGridView1.Columns("SYS_Computer_ID").HeaderText = "コンピューターID"

                '通貨表示
                Me.DataGridView1.Columns("TotalAmount").DefaultCellStyle.Format = "c"
                Me.DataGridView1.Columns("TaxAmount").DefaultCellStyle.Format = "c"
                Me.DataGridView1.Columns("SalesAmount").DefaultCellStyle.Format = "c"
                Me.DataGridView1.Columns("CostAmount").DefaultCellStyle.Format = "c"
                Me.DataGridView1.Columns("Discount").DefaultCellStyle.Format = "c"
                Me.DataGridView1.Columns("FixedAmount").DefaultCellStyle.Format = "c"

            Catch ex As Exception
            End Try

            'DataGridViewの列移動を可能にする(その時の使用時のみ)---次に開くときは設定時(の順)に戻る。
            DataGridView1.AllowUserToOrderColumns = True

            'DataGridViewの列表示順指定()
            With DataGridView1
                '.Columns("Client_ID").Visible = False  '8
                .Columns("ClientCode").DisplayIndex = 9
                .Columns("ShortName").DisplayIndex = 10
                .Columns("BranchNo").DisplayIndex = 11
                .Columns("E_StaffName").DisplayIndex = 14
                .Columns("Operate_Staff_ID").DisplayIndex = 15
                .Columns("StaffName").DisplayIndex = 16
                .Columns("Profit").DisplayIndex = 23
                .Columns("Profit_Rate").DisplayIndex = 24
            End With

            Con.Close()

            '▼後処理
            dtsales.Dispose()
            adapter.Dispose()
            sql.Dispose()
            Con.Dispose()
        End Sub

     

     

     

    2012年9月19日 3:57
  • こんにちは。

    遅いのは、どこですか?

     adapter.Fill(ds_staff)
     Dim dt_staff As New DataTable
     dt_staff = ds_staff.Tables(0)

    このコードの
     Dim dt_staff As New DataTable
    部分に、ブレークポイントを設定すると、そこまで、何秒かかりますか?

    つまり、SQL文が成功して結果が撮り終わるまでが遅いのか、それとも、それ以降の処理が遅いのかという事になります。


    というか、

    いくつも突っ込みどころあるので、何から言って良いか・・・


    (1)SQL 文を作るところと、結果の処理を行う部分は分けた方が良い
    (2)DBのテーブルの各列の型を見直す。
      列の初期値も設定しておきましょう。
     できる限り、NULLを許容しないようにしましょう。 
    (3)Cintが無駄に発生しているように見える
     CInt(dtsales.Rows(i).Item("SalesAmount").ToString) = 0 
    こういうやつ。
    (4) WHERE AND は全部ANDにして最後に先頭のANDをWHEREに変えるとか、1=1みたいなのを入れておくとかでOKでは。
     いちいちパラメタカウント見るひつよなくなる。
    (5) dtsalesに、ほげ列を追加は、SQL文でできちゃうのでは?って気がする。
    (6) For ループ内での Dim はいくないはず。
    (7) T_M_Clientから、探して入れるのは最初のSQL文でできちゃうんじゃないかなぁ・・・
    (8) '列名(英語--サーバー)を日本語表記にして表示 および DataGridViewの列表示順指定
      これは、動的にやる意味あるのか謎。
    (9) やっぱビュー作った方がよさそう、というか全部ストアドプロシジャでよさそう
    (10)インデクス、チャント張ってるのかなぁ?という問題

    遅いのは、多分、無駄な型変換処理とか、データベースのインデックスが無いとかそんな理由じゃないかなと思います。

    2012年9月19日 4:13
  •  Keiichi Oumiさんへ

    投稿、ありがとうございます。

    >adapter.Fill(ds_staff)
    >Dim dt_staff As New DataTable
    >dt_staff = ds_staff.Tables(0)

    上記まで、約3~4秒です(約90,000件)。 90,000件だと、こんなものでは? それとも、遅いですか?

    >(1)・(9)

    これは、良く知らないのです。 これからの私の学習課題です。

    >(2)・(8)

    すでに使用中のもので、今では修正出来ないのです。 出来れば、いじらない。 必要とあらば、いずれしますが、後の問題です。

    (8)こそ、使用に当たっては重要です。列名が英語だったら、誰が使うのでしょうか?

    列の表示順にしても使う側にたって考えたいものです。

    >(3)

    こうしないと、受け付けてくれませんでした。

    >(4)・(5)・(6)---どうでしょうか? やってはみますが。

    >(7)

    そうです、出来ます。この上の私の書き込み内で書きましたが、3個のテーブル連結を試みました。 T_Salesに、T_M_Client/T_M_Staffの共通項を連携させる方法です。 思いのほか、あっという間に出来ました。

    しかし、時間短縮にはなりませんでした。 同じ15秒弱です。

    先の書き込みについては、この場をお借りして、報告します。 

    コードを載せようと思いましたが、簡単にできるので不要と判断しました。 結果は、時間短縮には不成功ということです。

    問題は、For--Nextにあります。 考えてみれば、当然でしょうね。 一個一個やるわけですから。

    先ほどのブレークポイントまで、3-4秒。 このFor -- Nextを削除すると、全体(90,000件)で3-4秒弱です。 これなら、まあ我慢できる範囲ですね。

    ですから、遅い原因は、このFor--Nextの使用にあるのです。

    現時点では、Profit_Rateを計算するのに、For--Next は欠かせません。

    Profit 計算では、列同士の計算でした(引き算)、だから速い!  Profit_Rate 計算では、分母にSalesAmount(売上高)がきます。

    ここ(SalesAmount)が、サーバーデータでは、空白だったりゼロだったりします。 これだと、割り算が出来ないわけで列同士の一括計算は、このままだと出来ません。 サーバーデータは、出来るだけいじらないのを原則として進めています。

    この場合、SalesAmount に強制的に値を入れて(1または-1を代入して)列同士の計算もやってみました。

    この方法は、避けたいのです。

    列の値が空白またはゼロの場合は列計算をやめ、そうでないときのみ列同士を計算する方法を考えるべきなのでしょう!

    どなたか、この方法(For--Next以外の方法)をご存じですか?

    YKsaila




    • 編集済み yksaila 2012年9月19日 12:58
    2012年9月19日 8:22
  • trapemiyaさんへ

    ありがとうございます。

    おっしゃる通りだと思います。

    まだ、エラー表示の内容が完全には理解できていないのでしょう。 でも、以前よりは、おかげさまで少し分かるようになりました。

    今回は、SQL文に誤りがあったのですが、未経験で、なかなか的が絞れずにいました。 結果、一個一個の消去法での探索となりました。 ま、最初は、こんなものかもしれません。 早く、皆さんが言うようなデバッグが迅速にできるようになりたいものです。

    YKsaila


    • 編集済み yksaila 2012年9月19日 13:00
    2012年9月19日 8:33
  • Jittaさんへ

    ありがとうございます。

    長すぎるといけないと思いまして、あのような書き込みになってしまいました。 反省します(毎度のこと?)。

    たしかに、自分では分かっていても(何時間も取り組んでいたのだから)、ほかの方には分かりにくいかもしれません。

    ほかの方が読んで問題点が理解できるように、落ち着いて判断しなければいけませんね。

    以後、気をつけます。

    デバッグの仕方、問題点の探し方、おかげさまで、ほんの少し進歩しました。 まだ、一合目ですが。

    YKsaila

    2012年9月19日 8:52
  • こんにちは。

    >(3)
    >こうしないと、受け付けてくれませんでした。

    何故、いったん文字列にして、その後キャストしないとならないのか、考えてみましたか?


    2012年9月19日 8:55
  • 遅い原因はご想像の通り、For--Nextです。しかもこの中でデータテーブルを操作しているため、その部分がかなりレスポンスの悪さに影響しているものと思われます。
    ざっとコードを読んだだけですが、おそらくSelect文一つで全て書けると思います。もし、無理でもストアドプロシージャで記述することにより、かなりレスポンスが良くなると思います。
    さて、一つのSelect文にしてしまう方法ですが、まず、case句を活用することを覚えられると良いと思います。例えば、where句の場合、以下のように書けます。

    where TradeType = case when @TradeType = '' then TradeType else @TradeType end

    TradeTypeが空白の場合、TradeType  = TradeType となりますから、Where句ではTradeType により絞り込みがないのと同じになります。

    同様に計算についても、SalesAmountの値で場合分けして計算すれば良いでしょう。

    また、会社名や支店コードなどは最初からテーブルをjoinするなどして取得してしまえば良いと思いますが、そのようにできない理由があるのでしょうか?

    SQL文は動作確認してからコードに落とすのが基本です。まず、SQL Server Management Studio等で動作するSQL文を作成してから、コード内に記述して下さい。
    しかし、本来はこのように項目が多かったり、抽出条件が多いようなものは、ストアドプロシージャとして作成するのがベターです。ストアドプロシージャ内ではSQL文をそのまま書けますから、読みにくくなるSQL文を文字列連結して組み立てる必要もありません。(ただし、文字列で組み立てなければならない場合もある。この場合も当然ながらSQLインジェクションに注意)

    SQLで抜いたものを加工せずに使う努力をすることが、まず最初にトライしなければならないことです。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/


    2012年9月19日 9:07
    モデレータ
  • trapemiyaさんへ

    ストアドプロシージャー、case句活用等は、未経験です。今から勉強します。

    >また、会社名や支店コードなどは最初からテーブルをjoinするなどして取得してしまえば良いと思いますが、そのようにできな>い理由があるのでしょうか?

    これは、すでにやりました。 掲示のコードは、データ連結等がVBでは初めてでしたので、簡単なものでまずやってみたのです。

    T_Sales/T_M_Staff/T_M_Client、3個のテーブルの共通項を連結したものを掲示しようかなと思ったのですが、すぐに簡単にできることなので不要と考え、やめました。 前のどこかの投稿で、この件について書いておきました。

    後半の内容も未経験なので挑戦してみます。 ネット・書籍等で、いろいろ調べながら勉強してみます。

    貴重な、ご忠告・ご教示、参考になりました。 ありがとうございます。 

    だいぶ、学習しなければならないことがあります、大変です。 でも、頑張ります!

    YKsaila

    2012年9月19日 13:22
  • trapemiyaさんへ

    ご教示ありがとうございます。 

    今後取り組むべき課題を明示していただき、感謝しています。 大変参考になります。 

    全課題に挑戦する予定ですが、まずは一歩一歩進みたいと思います。 現段階でやったことを報告します。問題なく出来たのですが、一個質問があります。 

    前回のコードを、下記の通りに修正しました:

    (1):SQL文に、下記コード追加(Profit列、Profit_Rate列をデータテーブル<dtsales>作成時に追加)
    sb.Append(" , case when T_Sales.SalesAmount=0 then 0 when T_Sales.CostAmount > T_Sales.SalesAmount then -1" _
                          & " else (T_Sales.SalesAmount-T_Sales.CostAmount)/T_Sales.SalesAmount*100 end as Profit_Rate")

    sb.Append(" , T_Sales.SalesAmount-T_Sales.CostAmount as Profit")

    (2)B:SQL文のFROM以下を変更---T_Salesに、二個のテーブル(T_M_Staff, T_M_Client)の共通項を連結
    sb.Append(" FROM T_M_Staff Inner Join (T_Sales Inner Join T_M_Client On T_M_Client.Client_ID = T_Sales.Client_ID )On T_Sales.Operate_Staff_ID = T_M_Staff.Staff_ID")

    (3)データテーブル(dtsales)作成後に、追加する列(Profit,Profit_Rate)は削除(前回のコードで書いたもの)--(1)で作成しますから。


    これで、少しは、うまくいきます(時間短縮にはなりません、1~2秒ほど短くはなりますが。 まだFor--Nextが一個残っていますので。)
    ただ、今回は、Datagridviewでの列表示(Profit,Profit_Rate)での数値表現が、少数点以下4桁まで表示します。

    この、列全体(Profit,Profit_Rate)の少数点以下の桁表示を、Profit_Rateは2桁(少数第二位)まで、Profitは0桁(整数表示)にしたいのですが、うまくいきません。 教えていただけますか?
    Format関数等は列全体では有効でないようなのです(個別ではできますが)。

    列全体で、少数表示桁数を一括で指定したいのです。

    追加の質問です:

    最終的には、FOR--NEXTの使用一個避けられないようなので(後述)、ストアドプロシージャ(または、ストアドファンクション)の利用になるような気がしますが、どうなのでしょうか? これは、当面の問題が解決しましたら、この学習に移行します。

    T_M_Staff からT_Salesには、二個連結があります。 T_M_Staffには、Staff_ID→StaffNameがあります。

    で、本体のT_Salesには、このStaff_IDが二個所で使用されています。 営業担当者(E_StaffName)と事務担当者(StaffName)です。この二者は、どちらもT_M_StaffのStaff_IDとつながっています。 当然、この二者は別人です。

    事務担当:T_SalesのOperate_Staff_ID(StaffName) ⇔T_M_StaffのStaff_ID--(T_M_StaffのStaffName表示)

    営業担当:T_SalesのStaff_ID(E_StaffName)⇔T_M_StaffのStaff_ID --  (T_M_StaffのStaffName表示)

    一個のSQL(Select)文に、これがが収まるでしょうか? この場合、T_M_Staff のStaff_IDを、T_Salesの異なる二個のデータへは連結できないと考えているのですが(自己矛盾が発生しますから)?

    よろしく、お願いします。

    YKsaila


    • 編集済み yksaila 2012年9月24日 15:38
    2012年9月24日 3:57
  • >Format関数等は列全体では有効でないようなのです(個別ではできますが)。

    >列全体で、少数表示桁数を一括で指定したいのです。

    列全体というのは、全ての行においてという意味でしょうか? であれば、以下で適切に書式指定子を指定すると小数点以下の桁が揃えられます。適切にとは、書式指定子でゼロプレースホルダーを使ってみて下さい。

    DataGridViewのセルに表示するテキストの書式(フォーマット)を指定する
    http://dobon.net/vb/dotnet/datagridview/format.html

    書式を指定して数値を文字列に変換する
    http://dobon.net/vb/dotnet/string/inttostring.html

    >この場合、T_M_Staff のStaff_IDを、T_Salesの異なる二個のデータへは連結できないと考えているのですが(自己矛盾が発生しますから)?

    できますよ。テーブルにエイリアスを使えば良いのです。例えば、

    select *, staff1.StaffName as E_StaffName, staff2.StaffName from T_Sales as sales
                inner join T_M_Staff as staff1 on staff1.Staff_ID = sales.Staff_ID
                inner join T_M_Staff as staff2 on staff2.Staff_ID = sales.Operate_Staff_ID

    その他、以下の方法でもできます。

    select *, (select StaffName from T_M_Staff  where Staff_ID = sales.Staff_ID) as E_StaffName,
                 (select StaffName from T_M_Staff  where Staff_ID = sales.Operate_Staff_ID) as StaffName
            from  T_Sales as sales

    #上記でasは省略できます。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/

    • 回答としてマーク yksaila 2012年9月24日 15:41
    2012年9月24日 8:40
    モデレータ
  • trapemiyaさんへ

    ご教示、ありがとうございます。

    (Ⅰ)>列全体で、少数表示桁数を一括で指定したいのです。

    これは、すでに私の掲示コードの中にありました。 なぜ、気付かなかったのでしょうか? 

    自分で探してきたものなのに、不思議です! 疲れていたのでしょうね、きっと。

    'DataGridViewの列表示指定
    Me.DataGridView1.Columns("Profit").DefaultCellStyle.Format = "c"          '(例):¥23,000と表示
    Me.DataGridView1.Columns("Profit_Rate").DefaultCellStyle.Format = "0.00"  ’少数第二位まで表示

    (Ⅱ)T_M_Staff のStaff_IDを、T_Salesの異なる二個のデータへ連結する方法について:

    >できますよ。テーブルにエイリアスを使えば良いのです。

    全く知りませんでした、大変参考になりました。 ありがとうございます。 以下、やったことを掲示します。

    Case (When---then---else---) End 句を、SQL文の中で使用しました。だいぶ、すっきりしました。
    下記の4個の方法を試しました。 それで、約90,000件のデータ表示の所要時間を調べてみました。

    方式(A,B):Select文の中に、全て収める。データテーブル作成後に列の追加をしない。For--Nextなし。

    方式(C,D):Select文の中に、ほとんどを収める。但し、一個だけ、データテーブル作成後に列を追加(E_StaffName)。 FOR NEXTあり。

    結果をまとめますと、INNER JOIN句が多くなると所要時間が増えるようです。 FOR -- NEXTの使用と同じくらい。

    方式B,Cが、よさそうです。 FOR NEXTの使用を回避する点と、コードがすっきりしている点から、Bが少し勝るのでしょうね。

    所要時間8秒は、まだ長いので、これからストアドプロシージャー(ストアドファンクション)に挑戦してみます。

    再度、有難うございました。 本当に、助かっています。

    YKsaila

    以下、参考コードです。 変更部分のみです。
    (A)21秒 -- INNER JOIN 3個使用
                sb.Append("SELECT sales.Sales_ID") 
                sb.Append(" , sales.SYS_Update_Date")
                -------
                ------
                sb.Append(" , case when sales.SalesAmount=0 then 0 when sales.CostAmount>sales.SalesAmount then -1" _
                       & " else (sales.SalesAmount- sales.CostAmount)/sales.SalesAmount*100 end as Profit_Rate")

                sb.Append(" , sales.SalesAmount-sales.CostAmount as Profit")
                ------
                sb.Append(" , staff2.StaffName as StaffName")   ' ここでは、OperateStaffName!
                sb.Append(" , Client.ClientCode as Clientcode") 
                sb.Append(" , Client.BranchNo as BranchNo")  
                sb.Append(" , Client.ShortName as ShortName") 
                sb.Append(" , staff1.StaffName as E_StaffName")
               
                sb.Append(" From T_Sales as sales")
                sb.Append(" Inner join T_M_Staff as staff1 on staff1.Staff_ID = sales.Staff_ID")
                sb.Append(" Inner join T_M_Staff as staff2 on staff2.Staff_ID = sales.Operate_Staff_ID")
                sb.Append(" Inner join T_M_Client as Client on Client.Client_ID = sales.Client_ID")


    (B)8秒--INNER JOIN なし
                sb.Append("SELECT sales.Sales_ID") 
                -------
                sb.Append(" , case when sales.SalesAmount=0 then 0 when sales.CostAmount>sales.SalesAmount then -1" _
                       & " else (sales.SalesAmount- sales.CostAmount)/sales.SalesAmount*100 end as Profit_Rate")

                sb.Append(" , sales.SalesAmount-sales.CostAmount as Profit")

          ------           
                sb.Append(" , (select StaffName from T_M_Staff where T_M_Staff.Staff_ID = sales.Operate_Staff_ID) as StaffName") ' ここでは、OperateStaffName!
                sb.Append(" , (select StaffName from T_M_Staff  where T_M_Staff.Staff_ID = sales.Staff_ID) as E_StaffName")

                sb.Append(" , (select ClientCode from T_M_Client where T_M_Client.Client_ID = sales.Client_ID) as Clientcode")
                sb.Append(" , (select BranchNo from T_M_Client where T_M_Client.Client_ID = sales.Client_ID) as BranchNo") 
                sb.Append(" , (select ShortName from T_M_Client  where T_M_Client.Client_ID = sales.Client_ID) as ShortName")
                sb.Append(" From T_Sales as sales")


    (C)8秒  For --- Next使用(E_StaffName) & INNER JOIN 2個
                sb.Append("SELECT sales.Sales_ID")  '0
                -------
                sb.Append(" , case when sales.SalesAmount=0 then 0 when sales.CostAmount>sales.SalesAmount then -1" _
                       & " else (sales.SalesAmount- sales.CostAmount)/sales.SalesAmount*100 end as Profit_Rate")

                sb.Append(" , sales.SalesAmount-sales.CostAmount as Profit")
          --------
                sb.Append(" , staff2.StaffName as StaffName")   ' ここでは、OperateStaffName!
                sb.Append(" , Client.ClientCode")
                sb.Append(" , Client.BranchNo") 
                sb.Append(" , Client.ShortName")

                'sb.Append(" , staff1.StaffName as E_StaffName")---ここは、For--Next使用
                 sb.Append(" FROM T_M_Staff as staff2 Inner Join (T_Sales as sales Inner Join T_M_Client as Client On Client.Client_ID = sales.Client_ID )On sales.Operate_Staff_ID = Staff2.Staff_ID")
     
    (D)11秒 For--Next使用(E_StaffName) & INNER JOIN 2個
          sb.Append("SELECT T_Sales.Sales_ID")
                ------
                sb.Append(" , case when T_Sales.SalesAmount=0 then 0 when T_Sales.CostAmount > T_Sales.SalesAmount then -1" _
                          & " else (T_Sales.SalesAmount-T_Sales.CostAmount)/T_Sales.SalesAmount*100 end as Profit_Rate")

                sb.Append(" , T_Sales.SalesAmount-T_Sales.CostAmount as Profit")
                -----
                sb.Append(" , T_M_Staff.StaffName")   ' ここでは、OperateStaffName!
                sb.Append(" , T_M_Client.ClientCode")
                sb.Append(" , T_M_Client.BranchNo")  
                sb.Append(" , T_M_Client.ShortName")
                'sb.Append("  FROM T_Sales")
                sb.Append(" FROM T_M_Staff Inner Join (T_Sales Inner Join T_M_Client On T_M_Client.Client_ID = T_Sales.Client_ID )On T_Sales.Operate_Staff_ID = T_M_Staff.Staff_ID")



    • 回答としてマーク yksaila 2012年9月24日 15:42
    • 編集済み yksaila 2012年9月24日 15:44
    2012年9月24日 15:37
  • trapemiyaさんへ

    あれから、再度いろいろやってみました。

    以下の方法で、90,000件--5秒に短縮しました。

    Inner Join 2回使用+(select StaffName from T_M_Staff  where T_M_Staff_ID = T_Sales.Staff_ID) as E_StaffName

    以下、変更したコードです。'****A,****Bを参照してください。

     '90,000件--5秒(二か所変更)  Inner Join 2回('***B) + '****A
                sb.Append("SELECT T_Sales.Sales_ID")  '0
                sb.Append(" , T_Sales.SYS_Update_Date")  '1
                ---------略
                sb.Append(" , case when T_Sales.SalesAmount=0 then 0 when T_Sales.CostAmount > T_Sales.SalesAmount then -1" _
                          & " else (T_Sales.SalesAmount-T_Sales.CostAmount)/T_Sales.SalesAmount*100 end as Profit_Rate")
                sb.Append(" , T_Sales.SalesAmount-T_Sales.CostAmount as Profit")
          ---------略
                sb.Append(" , T_M_Staff.StaffName")  '23  ' ここでは、OperateStaffName!

                '****A          
                sb.Append(" , (Select StaffName from T_M_Staff  where T_M_Staff.Staff_ID = T_Sales.Staff_ID) as E_StaffName")

                sb.Append(" , T_M_Client.ClientCode")  '23
                sb.Append(" , T_M_Client.BranchNo")   '24
                sb.Append(" , T_M_Client.ShortName")  '23

                '****B
                sb.Append(" FROM T_M_Staff Inner Join (T_Sales Inner Join T_M_Client On T_M_Client.Client_ID = T_Sales.Client_ID )On T_Sales.Operate_Staff_ID = T_M_Staff.Staff_ID")

        90,000件表示で、5秒---微妙な時間ですね。 我慢すべき時間でしょうか?

    ちなみに、私のPC環境(自宅)は、以下のようになっています:

    1.サーバー:MS Windows Server 2003 R2(SQL2003)  ---(冒頭のSQL2000 は書き間違いです。)

      CPU--2.60GHZ  メモリー(?)--1.00GBRAM

    2.クライアントPC:Win.7 Ultimate Service Pack1

        CPU--2.93GHZ(Intel Core i3 Cpu 530)      RAM(実装メモリ)--4.00GB(2.80GB使用可能)

      32ビット-オパレーティングシステム

      ハード構成--Win7用(97.6GB、内54.1GB空き)、Vista用(97.6GB、内41.2GB空き)、E(936GB,653GB空き)

    YKsaila     




    • 回答としてマーク yksaila 2012年9月25日 0:24
    • 編集済み yksaila 2012年9月25日 0:28
    2012年9月25日 0:01
  • >90,000件表示で、5秒---微妙な時間ですね。 我慢すべき時間でしょうか?

    ここから先はDataGridViewで表示するための処理に時間がかかっているような気がします。お手軽にはStopWatchクラスでところどころ時間を計測されてみてはいかがでしょうか?

    処理時間を正確に計測するには?[2.0のみ、C#、VB]
    http://www.atmarkit.co.jp/fdotnet/dotnettips/412stopwatch/stopwatch.html

    DataGridViewのパフォーマンスを上げるには以下が参考になります。

    Windows フォーム DataGridView コントロールでのパフォーマンス チューニング
    http://msdn.microsoft.com/ja-jp/library/ms171621(VS.80).aspx

    しかし、9万件も表示してユーザーが本当に目を通せるのでしょうか? 私は大抵表示する際の上限を決め、それを超えないように抽出条件をユーザーに指定させるようにしています。
    また、どうしても9万件を一覧表示する必要がある場合は、DataGridViewではなく、他のコントロールで試されるとよいかもしれません。ただ、今は9万件ですが、将来データが増えるに従ってレスポンスがどんどん悪くなるわけですよね?やはり、現実的には抽出条件を指定させて件数を絞るのが良いと思います。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/

    2012年9月25日 0:42
    モデレータ
  • trapemiyaさんへ

    返信、ありがとうございます。

    処理時間・DatagridViewについては、あとで(数日後)やってみます。 少し、休憩が必要みたいです。 

    90,000件は学習のためにやっています。 

    作成当初、実用上は問題ないような微妙な速度だったのですが、”まてよ? この速度で、よいのかなあ?”、と思ったのです。

    で、90,000件のデータで実験したのです、するとちょっと時間がかかりすぎ、と判断しました。 

    やって、良かったです。

    おかげさまで、現時点で、実用上問題が全くない速度になったと思います。 それに、勉強にもなりました。

    実際は、おっしゃる通り抽出条件がすでに決められており(または、使用者が決めます)、数千件です。 

    これでも多すぎるくらいで、数百件が良いところではないでしょうか? 数百件でも目を通すのは大変なので、さらに絞り込みを使用者が任意に出来るようにします。

    ご心配をおかけして、すいません。

    YKsaila

    2012年9月25日 1:52
  • 一つ忘れてました。SQLの実行に関しては、SQL Server Management Studioのクエリというメニューで、実行プランが確認できますから、そこでSQLの実行にどれぐらいのコストがかかるのかを確認することができます。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/

    2012年9月25日 2:12
    モデレータ
  • 一つ忘れてました。SQLの実行に関しては、SQL Server Management Studioのクエリというメニューで、実行プランが確認できますから、そこでSQLの実行にどれぐらいのコストがかかるのかを確認することができます。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/

    trapemiyaさんへ

    ありがとうございます。

    あれから、所要時間を測定してみました。 
    結果から見ますと、結局サーバーの能力(または、受け取るクライアントPC の能力)のようです。

    したがって、所要時間に関しては限界のようですね。 違うSQL文があれば、別ですが。 

    おかげさまで、次の段階へ進めます。

    実験結果:異なるデータ件数での所要時間
    A--88,000件
    B--13,000件
    C--1,700件

    1.サーバー → データテーブル(dtsales)
     A: 3.72 ~ 3.84sec
     B: 0.62sec
     C: 0.16sec

    2.データテーブル(dtsales)→ DataGridView
     A: 0.48sec
     B: 0.09sec
     C: 0.037sec

    3.DataGridViewでの、列表示順指定・列名を日本語に変更
     A/B/C: 0.06sec 当然、所要時間は同じでした。

    YK


    • 編集済み yksaila 2012年9月27日 10:01
    2012年9月27日 9:46