こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

締切り済みの質問

2次元のdictionary

こんにちは。

recordsetの結果をDictionaryにいれたいと思っています。
環境はWindows Vista、vbScriptで書いています。

set dc = createobject("scripting.dictionary")
Set rs = Server.CreateObject("ADODB.Recordset")
rs.open (sql文), con 

for i = 0 to rs.recordcount -1
for j = 0 to rs.fields.count - 1
dc.add rs.fields(j).name, rs.fields(j).value
next
next

ちょっとイメージっぽく書きましたが(このままではエラーでます)、要は複数のフィールドを持つ複数のレコードを入れられないかということです。

色々調べたりしてみたのですがわかりません。
できないのかな?と思いました。

もしできるなら書き方をご教授頂けたらと思います。
よろしくお願いします。

投稿日時 - 2009-10-20 21:23:10

QNo.5383174

すぐに回答ほしいです

このQ&Aは役に立ちましたか?

16人が「このQ&Aが役に立った」と投票しています

回答(8)

ANo.8

>>dc(i) = rs.GetRows(1, adBookmarkCurrent)
>これが理想なのですが、dc(i)という書き方はエラーになってしまいます。
>オブジェクトにindexがつけられればいいのですが。

"C:\temp\test.xls"のSheet1に適当な65535件のデータでテストしたコードを載せておきます。
特にエラーは出ないので、データもしくは環境のせいでしょうか。

>dc(i) = rs.GetRows(1, adBookmarkCurrent)
とは、
dc.Add i, rs.GetRows(1, adBookmarkCurrent)
と一緒で、i を index としています。

Const adBookmarkCurrent = 0
Const adOpenStatic = 3
Const wkName = "C:\temp\test.xls"
Dim con
Dim rs
Dim sql
Dim dc
Dim i

'On Error Resume Next
Set con = CreateObject("ADODB.Connection")
With con
  .Provider = "Microsoft.Jet.OLEDB.4.0"
  .Properties("Extended Properties") = "Excel 8.0"
  .Properties("Data Source") = wkName
  .Open
End With
sql = "SELECT * FROM [Sheet1$]"
Set rs = CreateObject("ADODB.Recordset")
Set dc = CreateObject("scripting.dictionary")
rs.Open sql, con, adOpenStatic
Stop
For i = 1 To rs.RecordCount
  dc(i) = rs.GetRows(1, adBookmarkCurrent)
Next
rs.Close
con.Close
Stop
MsgBox dc(1)(0, 0) & vbTab & dc(1)(1, 0) & vbTab & dc(1)(2, 0)
MsgBox dc(2)(0, 0) & vbTab & dc(2)(1, 0) & vbTab & dc(2)(2, 0)
MsgBox dc(dc.Count)(0, 0) & vbTab & dc(dc.Count)(1, 0) & vbTab & dc(dc.Count)(2, 0)

Set dc = Nothing
Set rs = Nothing
Set con = Nothing
If Err.Number <> 0 Then
  MsgBox Err.Number & vbLf & Err.Description
Else
  MsgBox "end"
End If

#修正なしでVBAでも試せます。

投稿日時 - 2009-10-22 17:59:33

ANo.7

いろいろ回答はあるけれど、結局どのようなデータを持ってどのように取り出すかがわからないとうろうろするばかりではないでしょうか。
私はキーを連結して1つのキーにしてしまえばと書いたけど
取り出すときに取り出すキーが二つともわかっていればこれでいいでしょう。
でも最初のキーで取り出したデータの中に2番目のキーがあるような場合はお手上げです。
正直連想配列の必要も無い場合もあるのではないかとも思っています。
どのようなデータを「どのように取り出したいか提示してもらえると回答も正解に塚づくのではないでしょうか、

投稿日時 - 2009-10-22 17:03:24

ANo.6

Recordsetの結果をDictionaryに入れる時、何か別のkeyでレコード単位で入れるのですか?
それともfields.Nameをkeyにしてフィールド単位で入れるのですか?

GetRowsを使ってレコード単位で入れるなら
Const adBookmarkCurrent = 0
Const adOpenStatic = 3
':
rs.Open sql, con, adOpenStatic
Stop
For i = 1 To rs.RecordCount
  dc(i) = rs.GetRows(1, adBookmarkCurrent)
Next
rs.Close
con.Close
Stop
MsgBox dc(1)(0, 0) & vbTab & dc(1)(1, 0) & vbTab & dc(1)(2, 0)
MsgBox dc(2)(0, 0) & vbTab & dc(2)(1, 0) & vbTab & dc(2)(2, 0)
こんな感じになるかと。

フィールド単位で、LoopでField別に配列を作ってもそんなに時間かかりますか?
Dim v, w, x
':
rs.Open sql, con
Stop
v = rs.GetRows
ReDim w(UBound(v, 2))
For i = 0 To UBound(v, 1)
  For j = 0 To UBound(v, 2)
    w(j) = v(i, j)
  Next
  dc(rs.fields(i).Name) = w
Next
rs.Close
con.Close
Stop
x = dc.keys
MsgBox x(0) & vbTab & dc(x(0))(0) & vbTab & dc(x(0))(1) & vbTab & dc(x(0))(2)
MsgBox x(1) & vbTab & dc(x(1))(0) & vbTab & dc(x(1))(1) & vbTab & dc(x(1))(2)

Dictionaryに格納した後どうやって利用するかなのだと思いますが、
v = rs.GetRows
ただ単に配列で保持しておくっていう意味じゃないんですよね?

投稿日時 - 2009-10-22 15:12:43

補足

お返事ありがとうございます。

>dc(i) = rs.GetRows(1, adBookmarkCurrent)
これが理想なのですが、dc(i)という書き方はエラーになってしまいます。
オブジェクトにindexがつけられればいいのですが。

>フィールド単位で、LoopでField別に配列を作ってもそんなに時間かかりますか?
処理件数によっては遅くなるのでは?という古い考えがありました;

経験言語の違う数名で話していたのですが、たとえばPHP経験者は連想配列で渡してもらいたい、他の者はオブジェクトで渡すべきだ(recordsetかDictionary)、などと色々意見がでまして。

私はVB経験者としてrecordsetかgetrowsでの配列渡しを提案したのですが、Rubyなどのように1行でできないか?と言われまして試行錯誤していました。
(つまり共通ライブラリの関数にsqlを渡すと接続・実行・切断を行って結果をもらいたい、と)
recordsetはcloneが作れますが、さすがにコネクト切断すると中身も失われてしまいます。

格納後の処理の利便性ではなく、そういった経緯で悩んでいました。

投稿日時 - 2009-10-22 15:47:17

ANo.5

#2&3です。
VBSでcollectionは使えないのですね、失礼しました。それでは、下記の様なのは如何でしょうか。安定して動くかどうかは定かでありません。珍奇な思いつきかも...
Sub test3()
Dim dic As Object, tempDic As Object
Dim i As Long

Set dic = CreateObject("Scripting.Dictionary")
For i = 1 To 2
Set tempDic = CreateObject("Scripting.Dictionary")
tempDic.Add "field_a", i
tempDic.Add "field_b", "code" & Format(i, "00")
dic.Add CStr(i), tempDic
Set tempDic = Nothing
Next i
For i = 1 To 2
Debug.Print dic.Item(CStr(i)).Item("field_a")
Debug.Print dic.Item(CStr(i)).Item("field_b")
Next i
End Sub
#2が二次元配列で可能かどうかはお試し下さい。

投稿日時 - 2009-10-21 20:25:48

ANo.4

普通と違うやり方をしたいなら、この前に、何がしたいのか質問に書くべきでしょう。アイデアが横道にそれていませんか。
質問者の珍奇な思いつきに、つき合わされるのはかなわない。
他に普通のやり方があるのではないか。

投稿日時 - 2009-10-21 09:27:04

補足

お返事ありがとうございます。

目的は、1つの関数でOpen Find Closeをしてデータを返せないか?というものです。
recordsetで返すと、その後別処理でCloseしないといけないので、
dc = Find(sql)
みたいな感じで1行でできないかと思いました。

投稿日時 - 2009-10-21 11:33:30

ANo.3

#2です。ちょっとピント外れでしたか。
下記の様なのは如何でしょうか。"a","b"のところをフィールド名にします。これなら、異なる型のフィールドのデータが入れられます。
Sub test2()
Dim dic As Object
Dim myKey As String
Dim tempCollection As Collection

Set dic = CreateObject("Scripting.Dictionary")
Set tempCollection = New Collection
myKey = "1"
tempCollection.Add Item:=123, key:="a"
tempCollection.Add Item:="ABC", key:="b"
dic.Add myKey, tempCollection
'dictionaryにセット後、消してしまっても、dictionaryの中味は保持されている
Set tempCollection = Nothing
Debug.Print dic.Item(myKey)("a")
Debug.Print dic.Item(myKey)("b")
End Sub

投稿日時 - 2009-10-20 23:53:10

お礼

お返事ありがとうございます。

Collectionを調べてみたのですが、VBScriptではCollectionが使えないようです;;

参考になりました。ありがとうございます!

投稿日時 - 2009-10-21 11:40:33

ANo.2

dictionaryに配列を入れるコードをみたことがあります。
試しにやってみたら、下記の通りできました。VBAですが、ご参考まで。
Sub test()
Dim dic As Object
Dim myKey As String
Dim i As Long

Set dic = CreateObject("Scripting.Dictionary")
myKey = "1"
dic.Add myKey, Array(1, 2, 3, 4, 5)
For i = 0 To UBound(dic.Item(myKey))
Debug.Print dic.Item(myKey)(i)
Next i
End Sub

投稿日時 - 2009-10-20 23:35:21

補足

お返事ありがとうございます。

dic.Add myKey, Array(1, 2, 3, 4, 5)
のArrayは1次元配列じゃないとやっぱりだめでしょうか?
Getrowsを指定できれば使えるな~と思いました。

もし1次元配列で指定しなければいけないのなら、ループでField別に配列を作らないといけないので、処理時間をくってしまいそうなので、おとなしくrecordsetにしようと思います。

投稿日時 - 2009-10-21 11:41:30

ANo.1

どのように取り出すのかが分かりませんが、二つのfieldを連結したものをキーにすれば一応連想配列はできると思います。

投稿日時 - 2009-10-20 22:15:39

補足

お返事ありがとうございます。

>二つのfieldを連結したものをキー
というのは具体的にはどういった手法なのでしょうか?

投稿日時 - 2009-10-22 16:43:40

あなたにオススメの質問