Microsoft Access 掲示板

views
4 フォロー
6,283 件中 3,521 から 3,560 までを表示しています。
22

皆様、ベンチマークなども行っていただき本当にありがとうございました。自分には難しくて理解できなかった部分もありましたので、テーブル設計も含めて、皆様から提示いただいたクエリやVBAをしっかり勉強したいと思います。ありがとうございました。HH

21

>> 18
丁寧に返信いただいてありがとうございます。お礼嬉しいです。

今回はデータ件数という「壁」があっただけで
りんごさんは、HHさんの希望に沿った回答をちゃんと提供できています。
長時間のチャレンジ、お疲れ様でした。

20

些細なことですけどENDをチェックする形にするとENDがあればSTARTがあることが保証される(はず)なのでIf判定がおよそ半分減らせますね(処理時間の差がわからない程度の差ですが)

VBAで連番振ってSQLって方式はどうせVBA使うならこれやればいいじゃないなのでACCESS上の話である以上ナンセンスなんですよね

実は対抗としては>> 14でりんごさんが試されている通り元のテーブルに手を入れてあげればn²オーダーに見えるクエリでも爆速で動くという手法だと思います。主キーインデックスのないテーブルに主キーインデックスを設定してスカラサブクエリで表示するという手法でもVBAだけと遜色ない速度がでます

結局のところテーブル設計をしっかりやりましょうという話です

19

StartTime フィールドを追加して、
"END"レコードの方に対応する"START"の時刻を格納するという方法にしてみました。

発想の柔軟さ、パフォーマンスへの配慮、さすがhatenaさんです。

標準SQLのウィンドウ関数 LAG が利用可能な SQLServer だと
以下のSQLで hatenaさんと同等のロジックになりそうですね。

SELECT EVENT
     , starttime
     , endtime
     , datediff( second, starttime, endtime ) DURATION
FROM
(
    SELECT EVENT
         , E_Time endtime
         , E_Cond
         -- OVER句における E_Cond DESC の指定は不要かも
         , LAG( E_Time, 1 )
               OVER ( PARTITION BY EVENT
                      ORDER BY     E_Time, E_Cond DESC ) starttime
    FROM 表1
) 
VBA_PART
WHERE E_Cond = 'END' ;
18
りんご 2021/06/22 (火) 17:13:08 c564b@0e907

>> 16
 早速、試してみたところ、時間がかかりすぎると、とても実感できました。続けて、テーブル作成クエリやSQLの悪あがきをやってみたけれど、力尽きました。
 ビギナーズラック達成かと舞い上がったのですが、なかなか難しいですね。
 返信、遅くなりましたが、このようなアドバイスはとても助かります。ありがとう。

17

mayuさん、ベンチマーク実験ありがとうございました。非常に示唆に富む興味深い実験ですね。
あと、PC性能すごいですね。当方の環境でテストしたら数倍時間かかりました(;^_^A

100万件のデータとなると連番を入力しておいても結構時間がかかるようです。
そこでテーブルに連番を入力するのではなく StartTime フィールドを追加して、"END"レコードの方に対応する"START"の時刻を格納するという方法にしてみました。

表1に日付/時刻型のフィールドを追加して、名前を StartTime とします
下記のVBAを実行します。

Public Sub SetStarTime()
    Const strSQL = _
          "SELECT * FROM 表1 ORDER BY EVENT, E_Time, E_Cond DESC;"

    Dim rs As DAO.Recordset
    Set rs = CurrentDb.OpenRecordset(strSQL, dbOpenDynaset)

    Do Until rs.EOF
        Dim stime As Date
        If rs!E_Cond = "START" Then
            stime = rs!E_Time
        Else
            rs.Edit
            rs!StartTime = stime
            rs.Update
        End If
        rs.MoveNext
    Loop

    rs.Close

    MsgBox "StarTime入力完了"
End Sub

クエリは下記のようにシンプルになります。

SELECT
    EVENT,
    StartTime,
    E_Time,
    DateDiff('s', StartTime, E_Time) AS DURATION
FROM
    表1
WHERE
    E_Cond = "END";

E_Condにインデックスを貼っておいたらほぼ一瞬で表示されました。

16

テーブルのEVENTとE_Timeに複合主キーを設定して、
再度チャレンジしてみたら、だいたい5秒で処理出来てしまったのだが、
うーん、これはありなのだろうか?

ありでしょう。
実際かなりの高速化が見込めるでしょうし、素晴らしいチューニングだと思います。

私がインデックスや主キーの設定を施さなかったのは
フィールドのグルーピングや抽出条件に指定するフィールドの順番が
回答者によって異なっていたため、
特定の手法だけベンチマークに有利な影響を及ぼす設定では
アルゴリズムの比較にならない、という理由です。

ただ、処理時間が本当に5秒へ縮小されたかというと...疑問符が付きます。
というのも
定義域集計関数を使ったクエリの場合、データシートビューの表示において
「目視できている」部分のレコードしか各行の演算が済んでいない
ということがあります。

つまり、
「 クエリのデータをデータシートビューで閲覧可能、且つUIの操作も可能 」
という状態であっても
全行の処理が終わっているとは限らないんです。
 

SELECT EVENT
     , E_Time
     , CDate(
           DMin( "E_Time", "表1"
               , "EVENT = '" & [EVENT] & "' And E_Time > #" & [E_Time] & "#"
           )
       ) As E_END
     , DateDiff( 's', E_Time, E_END ) As DURATION
FROM 表1
WHERE E_Cond = 'START' ;

 
この SQL に名前を付け、クエリとして保存した後に
クエリを右クリックして表示されるコンテキストメニューから
テキストファイルにでもデータをエクスポートしてみることをお薦めします。

クエリを開くために要した時間は数秒程度と短いのに
データのエクスポートを実施した途端、
ファイル書き出しのオーバーヘッドが多少はあるにしろ
異常なくらい長い時間を要する

ということが実感できると思います。

14
りんご 2021/06/22 (火) 00:21:18 c564b@0e907

>> 13
テーブルのEVENTとE_Timeに複合主キーを設定して、再度チャレンジしてみたら、だいたい5秒で処理出来てしまったのだが、うーん、これはありなのだろうか?

13
りんご 2021/06/21 (月) 23:48:40 c564b@0e907 >> 10

Mayu様のサンプルデータ追加モジュールを活用して、上記、りんごのクエリを実行中、、、
応答なし、、、応答なし、、、30min経過、一部、描画、、、応答なし、、、計測断念。
お試ししたら、ダメなやつでした、ごめんなさい。

4

クロス集計クエリのデザインビューで下記のように設定すればどうでしょう。
"一般協賛"の場合です。

フィールド 会社名 住所 電話番号 年度 売上金額 収入科目
集計 グループ化 グループ化 グループ化 グループ化 合計 Where条件
行列の入れ替え 行見出し 行見出し 行見出し 列見出し
抽出条件 "一般協賛"
3
名前なし 2021/06/21 (月) 22:56:27 c564b@0e907

 クロス集計クエリは、Google先生と同じ説明になると思います。
 例えば、新しいクエリを作り、テーブルの各フィールドを選択・追加。
 次に、デザインタブのΣ集計ボタンを押し、下記を設定。
・売上金額フィールドの集計:グループ化を集計:合計に変更。
・収入科目の集計:グループ化をWhere条件に変更、抽出条件:“一般協賛”を入力。
 次に、デザインタブのクロス集計ボタンを押して、行列の入れ替えを設定。
・年度が列見出し。
・売上金額が値。
・会社名と住所と電話番号が行見出し。
 最後に、実行。

 年度別の売上金額合計・目標金額・達成率は、わかりませんでした。もしかしたら、目標金額のような新しいフィールド、見出しや明細のような新しいテーブルが必要なのかもしれません。
 個人的には、クロス集計の美しさよりも、テーブル設計の美しさを追求するほうが楽しいと思いますよ?
 

2
セメント 2021/06/21 (月) 20:15:02 d1e06@c0fb6

回答ありがとうございます。

説明不足ですみません。目標金額については無視していただいて構いません。

クロス集計クエリでもできないか調べてみたのですが、いろんな要素があって挫折したのです。

もう少々具体的に教えていただけるとありがたいです、ご回答よろしくお願いします。

2
セメント 2021/06/21 (月) 20:11:37 d1e06@c0fb6

回答ありがとうございます。

キーワードから色々調べてみてなんとかできました!!

4
朱色 2021/06/21 (月) 19:59:40 修正 db0eb@2db89

データを直接修正することについての是非は横においておくとして・・・

3結合にすることでデータ量が増えるということはないですか?
たとえば2結合では 1-A、2-B だったのに、3結合では 1-A-a、1-A-b、1-B-c という風に。
それぞれの結合が、各サブテーブルのすべてのキー項目と連結されているなら増えないハズ。

あと邪道ですが、SQLServer側にVIEWを作ってそれに対してリンクを張るという手もありますね。
お勧めはしませんが。

すみません、パフォーマンス系の知識はあまりなくて。
他の方の有用回答にご期待ください(;==)/

(ちなみにテーブル構造と結合部分が完全なSQL貼ってくださったほうが良いと思います)

12

100万件のデータに対して、クエリ( SQL )の実行を10回繰り返し
最終行のデータが表示されるまでの平均所要時間を計測してみました。
( EVENT, E_Time, E_Cond, Seq 各列にインデックスや主キーの設定は無し )

< ベンチマーク実施環境 >
バージョン:  Access 2019 (32bit)
OS:     Windows 10 Pro 64bit
CPU:     Intel Core i9-8950HK 2.90GHz
メモリ:    64 GB
ディスク:   Samsung SSD 970 Pro
 
< 実行結果 >

手段EVENT数 15,000EVENT数 700
クロス集計クエリ14.8秒10.4秒
選択クエリ(多段)42.1秒36.8秒
集計クエリ6.2秒6.0秒
自己結合(内部結合)20.4秒7分22秒
自己結合(外部結合)36.7秒12分48秒
スカラサブクエリ計測断念計測断念※ 20分経過しても結果が表示されず
定義域集計関数計測断念計測断念※ 20分間Accessが固まったまま
 
< サンプルデータ追加モジュール >
`js
Sub add_record()

    Const TOTAL_REC    As Long = 1000000
    Const EVENT_CHAR   As Long = 3
    Const MAX_DURATION As Long = 120
    Const TABLE_NAME   As String = "表1"

    With CurrentDb
        If (DCount("*", "MSysObjects", "[Name] = '" & TABLE_NAME & "'") = 0) Then
            Dim strDDL As String
            strDDL = "CREATE TABLE " & TABLENAME & "     " & vbNewLine
                   & "(                                   " & vbNewLine
                   & "      EVENT   VARCHAR(50)  NOT NULL " & vbNewLine

                   & "    , ETime  DATETIME     NOT NULL " & vbNewLine
                   & "    , ECond  VARCHAR(20)  NOT NULL " & vbNewLine
                   & "    , Seq     INT                   " & vbNewLine _
                   & ");"
            .Execute Query:=strDDL, Options:=dbFailOnError
            Application.RefreshDatabaseWindow
        End If
        .Execute Query:="DELETE FROM " & TABLE_NAME & ";", Options:=dbFailOnError

        Dim ts As Date
        ts = DateSerial(Year(Date), 1, 1) + TimeSerial(0, 0, 1)

        Dim dic As Object
        Set dic = CreateObject("Scripting.Dictionary")

        With .OpenRecordset(TABLE_NAME, dbOpenTable)
            Dim i      As Long
            Dim j      As Long
            Dim e_name As String

            e_name = Space$(EVENT_CHAR)

            For i = 1 To TOTAL_REC
                If (i Mod 2 = 1) Then
                    Randomize
                    For j = 1 To EVENT_CHAR
                        Mid(e_name, j, 1) = Chr$(Int((90 - 65 + 1) Rnd) + 65)
                    Next j
                    dic(e_name) = dic(e_name) + 1
                End If
                ts = DateAdd("s", Int(MAX_DURATION
Rnd + 1), ts)

                .AddNew
                .Fields("EVENT").Value = e_name
                .Fields("E_Time").Value = ts
                .Fields("E_Cond").Value = IIf(i Mod 2 = 1, "START", "END")
                .Fields("Seq").Value = CLng(dic(e_name))
                .Update
            Next i
            .Close
        End With

        Set dic = Nothing
        .Close
    End With
    MsgBox Prompt:="データ作成完了", Buttons:=vbInformation, Title:="実行結果"
End Sub


3
だいふくもち 2021/06/21 (月) 10:44:18

りんご様、l2106様、ご回答いただきありがとうございます!

rs.FindFirst "入金月 = " & "#" & DateAdd("m", i, startDate) & "#"

このように変えたらうまくいきました!
なるほど…Format関数は日付型でなく文字列型にするので、"#"で囲んでも意味がなかったのですね…
おかげさまで大変勉強になりました!この度は助けていただき本当にありがとうございました!

11
hiroton 2021/06/21 (月) 08:53:58 2ea60@f966d

計算量という考え方があります

表1のデータを表2の形にするということは、STARTのデータに対しENDのデータを見つけることになりますが、シンプルに考えると100万のうち半数のSTARTのデータそれぞれに対し、同様の50万のデータがENDの候補になっていて、その中から最適な一つのデータを見つけることになるので50万x50万のデータのチェックをすることになります。データ数をnとすれば

n² x a

のチェック回数が必要であることがわかります
nは2乗されます(²が環境依存文字のため念のため)
aはSTARTとENDのデータがそれぞれ元のデータnの半数なので2分の1を2回掛けたり、インデックスが適切に設定されていれば比較処理も半分で済んだりするといったようなチェック回数に関係する係数です(増える係数が掛かることもあるでしょう)

1回のチェック時間がどの程度かは置いておいて、このようなデータ処理を組むと、データ数が10倍になれば処理時間が100倍になります

結果が表示されるまでに約38秒かかりました。この時間は遅いのでしょうか。それとも相応なのでしょうか。

38秒自体が早いか遅いかは感覚によるところですが、データを複数回チェックしなくていいような仕組みを組めば飛躍的に処理時間が短くなることは期待できます

既にいくつかの手法が出ていますが、データのチェックが1回で済むような複数の処理を組み合わせることにより計算量の式が

n x a

となるような処理を作ることができます。この形の処理であればデータ数が10倍になっても処理時間は10倍で済むような処理となるので実時間(38秒)がどうであれ、試した手法は遅い手法だと言えます

こういったデータの並び替えって、需要が結構ありそうだと思っていたんですが、以外に少ないみたいですね。

シンプルな処理は理論の時点で遅く、データの特徴を掴んだ特殊な処理を入れれば高速化が見込めるというものなので、需要はあっても特殊な処理が必要(コピペで動かない)ではずばりの情報は少ないでしょうね


hirotonの感覚からすれば100万件のデータで38秒は「十分に早い」と思います。問題なのは表1の形式のデータが正式なデータとして使われ、表2の形式が欲しいとなったとき、その都度38秒が発生することでしょう

それなら表2の形式のデータを正式なモノにすればよくないですか?一度データを作ってしまえば次からは集計処理がないので表示も一瞬ですよ

「シンプルな処理を考える時間+38秒」と「複雑な処理を考える時間+一瞬の処理」、一度しかやらなくていい処理ならどちらがいいかの選択も臨機応変です

こういったデータの並び替えって、需要が結構ありそうだと思っていたんですが、以外に少ないみたいですね。

改めてですが、テーブル構造をしっかり設計するとそもそも需要がないんですよね

10
りんご 2021/06/20 (日) 18:39:48 48103@0e907

シンプルに、新しいクエリを作成して、下記フィールドを考えてみました。
EVENT
E_Time
E_Cond・・・抽出条件:“START”
式1(表2のENDを導出):CDate(DMin(“E_Time”,”表1”,”EVENT=‘“ & [EVENT] & “‘ and E_Time>#” & [E_Time] & “#”))
式2(表2のDURATIONを導出):DateDiff(“n”,[E_Time],[式1])

 もしお試し頂けたらこれ幸いです。成否の結果と100万レコードの処理時間を教えてくれると嬉しいなぁ。

9

hatenaさんが回答なさっている{ クロス集計 + 選択クエリ }による
多段クエリのほうが可読性が良く、内容の理解は容易なのですが
SQL の読み書きに対し、特に苦手意識が無いのでしたら
SetSequenceNumber関数で Seq フィールドに連番を付与した後、
表のスキャンが一度だけになる集計クエリを作ればいいでしょう。

SELECT EVENT
     , Max( IIf( E_Cond = 'START', E_Time ) ) As E_START
     , Max( IIf( E_Cond = 'END',   E_Time ) ) As E_END
     , DateDiff( 's'
        , Max( IIf( E_Cond = 'START', E_Time ) )
        , Max( IIf( E_Cond = 'END',   E_Time ) )
       ) As DURATION
FROM 表1
GROUP BY EVENT
       , Seq
ORDER BY Max( IIf( E_Cond = 'START', E_Time ) )
       , EVENT ;

  
また、朱色さんと似たロジックになりますが
自己結合を用いた記述方法もご紹介します。
(
  ENDが存在しないイベントも表示する場合は  LEFT JOIN
  ENDが存在しないイベントが表示不要の場合は INNER JOIN
  というように
  表示件数とパフォーマンスを自身で調整することが可能です
)

SELECT x.EVENT
     , x.E_Time        As E_START
     , Min( y.E_Time ) As E_END
     , DateDiff( 's', x.E_Time, Min( y.E_Time ) ) As DURATION
FROM       表1 x
INNER JOIN 表1 y
ON
(
    x.E_Cond = 'START'   AND
    y.E_Cond = 'END'     AND
    x.EVENT  = y.EVENT   AND
    x.E_Time < y.E_Time
)
GROUP BY x.EVENT
       , x.E_Time
ORDER BY x.E_Time
       , x.EVENT ;

 

こういったデータの並び替えって、
需要が結構ありそうだと思っていたんですが、以外に少ないみたいですね。

前後への行参照は、定番と言っていいほど需要はあるのですけど
hatenaさんが仰っているように、SQL では重い処理になります。
朱色さんが記述したスカラサブクエリ、私が後半で記述した自己結合ともに「重い」SQLです。

Oracle や SQLServer などの本格的なデータベースでは
SQL文中に row_number, rag, lead といった分析関数を駆使して高速化できるのですが
残念ながら、Accessには分析関数が実装されていません。

したがって、SQL のボトルネックを VBA で補い、
SQL と組み合わせるという Access ならではのテクニックを用いて高速化したのが
hatenaさんの回答になります。

7

最初に紹介した連番入力する方法を提示しておきます。(ちょっと修正してます。)

まず、テーブル「表1」に数値型のフィールドを追加します。フィールド名は「Seq」とします。

次に、リンク先から、「グループ毎の連番を入力する関数」をコピーして標準モジュールに貼り付けます。

表1のデータを更新したときに、下記のコードを実行します。入力フォームの更新後処理にでも実行するといいでしょう。

    SetSequenceNumber "Seq", "表1", "Event,E_Cond", "E_Time" 

下記のようなクロス集計クエリを作成します。名前は「Q1」とします。

TRANSFORM First(E_Time)
SELECT EVENT, Seq
FROM 表1
GROUP BY EVENT, Seq
PIVOT E_Cond;

このクエリからさらに下記のクエリを作成します。

SELECT EVENT, START, END, DateDiff("s",START,END) AS DURATION
FROM Q1
ORDER BY START, EVENT;

クエリ2つ、使ってますが、サブクエリを使って一つに纏めることもできます。

クエリはかなり高速になると思います。

100万レコードだと連番入力のコードの実行に時間がかかるかも知れません。その場合は、更新したレコードのEVENTのみを抽出して連番入力するようにするといいでしょう。

表1の入力フォームの更新後処理で実行するとして、下記のようなコードになります。

    SetSequenceNumber "Seq", "表1", "Event,E_Cond", "E_Time", "EVENT='" & Me.EVENT & "'"
6

クエリでやると件数が増えると指数関数的に重くなりますので、そんなもんだと思います。
EVENT と E_Time にインデックスを設定する改善するかも知れません。

5

約100万レコードある表1形式のテーブルを使って、朱色様に教えていただいたクエリを実行してみました。結果が表示されるまでに約38秒かかりました。この時間は遅いのでしょうか。それとも相応なのでしょうか。hatena様の方法はまだ試しておりませんので、比較はできておりません.....

2
l2106 2021/06/18 (金) 20:17:16 9b357@095c4

私もそう思います。
値と書式(指定に従って値を Access が加工してくれるもの)は別なのでは。
例えば日付型のフィールドの書式にyyyy/mm/dd(aaa)を指定して、
「2021/06/18(木)」と入力しようとしても拒否されます。
「2021/06/18」の入力が受け付けられた後、Access が「2021/06/18(木)」を表示しますよね。

3
AI2000 2021/06/18 (金) 17:24:48 8c4bd@8699c

いろいろといじっているうちに遅くなっている原因がソート(ORDER BY)であることがわかりました。
サンプルには記載せず申し訳ありません。
ORDER BY の部分を消すと即時表示されるようになりました。

ソートしてる列に クラスター/非クラスターインデックスの設定をいろいろとしてみましたが、相変らず遅いです。
その項目の型は float型なのですが、何か関係はあるのでしょうか?

いずれにしても2つのテーブル結合のクエリでは速く、3つのテーブル結合では遅くなるという原因がわかりません。
引き続き調べてみます。

1
りんご 2021/06/18 (金) 17:22:08 48103@0e907

日付型のデータであるならば、#で囲めるとする。
例えば、#おはようございます#、これは、日付じゃないから、ルール違反。
では、#2021\月6\月#、これはどうですか?
と、思ったのだが、間違っていたら、すまん。

7
hiroton 2021/06/18 (金) 15:59:46 1b487@f966d

VBAの書き方っていうよりACCESSデータベースの設計の話なんですかね?

いろいろ端折って該当部分のVBAだけなら

Private Sub Form_AfterInsert()
    Me.Parent!顧客情報履歴CD = Me.顧客情報履歴CD
End Sub

で済みそうだけど

1

目標金額のデータはどこにどのように格納されているでしょうか。

とりあえず目標金額は無視して、年度を列名にして集計するにはクロス集計クエリを使えば可能です。

1

レポートの「グループ化と並べ替え」の機能て、日付でグループ化してグループフッターを表示させます。
グループフッターにテキストボックスを2つ配置して、下記のように設定します。

日計用のテキストボックス
コントロールソース =Sum([残高])

累計用のテキストボックス
コントロールソース =Sum([残高])
集計実行 全体

2
AI2000 2021/06/18 (金) 09:24:29 8c4bd@8699c

回答ありがとうございます!
実は未だにAccessのADPを使って運用しています。
いよいよ使えなくなりそうだということで、.accdb のODBC接続を勉強しているところです。
数年ぶりにACCESSをいじっています。

>明細15000万くらいから26万抽出

すごいデータ量ですね! そんなに多いのに即時!?
データはせいぜい50万件ぐらいです。しかしサーバーも10年以上前のものなので比較にならないかもしれません。

>インデックスの問題とかでしょうか。

インデックス! その発想はありませんでした。最初に設定しただけで全然いじってないので可能性はあるかもしれません。
インデックスの設定によっては、発行されるSQLの違いで速度に影響がでるという認識でよいでしょうか?

>もしフォームやレポートに表示するのであれば、

いずれはそうしたいのですが、まだその段階ではありません。
ODBC接続の遅さにびっくりしてしまって、いろいろとSQLをいじっているところです。

パススルークエリは速いのですが、編集可能にしたいのです。
インデックスについて勉強したいと思います。

6
Strike 2021/06/17 (木) 23:57:26 70517@63fff

hatena様
いつもお世話になっております。
フィールドサイズのご意見ありがとうございます。
参考させていただきます。
VBAで更新コードもありがとうございます。
取り入れてみますが、上手くいかなかった場合はまた相談させてください。
本当にありがとうございました。

1
朱色 2021/06/17 (木) 19:57:54 db0eb@bbb08

自分の環境(明細15000万くらいから26万抽出)の場合、単純抽出であれば即時表示されますね。
抽出したデータを集計させるとか、最後の行を表示しろとかすると時間が掛かりますが・・・。
インデックスの問題とかでしょうか。
もしフォームやレポートに表示するのであれば、そちらの情報提示していただけると
他の情報を誰かが出してくれるかもしれません。

単純に「パススルークエリにすれば解決するんや!」であれば、ちゃんとした形のパススルークエリを作成するか、
動的にしたいのであればVBAでQueryDef.sqlを書き換えるという手があります。
データベース・サーバに直接コマンドを発行する ←パススルークエリの作り方
VBAでクエリを作成する (DAO) ←動的にSQLを設定したい場合

ちなみにパススルークエリで書き込むSQLは、SQLServer形式にする必要があります。
具体的にいうと”FROM ( A LEFT JOIN B ON ...) LEFT JOIN C ON ...”ではなく
”FROM A LEFT JOIN B ON ... LEFT JOIN C ON ...”で動作します。

5

まず、蛇足から、

 ・顧客情報履歴CD 主キー 数値型 サイズ:バイト型,式=Nz(DMax("[顧客情報履歴CD]","顧客情報履歴")+1,1)

上記の主キーですか、バイト型だと255件しか登録できないので、整数型が長整数型にしておいた方かいいでしょう。長整数型をお勧めします。

本題

入力フォームで、入力するするときにVBAで更新することになりますが、現状の入力フォーム設計が不明なので、下記のような設計だと仮定します。

顧客情報履歴 に過去の顧客情報もすべて保存していくということなので、基本的に下記のような処理の流れになると思います。(一例ですが)

顧客情報閲覧フォームで顧客情報(最新のデータ)を表示
もし、データに変更がある場合は、
顧客情報更新フォームを新規レコードで開き(「データ入力」プロパティを「はい」に設定)、
顧客情報閲覧フォームのデータを転記する。

顧客情報更新フォームの更新前処理で、顧客マスター の 顧客情報履歴CD、登録日 を更新する。

顧客情報更新フォームの更新前処理は下記のような感じでいいでしょう。

Private Sub Form_BeforeUpdate(Cancel As Integer)
    If Not Me.NewRecord Then Exit Sub
    
    Dim rs As DAO.Recordset
    Set rs = CurrentDb.OpenRecordset( _
             "SELECT * FROM 顧客マスター WHERE 顧客コード =" & Me.顧客コード, dbOpenDynaset)
    rs.Edit
    rs!顧客情報履歴CD = Me.顧客情報履歴CD
    rs!登録日 = Date
    rs.Update
    rs.Close
End Sub

上記はDAOレコードセットを開いて、Edit、 Updateメソッドで更新してますが、
更新クエリで更新する方法もあります。

Private Sub Form_BeforeUpdate(Cancel As Integer)
    If Not Me.NewRecord Then Exit Sub
    
    Dim stSQL As String
    '更新クエリのSQL生成
    stSQL = "UPDATE 顧客マスター SET " & _
            " 顧客情報履歴CD= " & Me.顧客情報履歴CD & _
            ",登録日= " & Date & _
            " WHERE 顧客コード =" & Me.顧客コード
    
    CurrentDb.Execute stSQL '更新クエリ実行

End Sub
4
Strike 2021/06/16 (水) 23:22:47 70517@63fff

hatena様
いつもお世話になっております。
顧客マスターに顧客情報を登録後、顧客情報に変更(連絡先や支払い条件等)があった場合、過去の情報を維持したいので顧客情報履歴CDをもたせたいです。顧客マスターの情報変更後以降の処理には最新の顧客情報データを適用したいので最新顧客情報履歴CDを顧客マスターテーブルに反映したいです。
もし、間違っていたらご指摘の程よろしくお願いします。

3

顧客マスターと顧客情報履歴は、一対多の関係ですよね。
だとしたら、一側の顧客マスターに 多側の 顧客情報履歴CD を持たせるのはどのような目的でしょうか。
また、顧客マスターの一つのレコードに対して、複数の顧客情報履歴のレコードが紐づきますが、その中のどの 顧客情報履歴CD を入力するのでしょうか。

2
Strike 2021/06/15 (火) 22:57:46 70517@63fff

hatena様
いつもお世話になっております。
ご無沙汰しております。
下記はテーブルの構成になりますが、よろしくお願いします。

|顧客マスター
 ・顧客コード 主キー 数値型 サイズ:整数型,式=Nz(DMax("[顧客コード]","顧客マスター")+1,1)
 ・顧客情報履歴CD
 ・登録日

|顧客情報履歴
 ・顧客情報履歴CD 主キー 数値型 サイズ:バイト型,式=Nz(DMax("[顧客情報履歴CD]","顧客情報履歴")+1,1)
 ・顧客コード
 ・顧客名
 ・ふりがな
 ・変更日
 などです。

1

顧客マスターテーブル、顧客情報履歴テーブル のフィールド構成、主キー設定を提示してください。

5
たくろー 2021/06/11 (金) 12:37:05 bc949@a9354

ありがとうございます。
検索が甘かったです、ひとまず参考リンク含めて調べてみます。

それでも躓くことがあれば具体的にまたご相談させていただきます。

4
hatena 2021/06/11 (金) 10:34:32 修正

下記が参考になるとおもいます。

クエリでランダムに並べ替える、また、指定件数を無作為に抽出する - hatena chips

上記で指定件数を抽出できますので、もし、フィールドにフラグを立てたいなら、このクエリを更新クエリすればいいでしょう。

3
hiroton 2021/06/11 (金) 10:32:14 d36e7@f966d

キーワードに「ACCESS」も添えて「ACCESS 無作為抽出」とかで検索すればほぼそのままズバリな内容がたくさん出てくるんですよ

クエリで乱数を取得するフィールドを追加して、その乱数で並び替えて上からnレコード取得(TOP値プロパティ)とすれば欲しいレコードが得られます。これを「フラグ」とするための実装はその後の運用と合わせて考える部分なので、躓くようであればもう少し具体的な内容を含めて質問してください

2
たくろー 2021/06/11 (金) 09:08:12 bc949@34ac6

こんな方法はできるかな?とふと思いつきました。

ワークテーブルに、オリジナルのオートナンバーとワークテーブル用の1からの連番、そして、フラグのフィールドを設定して、クエリの抽出データをワークテーブルに書き出し、ワークテーブル用の連番の範囲に対して、乱数を使ってチェックを入れる、無限ループで、Dcountで、所定の数にチェックが付くまで繰り返し、最後に、データファイルにオリジナルのオートナンバーをキーにして抽出し、フラグを書き戻す。