- ベストアンサー
Accessでの抽出方法を教えてください。
- みんなの回答 (12)
- 専門家の回答
質問者が選んだベストアンサー
【確認とVBの提示】 以下のVBコードを書いて実行すると添付図のような結果を得ました。このようなVBコードで宜しいのかどうかは判りませんので、コード中で使用している関数の紹介は割愛します。 なお、元テーブルと同じ構造のテーブルを別途用意して、それに結果をINSERTしています。また、テストテーブルの列名は以下のようです。 ・No.--->ID ・個有名--->name ・属名------>family Private Sub コマンド0_Click() Dim H As Integer Dim I As Integer Dim N As Integer Dim M As Integer Dim intTotal As Integer Dim strDatas(1) As String ' DBSelect()の戻り値を格納する配列 Dim strNames() As String ' 列[name]の値を格納する配列 Dim strFamilies() As String ' 列[family]の値を格納する配列 Dim strSQL As String ' SQL文をセットする変数 DoCmd.SetWarnings (False) ' ---------------------------------------- ' name 一覧を取得する ' ---------------------------------------- strSQL = "SELECT name FROM FamilyList ORDER BY ID" strDatas(0) = DBSelect(strSQL, ",", ",") strSQL = "SELECT family FROM FamilyList ORDER BY ID" strDatas(1) = DBSelect(strSQL, ",", ",") If Len(strDatas(0) & "") Then strNames() = Split(PackList(strDatas(0) & "," & strDatas(1)), ",") Else strNames() = Split(PackList(strDatas(1)), ",") End If ' ---------------------------------------- ' [FamilyList_ALL] をクリアする ' ---------------------------------------- DoCmd.RunSQL "DELETE FROM FamilyList_ALL" ' -------------------------------------------------------- ' テーブル[FamilyList_ALL]に固有名と属名を挿入する ' -------------------------------------------------------- intTotal = UBound(strNames) For I = 0 To intTotal ' ' name に対応する主属名と副属名を取得し配列に代入 ' strFamilies() = Split(GetFamilies(strNames(I)), ",") ' ' テーブル[FamilyList_ALL]の現在の行数を取得 ' N = DBLookup("SELECT COUNT(*) FROM FamilyList_ALL") ' ' For-Next ' M = UBound(strFamilies) For H = 0 To M ' ' テーブル[FamilyList_ALL]に'固有名'対'属名'関係を登録 ' ' 注意1: <個有名=属名> も発生する! ' If Len(strFamilies(H) & "") And strNames(I) <> strFamilies(H) Then N = N + 1 strSQL = "INSERT INTO FamilyList_ALL (ID,name,family)" & _ " VALUES (" & N & ",'" & strNames(I) & "','" & strFamilies(H) & "');" DoCmd.RunSQL strSQL End If Next H Next I DoCmd.SetWarnings (True) End Sub
その他の回答 (11)
- 山田 太郎(@f_a_007)
- ベストアンサー率20% (955/4574)
********************************** 新補足3:≪GetFamilies()≫ ********************************** Public Function GetFamilies(ByVal strStartName As String) As String Dim ExitNow As Boolean Dim N As Integer Dim I As Integer Dim S As Integer Dim T As Integer Dim strINList(1) As String Dim strSQL As String Dim strDatas As String Dim strFamily As String Dim strFamilies As String Dim strName As String Dim strNames As String ' ======================== ' 準備処理 ' ======================== strNames = strStartName strFamilies = strStartName ' ======================== ' メイン ' ======================== Do ' ' ExitNowの初期値を設定する ' ExitNow = True ' ********************************************************************* ' ' name に対応する family を検索・取得する ' ' 【SQL文】NOT IN 句を利用して既知の family の取得を回避する ' ' SELECT DISTINCT family FROM テーブル名 ' WHERE name='XXXXX' ' AND family NOT IN('AAA','BBB',CCC') ' ' ********************************************************************* N = CharCount(strNames, ",") For I = S To N strName = CutStr(strNames, ",", I + 1) strINList(0) = "'" & Replace(strFamilies, ",", "','") & "'" strSQL = "SELECT DISTINCT family FROM familyList" & _ " WHERE name='" & strName & "'" & _ " AND family NOT IN (" & strINList(0) & ")" ' ' SQL文を実行して合致する[family]データを取得する ' strDatas = DBSelect(strSQL, ",", ",") If Len(strDatas & "") > 0 Then ExitNow = False strFamilies = PackList(strFamilies & "," & strDatas) Else If strFamilies = strName Then ExitNow = False Else ' ' name に対応する family がヒットしなくても ' name に対応する name が存在するケースがある ' その場合には、name を新 family と見做す ' ' (例) 4 AAAA BBBB <---- ヒットしない ' 5 BBBB CCCC <---- 検索済み ' ' AAAA <---- 新 family と見做す ' strSQL = "SELECT name FROM familyList WHERE family='" & strName & "'" strDatas = DBLookup(strSQL) & "" If Len(strDatas & "") > 0 Then ExitNow = False strFamilies = PackList(strFamilies & "," & strDatas) End If End If End If Next I ' ' 次回は、今回N番目の次から ' S = N + 1 ' ********************************************************************* ' ' strFamily に対応する name を検索・取得する ' ' 【SQL文】NOT IN 句を利用して既知の name の取得を回避する ' ' SELECT DISTINCT name FROM テーブル名 ' WHERE family='XXXXX' ' AND name NOT IN('AAA','BBB',CCC') ' ' ********************************************************************* If Not ExitNow Then N = CharCount(strFamilies, ",") For I = T To N ' ' 属名リストから個別名を抽出する ' strFamily = CutStr(strFamilies, ",", I + 1) ' ' SQL文を作成する ' strINList(1) = "'" & Replace(strNames, ",", "','") & "'" strSQL = "SELECT DISTINCT name FROM familyList" & _ " WHERE family='" & strFamily & "'" & _ " AND name NOT IN (" & strINList(1) & ")" ' ' SQL文を実行して合致する[name]データを取得する ' strDatas = DBSelect(strSQL, ",", ",") If Len(strDatas & "") > 0 Then ExitNow = False ' ' 新しいnameが見つかったらstrNamesとstrFamiliesに付加する ' strNames = strNames & "," & strDatas ' ' 新しいnameはstrFamiliesにも付加する ' strFamilies = PackList(strFamilies & "," & strDatas) End If Next I ' ' 次回は、今回N番目の次から ' T = N + 1 End If Loop Until ExitNow ' ======================== ' 終了処理 ' ======================== GetFamilies = strFamilies End Function 祈、成功!
- 山田 太郎(@f_a_007)
- ベストアンサー率20% (955/4574)
********************************** 新補足2:≪標準ライブラリの必須関数≫ ********************************** Public Function PackList(ByVal strList As String, _ Optional strDemiliter As String = ",") As String Dim I As Integer Dim N As Integer Dim strDatas() As String Dim strNewList As String strDatas() = Split(strList, strDemiliter) N = UBound(strDatas) strNewList = strDatas(0) For I = 1 To N If InStr(1, strNewList, strDatas(I)) = 0 Then strNewList = strNewList & "," & strDatas(I) End If Next I PackList = strNewList End Function Public Function CutStr(ByVal Text As String, _ ByVal Separator As String, _ ByVal N As Integer) As String Dim strDatas() As String strDatas = Split("" & Separator & Text, Separator, , 0) CutStr = strDatas(N * Abs(N <= UBound(strDatas))) End Function Public Function CharCount(ByVal Text As String, _ ByVal C As String) As Integer CharCount = Len(Text) - Len(Replace(Text, C, "")) End Function Public Function DBSelect(ByVal strQuerySQL As String, _ Optional colDelimita As String = ";", _ Optional rowDelimita As String = ";") As String On Error GoTo Err_DBSelect Dim R As Integer ' 行インデックス Dim N As Integer ' 行総数 - 1 Dim rst As ADODB.Recordset Dim fld As ADODB.Field Dim strList As String ' 全てのデータを区切子で連結して格納 Set rst = New ADODB.Recordset With rst .Open strQuerySQL, _ CurrentProject.Connection, _ adOpenStatic, _ adLockReadOnly If Not .BOF Then N = .RecordCount - 1 .MoveFirst For R = 0 To N For Each fld In .Fields With fld strList = strList & .Value & colDelimita End With Next fld strList = Mid(strList, 1, Len(strList) - 1) & rowDelimita .MoveNext Next R Else strList = "" End If End With Exit_DBSelect: On Error Resume Next rst.Close Set rst = Nothing DBSelect = IIf(Len(strList) > 0, Replace(strList & "[END]", rowDelimita & "[END]", ""), "") Exit Function Err_DBSelect: MsgBox "SELECT 文の実行時にエラーが発生しました。(DBSelect)" & Chr(13) & Chr(13) & _ "・Err.Description=" & Err.Description & Chr(13) & _ "・SQL Text=" & strQuerySQL, _ vbExclamation, " 関数エラーメッセージ" Resume Exit_DBSelect End Function Public Function DBLookup(ByVal strQuerySQL As String, _ Optional ByVal ReturnValue = Null) As Variant On Error GoTo Err_DBLookup Dim DataValue Dim rst As ADODB.Recordset Set rst = New ADODB.Recordset With rst .Open strQuerySQL, _ CurrentProject.Connection, _ adOpenStatic, _ adLockReadOnly If Not .BOF Then .MoveFirst DataValue = .Fields(0) End If End With Exit_DBLookup: On Error Resume Next rst.Close Set rst = Nothing DBLookup = IIf(Len(DataValue & ""), DataValue, ReturnValue) Exit Function Err_DBLookup: MsgBox "SELECT 文の実行時にエラーが発生しました。(DBLookup)" & Chr$(13) & Chr$(13) & _ "・Err.Description=" & Err.Description & Chr$(13) & _ "・SQL Text=" & strQuerySQL, _ vbExclamation, " 関数エラーメッセージ" Resume Exit_DBLookup End Function ※添付図が落ちていました。
- 山田 太郎(@f_a_007)
- ベストアンサー率20% (955/4574)
********************************** 新補足1:先のプログラミングやUNIONクエリ版を動かすには? ********************************** 以下の関数を標準ライブラリに登録して下さい。 ≪標準ライブラリの必須関数≫ 【ベーシック関数】 1、PackList(文字列,区切子) ? PackList("シロサケ,ギンザケ,シロサケ") シロサケ,ギンザケ PackList()は、区切子で区切られた単語列から重複を取り除きます。 2、CutStr(文字列,区切子,切出す位置) ? CutStr("シロサケ,ギンザケ", ",", 1) シロサケ ? CutStr("シロサケ,ギンザケ", ",", 2) ギンザケ CutStr()は、区切子で区切られた単語列から指定番目を取出します。 3、CharCount(文字列,カウントする文字) ? CharCount("シロサケ,ギンザケ", ",") 1 CharCount()は、文字列中に指定の文字が何個あるかを調べます。 【データベースアクセス関数】 1、DBSelect(SQL文,列区切子,行区切子) ? DBSelect("SELECT * FROM FamilyLIST", ",", chr(13)) 1,シロサケ,ギンザケ 2,ベニザケ,ギンザケ 3,シロサケ,サクラマス 4,アメマス,イワナ 5,イワナ,オショロコマ DBSelect()は、SQL文の実行結果を指定の列区切子と行区切子とで連結した一つの文字列を返します。 2、DBLookup(SQL文) ? DBLookup("SELECT name FROM FamilyList WHERE ID=1") シロサケ DBLookup()は、SQL文の実行で最初に見つかったデータを1個だけ返します。 【アプリケーション用関数】 1、GetFamlies(個有名) 添付図のように、個有名に対応する属の全てを戻します。 PS、以上の6つの関数を全て標準ライブラリに登録されて下さい。 登録すべき関数の全容は、新補足2で示します。なお、これらの関数は1985年~1996年に書いたものですので試され済みです。新しく書いた関数は、GetFamlies(個有名)のみです。私は、既に70歳を迎えようかという老人。若い質問者の柔らかい頭が、これらを理解できないことはない筈。頑張ってください。
- 山田 太郎(@f_a_007)
- ベストアンサー率20% (955/4574)
【補足:UNIONクエリを利用すると・・・】 更に、VBコードの記述を簡便化できます。 ≪クエリ1≫ SELECT name, replace(GetFamilies(name), name &",","") AS FamiliesList FROM FamilyList UNION SELECT family, replace(GetFamilies(family), family &",","") AS FamiliesList FROM FamilyList; 用意するのは、このようなUNIONクエリです。同クエリを実行すると添付図のような結果が得られます。ここまでの情報が取得できれば、整形し出力するのは簡単です。実際には、次のようで事足ります。 Private Sub コマンド0_Click() Dim intTotal As Integer Dim H As Integer Dim I As Integer Dim N As Integer Dim M As Integer Dim strDatas As String Dim strName As String Dim strFamily As String Dim strFamilies As String Dim strSQL As String DoCmd.SetWarnings (False) DoCmd.RunSQL "DELETE FROM FamilyList_ALL" strDatas = DBSelect("クエリ1", ";", Chr(13)) intTotal = CharCount(strDatas, Chr(13)) For I = 0 To intTotal strName = CutStr(CutStr(strDatas, Chr(13), I + 1), ";", 1) strFamilies = CutStr(CutStr(strDatas, Chr(13), I + 1), ";", 2) N = DBLookup("SELECT COUNT(*) FROM FamilyList_ALL") M = CharCount(strFamilies, ",") For H = 0 To M N = N + 1 strFamily = CutStr(strFamilies, ",", H + 1) strSQL = "INSERT INTO FamilyList_ALL (ID,name,family)" & _ " VALUES (" & N & ",'" & strName & "','" & strFamily & "');" DoCmd.RunSQL strSQL Next H Next I DoCmd.SetWarnings (True) End Sub ※以上のようなやり方でもよければキー関数である GetFamilies() のコードをご提示します。
お礼
f_a_007さん、度々ご教示頂き有難うございました。前回の回答が私が望んだ姿だったので試してみました。結果はコンパイルエラー(SubまたはFunctionが定義されていません。)でした。『DBSelect』が引っかかっている様でした。こうなると知恵の無い私にとってはどうすることもできず諦めモードです。 幾度ものご回答の応えられず、申しわけありませんでした。
- m3_maki
- ベストアンサー率64% (296/460)
私の実力では、クエリでは絶対に不可能とは言い切れませんが VBA ならできます。 ワークテーブルを使いますので複数ユーザー同時作業はできません。 準備。 テーブル、クエリを作成します。 テーブル【分類】 ・項目(テキスト型)主キー ・グループ(長整数型) クエリ【項目一覧】 SELECT [データ1].項目A as 項目 FROM データ1 UNION Select [データ1].項目B as 項目 FROM データ1 クエリ【左更新】 PARAMETERS GNo Long; UPDATE (分類 INNER JOIN データ1 ON 分類.項目 = [データ1].項目A) INNER JOIN 分類 AS 元 ON [データ1].項目B = 元.項目 SET 分類.[グループ] = [GNo] WHERE 分類.[グループ] Is Null AND 元.[グループ]=[GNo] クエリ【右更新】 PARAMETERS GNo Long; UPDATE (分類 INNER JOIN データ1 ON 分類.項目 = [データ1].項目B) INNER JOIN 分類 AS 元 ON [データ1].項目A = 元.項目 SET 分類.[グループ] = [GNo] WHERE 分類.[グループ] Is Null AND 元.[グループ]=[GNo] クエリ【抽出データ】 SELECT 分類.[グループ], 分類.項目 AS 項目C, 分類_1.項目 AS 同族 FROM 分類 INNER JOIN 分類 AS 分類_1 ON 分類.[グループ] = 分類_1.[グループ] WHERE 分類_1.項目<>[分類]![項目] ORDER BY 分類.[グループ], 分類.項目, 分類_1.項目 モジュール Sub Sample() Dim dbs As DAO.Database Dim qdf1 As DAO.QueryDef Dim qdf2 As DAO.QueryDef Dim GNo As Long Dim cnt1 As Long Dim cnt2 As Long Dim strSQL As String Dim varName As Variant Set dbs = CurrentDb strSQL = "DELETE FROM 分類" dbs.Execute strSQL strSQL = "INSERT INTO 分類 ( 項目 )" & _ "SELECT 項目 FROM 項目一覧" dbs.Execute strSQL Set qdf1 = dbs.QueryDefs("左更新") Set qdf2 = dbs.QueryDefs("右更新") Do While True varName = DLookup("項目", "分類", "グループ Is Null") If IsNull(varName) Then Exit Do End If If cnt1 + cnt2 = 0 Then GNo = GNo + 1 strSQL = "UPDATE 分類 SET グループ = " & GNo & _ " WHERE 項目='" & varName & "'" dbs.Execute strSQL End If qdf1.Parameters("GNo") = GNo qdf1.Execute cnt1 = qdf1.RecordsAffected qdf2.Parameters("Gno") = GNo qdf2.Execute cnt2 = qdf2.RecordsAffected Loop Set qdf1 = Nothing Set qdf2 = Nothing dbs.Close Set dbs = Nothing End Sub テーブル、クエリを準備し、Sample() を実行します。 選択クエリ【抽出データ】に結果が得られます。 結果をテーブルにするなり、Excel に出力するなり、ご自由にどうぞ。
お礼
ご指南、有難うございました。見よう見真似で実行したところ、アウトプットは出ました。但し、望んだものとは少しばかり違ったみたいです。お手を 煩わせることになり申しわけございませんでした。
- 山田 太郎(@f_a_007)
- ベストアンサー率20% (955/4574)
もしかして・・・PartII 仮に最後の添付図が目的のそれに近いとして、それでもなお幾つかの疑問が残ります。それは、[データ1]の固有名と同族名とを入れ替えた探索の必要性の有無。もし、その必要性があれば、最後の添付図を作成する処理を左右入れ替えて再度実行するのが一番簡単です。その場合、幾つかの重複する組合せが発生するかも知れませんが、それは重複を除く最終処理を行えば解決します。 以上、念のために・・・・。
お礼
f_a_007さん、度重なるご回答有難うございました。VBで処理しなさいと理解しました。高いハードルが目の前にちらついてきました。
- 山田 太郎(@f_a_007)
- ベストアンサー率20% (955/4574)
- 山田 太郎(@f_a_007)
- ベストアンサー率20% (955/4574)
- 山田 太郎(@f_a_007)
- ベストアンサー率20% (955/4574)
Q、Accessでの抽出方法を教えてください。 A、一番簡単なのは? 01:”シロサケ”の同族名を検索する。 02:”1,シロサケ,ギンザケ”を目的のテーブルへINSERTする。 03:同族控え文字列”シロサケ,ギンザケ”を生成する。 ↓ 04:”シロサケ”の同族名を検索済み以外を検索する。 05:”2,シロサケ,サクラマス”を目的のテーブルへINSERTする。 06:同族控え文字列”シロサケ,ギンザケ,サクラマス”に更新する。 ↓ (04~06を繰り返す) ↓ 07:同族控え文字列”ギンザケ”を持つ”シロサケ”以外の固有名を検索する。 08:”3,ギンザケ,ベニザケ”を目的のテーブルへINSERTする。 09:同族控え文字列”ギンザケ,サクラマス|01|ベニザケ”に更新する。 ↓ 10:同族控え文字列”サクラマス”を持つ”シロサケ”以外の固有名を検索する。 11:ヒットしないのでINSERTなし。 ↓ 12:新しい同族控え文字列”ベニザケ”を持つ”ギンザケ”以外の固有名を検索する。 13:ヒットしないのでINSERTなし。 以上の手続きを全ての固有名で行えば目的は達成されると思う。 【ポイント】 同族控え文字列の生成 ”XXXXX,XXXXX|01|XXXXX,XXXXX|02|XXXXX・・・・・ 既検索リストの生成 "シロサケ,ギンザケ,サクラマス|01|ベニザケ・・・・・ 無限ループを回避する工夫が必要。 と、まずは、アルゴリズムをWORDかなんかでまとめるのが先決。
クエリでできると思いますが、問題は連続的な(連鎖していく)データがあることです。 これ連鎖していくと、もとの所に帰ってきて無限連鎖になるものが出てきますよ。 それに下に紹介するやり方では、無限連鎖を扱えません。 いずれにしろ連鎖の数を制限する必要があります。 連鎖の数を2つに限定した場合を例にとります。 まずクエリビルダに同じテーブルを2つ並べます。説明の都合上左右に並べるということにしましょう。 そして左の項目Bと右の項目Aをつなぎます(矢印なしの線でつなぎます)。 表示する項目は左の項目Aと右の項目Bです。 このクエリを実行すると「アメマス・オショロコマ」のレコードが表示されるはずです。 これは手元で確かめてみました。 同じようにしてテーブルを3つ並べて左のB-中のA、中のB-右のAをつないだクエリを作って、左のAと右のBだけを表示させます。 これは連鎖の数が2の場合の表です。 オリジナルの表も目的の表の一部になりますから、テーブル一つだけでAとBを表示させるクエリも作ります。 この3つのクエリを合体させれば、目的の表になります。 合体させるにはユニオンクエリを使います。 これはクエリビルダでは作ることができません。SQLビューで作ります。ここら辺を参考にしてください。 http://www.sk-access.com/syo_query/sqa013_union.html https://support.office.com/ja-jp/article/%E3%83%A6%E3%83%8B%E3%82%AA%E3%83%B3-%E3%82%AF%E3%82%A8%E3%83%AA%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%97%E3%81%A6%E8%A4%87%E6%95%B0%E3%81%AE%E9%81%B8%E6%8A%9E%E3%82%AF%E3%82%A8%E3%83%AA%E3%81%AE%E7%B5%90%E6%9E%9C%E3%82%92%E7%B5%90%E5%90%88%E3%81%99%E3%82%8B-3856f16c-0a22-43f2-8c23-29ec44acbc05 たぶんダブる項目がたくさん出てくると思います。 ですから、それはさらに集計クエリでダブった項目を整理します。 ほんの一部だけを手元で確かめただけなので、これを作っていく際にどこかでうまくいかないところが出てくるかもしれませんが・・・そのときはゴメン。 なお、連鎖の数を制限すると、当然漏れが出てくるかもしれません。
お礼
藁でも何でも掴みたい気持ちで初めてこのサイトで質問させていただきましたが、ご丁寧な回答有難うございました。願わくば、連鎖が無限な場合はどうすればいいのかご教示頂けると感謝感激です。VBになりますかね・・・。
補足
稚拙な質問内容の意図を汲み取って頂いた上、適切な回答、有り難うございました。ご回答頂いたユニオンクエリ、クエリの集計機能を利用した方法については理解できました。 但し、今回は連鎖の数が不明なので、無限連鎖の場合の求め方を知りたいのです。 どうやら、クエリでは無理なようですが、今からVBを勉強するには時間的余裕は無い上、能力的にもきついのが実情です。 VBのサンプルをご紹介いただけると幸甚です。
- 1
- 2
お礼
f_a_007さん、改めてご回答を振り返りながら勉強させてください。能力不足の小職をここまで導いていただき感謝いたします。