鮎を食べるのは楽じゃない、その1~STAとMTAの話~
http://blogs.wankuma.com/esten/archive/2008/01/23/118945.aspx
の続き。前回はSTA鵜匠さんとMTA鵜匠さんに「この川全部の鮎」を注文しました。つまりこれは、「SELECT * FROM 川 where 魚 = 鮎」というSQL文をADOに処理してもらい、その結果を取得するようにした、と考える事ができます。ここで「あれ?これって本当にちゃんと鮎全部をとってこれんの??」と思った人、鋭い(笑)。そこで、ちょっと話は戻って、「SELECT * FROM テーブル というSQL文をADOに処理させる」という部分、つまり、「川全部の鮎を捕まえる為に鵜に何をさせるのか」についてここでは考えてみることにします。
SELECT * FROM テーブル Where 条件
誰もが一度は書くSQLではないでしょうか? あるテーブルから条件にあうレコードを全部取ってくるSQLです。このSQL文を処理するには、大きく分けて4つの方法があります。以下、DBをSQLServerとしてのサンプルコードです。
Adapterによる取得(同期処理)
(鵜匠一人に鵜が一羽。鵜が仕事に行くと鵜匠は戻ってくるまでずっと待つ)
Public Sub UKAI_STA_Adapter()
'リターン値の初期化
Dim wGetDataTable As DataTable
'内部ワーク
Dim wParamValueArray() As String = {}
'SQL文処理開始
Using wSQLCommand As SqlCommand = New SqlConnection(StrConnectionString).CreateCommand
wSQLCommand.Connection.Open()
wSQLCommand.CommandText = "SELECT * FROM 川 WHERE 魚 = 鮎"
Using wSQLAdapter As SqlDataAdapter = New SqlDataAdapter
wGetDataTable = New DataTable
wSQLAdapter.SelectCommand = wSQLCommand
wSQLAdapter.Fill(wGetDataTable)
End Using
wSQLCommand.Connection.Close()
End Using
End Sub
Readerによる取得(同期処理)
(鵜匠一人に鵜が一羽。鵜が仕事に行くと鵜匠は戻ってくるまでずっと待つ)
Public Sub UKAI_STA_Reader()
Dim wGetDataTable As DataTable = New DataTable
Using wSQLCommand As SqlCommand = New SqlConnection(StrConnectionString).CreateCommand
wSQLCommand.Connection.Open()
wSQLCommand.CommandText = "SELECT * FROM 川 WHERE 魚 = 鮎"
Using wSQLReader As SqlDataReader = wSQLCommand.ExecuteReader()
While (wSQLReader.Read())
wGetDataTable.NewRow()
wSQLReader.GetValues(wGetDataTable.Rows(wGetDataTable.Rows.Count - 1).ItemArray)
End While
wGetDataTable.AcceptChanges()
wSQLReader.Close()
End Using
wSQLCommand.Connection.Close()
End Using
End Sub
BeginExecuteReader~EndExecuteReaderによる取得(非同期コールバック)
(鵜匠一人に鵜が一羽。鵜が仕事に行くと鵜匠はその間別の仕事をする)
Public Sub UKAI_STA_CallBackReader()
Dim wAsyncHandle As IAsyncResult
Dim wChildCommand As SqlCommand
Dim wCallBack As New AsyncCallback(AddressOf GetResult)
wChildCommand = New SqlConnection(StrConnectionString).CreateCommand
wChildCommand.CommandText = String.Format("SELECT * FROM 川 WHERE 魚 = 鮎")
wChildCommand.Connection.Open()
wAsyncHandle = wChildCommand.BeginExecuteReader(wCallBack, wChildCommand, CommandBehavior.CloseConnection)
End Sub
Protected Sub GetResult(ByVal pAsyncHandle As IAsyncResult)
Dim wGetDataTable As DataTable = New DataTable
Using wGetCommand As SqlCommand = CType(pAsyncHandle.AsyncState, SqlCommand)
Using wGetResult As SqlDataReader = wGetCommand.EndExecuteReader(pAsyncHandle)
While (wGetResult.Read)
wGetDataTable.NewRow()
wGetResult.GetValues(wGetDataTable.Rows(wGetDataTable.Rows.Count - 1).ItemArray)
End While
wGetDataTable.AcceptChanges()
wGetResult.Close()
End Using
End Using
End Sub
BeginExecuteReader~EndExecuteReaderによる取得(STA非同期)
(鵜匠一人に鵜が10羽。鵜が仕事に行くと鵜匠はその間別の仕事をし、鵜は仕事を終えたものから順番に船に戻ってくる)
Public Sub UKAI_STA_CallBackReader_WithWait()
Dim wAsyncHandle(10) As IAsyncResult
Dim wHandles(10) As Threading.WaitHandle
Dim wChildCommand(10) As SqlCommand
For j As Integer = LBound(wChildCommand) To UBound(wChildCommand)
wChildCommand(j) = New SqlConnection(StrConnectionString).CreateCommand
wChildCommand(j).CommandText = String.Format("SELECT * FROM 川 WHERE 魚 = 鮎")
wChildCommand(j).Connection.Open()
wAsyncHandle(j) = wChildCommand(j).BeginExecuteReader(Nothing, wChildCommand(j), CommandBehavior.CloseConnection)
wHandles(j) = wAsyncHandle(j).AsyncWaitHandle
Next
Dim wGetDataTable As DataTable = New DataTable
Dim sPos As Integer = 0
For j As Integer = LBound(wHandles) To UBound(wHandles)
sPos = Threading.WaitHandle.WaitAny(wHandles)
Using wGetCommand As SqlCommand = CType(wAsyncHandle(sPos).AsyncState, SqlCommand)
Using wGetResult As SqlDataReader = wGetCommand.EndExecuteReader(wAsyncHandle(sPos))
While (wGetResult.Read)
wGetDataTable.NewRow()
wGetResult.GetValues(wGetDataTable.Rows(wGetDataTable.Rows.Count - 1).ItemArray)
End While
wGetDataTable.AcceptChanges()
wGetResult.Close()
End Using
End Using
Next
End Sub
BeginExecuteReader~EndExecuteReaderによる取得(MTA非同期)
(鵜匠分身10人に鵜が10羽。鵜が仕事に行くと鵜匠はその間別の仕事をし、鵜の仕事が終わったら分身が仕事をして本体へ帰還)
Public Sub UKAI_MTA_CallBackReader_ManyWait()
Dim wAsyncHandle(10) As IAsyncResult
Dim wHandles(10) As Threading.WaitHandle
Dim wChildCommand(10) As SqlCommand
Dim wCallBack As New AsyncCallback(AddressOf GetResult)
For j As Integer = LBound(wChildCommand) To UBound(wChildCommand)
wChildCommand(j) = New SqlConnection(StrConnectionString).CreateCommand
wChildCommand(j).CommandText = String.Format("SELECT * FROM 川 WHERE 魚 = 鮎")
wChildCommand(j).Connection.Open()
wAsyncHandle(j) = wChildCommand(j).BeginExecuteReader(wCallBack, wChildCommand(j), CommandBehavior.CloseConnection)
wHandles(j) = wAsyncHandle(j).AsyncWaitHandle
Next
Threading.WaitHandle.WaitAll(wHandles)
End Sub
Protected Sub GetResult(ByVal pAsyncHandle As IAsyncResult)
Dim wGetDataTable As DataTable = New DataTable
Using wGetCommand As SqlCommand = CType(pAsyncHandle.AsyncState, SqlCommand)
Using wGetResult As SqlDataReader = wGetCommand.EndExecuteReader(pAsyncHandle)
While (wGetResult.Read)
wGetDataTable.NewRow()
wGetResult.GetValues(wGetDataTable.Rows(wGetDataTable.Rows.Count - 1).ItemArray)
End While
wGetDataTable.AcceptChanges()
wGetResult.Close()
End Using
End Using
End Sub
と、ずらーっとサンプル列挙。とりあえず、全部、「川から鮎を取る」という仕事をさせるコードです。こんなにやり方がいろいろとあるわけで……。でもちょっとタンマ。これらのサンプルのうち、本当にほしい動作をしてくれるのは最初の3つだけです。残りの2つ、非同期の処理にはサーバー上でのテーブルロック問題とデータ取得の論理的な問題の二つがあります。
というわけで、続きはまた~