Microsoft Access 掲示板

SQLの型が一致ませんエラー

11 コメント
views

初心者です。モジュールで作成したオリジナル関数でクエリ内で実行させてます。
モジュール内に simeID = DLookup("[締め日ID]", tbName2, joken1) を記述して値は取得出来ていますが処理速度を上げる為に下記SQLに変更しました(DLookupは重たい処理と情報得ましたので)。
 Dim strSQL As String
 strSQL = "SELECT [締め日ID] FROM " & tbName2 & " " & _
          "WHERE " & joken1 & ";"
 simeID = strSQL
そしてクエリを開くと型が一致ません(実行時エラー13)となります。
simeIDはLong型で宣言してます。それをVariant型にするとクエリでは"SELECT・・・・"がそのままの取得となります。
[締め日ID]はオートナンバー型です。
何が間違えているのか分からず困っています。

kameo
作成: 2026/02/05 (木) 11:19:12
通報 ...
1
hiroton 2026/02/05 (木) 12:04:20 f6bcb@f966d

いろいろ突っ込みどころはあるんだけど、
パッと見た感じ、「Dlookupが遅い理由をそのままに、Dlookupと同じ機能をわざわざ自前で作ろうとしている」感じですね
「なぜDLookupが遅いのか?」を理解しないと改善にはならないです

定義域集計関数とSQLの比較(T'sWare さん)

2
kameo 2026/02/05 (木) 13:25:25 9eaf4@2128d

hiroton様ありがとうございます。「なぜDLookupが遅いのか?」はなんとなくレベルですが理解しました。
今回作成のモジュール概要は A:顧客テーブル B:売上テーブル C:締め日テーブル D:売上明細テーブル 等があります。各テーブルにはオートナンバー型IDがあります。売上毎締め日期日クエリでそれぞれ連結させて売上毎の締め日付、支払期日等を他のモジュールで算出してまして、それは問題なく機能してました(この時は処理速度も速かったです)。
Aには顧客毎の支払条件があります(Cから選択)。その支払条件が変更になる場合が発生しました(例えば売上日付が2026/2/1から締め日が変更)。Aテーブルの支払条件を改定後の内容にしてしまうと、過去の売上分も改訂された締め日になってしまうので、対策としてE:変更前支払条件テーブルを作成し(顧客ID、変更前の締め日ID、初期日、最終日 等のフィールド)それに変更のあった顧客分のみ変更前の支払条件を入力してます。今回作成のモジュールにDCountでテーブルEから対象顧客分をカウントして、それが0の場合はAテーブルからから、1以上ならEテーブルから締め日IDを抽出しました。今後同一顧客で2回以上の変更もあるかもを想定して売上日付をEテーブルの初期日、最終日の期間内かどうかも判定させて条件に合う締め日IDを抽出させてます。このモジュールを売上毎締め日期日クエリに組み込み実行すると結構時間かかるので、Dlookで締め日IDを抽出が原因?でしたのでSQLでしたらと思ったのです。
このモジュールでいくつかの値(支払条件テキスト、締め実日等)も同じパターンで値を抽出しているのも遅い原因と思いますが。
この方法が良くないでしょうか?

3
hiroton 2026/02/05 (木) 19:10:42 f6bcb@f966d

良いか悪いかだと、うーん、ちょっとだけ、よくないですかねぇ
Eのテーブルは、変更があったものだけ、ではなく、履歴としてすべてを記録するようにすると余計な存在有無チェックがなくなるのでいろいろとはかどります(Dlookupを使うにせよ、SQLで全部やるにせよ高速化になります)


特定日のデータを期間の設定(開始日、終了日)から見つけ出す作業は、ACCESSでは特に苦手とする部類だと思います。モジュール化して対応しようとすると、どうしても「DLookupは遅い」のボトルネックに引っかかる形になるので、SQLを駆使してクエリ一つに書き上げるというのが一般的な回答になるんじゃないでしょうか?(SQLを直接記述するんでそもそもACCESSレベルでは一般的ではない)

遅い早いに関しては個人の感覚によるところなので、実際に運用する件数n件に対して、処理時間yでそれが、実運用に耐える/耐えない時間であるという評価基準が必要になります
それによって、手法のシンプルさ(メンテナンス性)と実処理時間のバランスが取れるように、最適な回答が変わってくるでしょう


自分はSQLゴリゴリ書くのはあまり得意ではないので、そっちの方向は他の人に任せます(お願いします)
VBAの話をするのであれば、まずは質問にあるところで、

このモジュールでいくつかの値(支払条件テキスト、締め実日等)も同じパターンで値を抽出している

これは、遅くなる原因としてかなりの比重がありそうです

Dlookup("ID")
Dlookup("支払条件テキスト")
Dlookup("締め実日等")
Set dbs = CurrentDb
Set rs = dbs.OpenRecordset("~")
id = rs!ID
支払条件 = rs!支払条件テキスト
締め実日 = rs!締め実日
rs.Close

上記Dlookupだけで処理した場合はDlookupのボトルネック+検索捜査が3回発生するのに対し、下記、レコードセットを取得した処理では、ボトルネック部分が1回、捜査処理も1回でそのほかのデータ取得は一瞬で終わります
先に挙げたリンク先でも、こういう処理をしたいならSQL文を使ったほうが良いと言っていますね
特に、一つのレコードから複数のフィールドの値を参照したいのであればDlookupの出番はないです

もう少し、VBAの高度な話をすると、

Set dbs = CurrentDb

これにかかる時間が相当にありそうだ、と、先に挙げたリンク先に指摘があります。オリジナル関数の呼び出し事(実処理のレコード数分だけ)実行しているとこれもボトルネックになるので、これをやらないように組むということもできます

オリジナル関数の外で変数を用意しておく

Public dbs As Database
Public rs As Recordset

Sub Init()
  Set dbs = CurrentDb
End Sub

Function オリジナル関数()
  Set rs = dbs.OpenRecordset("~")

Init関数を事前にどこかで1度だけ呼び出して、「ボトルネックになるDatabaseオブジェクトの生成」を1回のみにするなんて方法も考えられるでしょう

先に挙げたように、許容点はどこか?という話になるので、詳しい解説は抜きにしてひとまずこのくらいにしておきます

4
hatena 2026/02/06 (金) 09:43:35 修正

リレーション先の値に更新があった場合、過去の値も変更されてしまう場合の対処法ということですよね。

いろいろな方法があると思いますが、自分は下記のような設計にすることが多いです。

締日の更新に対処する場合の例

A:顧客テーブル B:売上テーブル C:締め日テーブル

このようなテーブル構成の場合、締め日IDはAではなくBに格納します。
Bの入力時に規定値として最新の対応する締め日IDを入力します。
これは規定値プロパティを使うか、入力フォームの更新後処理で入力します。

この方法だとクエリで単純にリンクさせるだけで対応する締日を取得できます。

5
kameo 2026/02/06 (金) 14:15:30 9eaf4@2128d

hiroton様ありがとうございます。色々とハイレベルなテクニックがありますね。レコードセットが今ひとつ理解できていません(どういうものなのか・・・)。使いこなせれば大変便利とは思っています。イメージとしてはプロシージャ内でテーブルの様なものを作成しておき(それがレコードセット?)、それに対して代入や更新等が可能という感じでしょうか? 何かで聞いた事があるのですがレコードソースのあるフォームであれば自動的にレコードセットが出来ているのでしょうか?
初歩的な事ですみません。

8
hiroton 2026/02/06 (金) 18:01:22 45420@f966d >> 5

レコードセットは、レコードの集合体です。レコードは管理されたデータのひと塊分です。レコードはイメージとしては「テーブルで表現したときの1行分のデータ」でも良いでしょう
主キーのないレコードセットもありますが、主キーがあればそれらは重複しないとか、必ず決められたフィールドを持つとか、フィールドに対してデータがあるとか、言葉にすれば当たり前のようなことですが、そういうルールで決められたデータのまとまりがレコードになります

レコードセットは「テーブルのようなもの」ととらえても問題ないですが、クエリをソースにして、複数のテーブルを繋げたり、必要なレコードだけ、必要なフィールドだけに絞り込んでデータをまとめて扱うことができます
また、フィールドによっては計算結果をソースとするものもあり、テーブルとして記録されないデータを持つ場合もあります

レコードソースのあるフォームであれば自動的にレコードセットが出来ているのでしょうか?

その通りです
レコードセットは必要なだけ作ることができるので、Aフォームを開けばAフォームのレコードセット、Bフォームを開けばBフォームのレコードセット、VBAで何か処理したいときにSet rs = CurrentDb.OpenRecordset("~")で読み込んだレコードセット「rs」のようにその時その時に応じて必要なだけ存在することになります

レコードセットはデータを直接操作できる仕組みなので、ACCESSがデータベースとして正しく動くための作法があります。VBAでレコードセットを扱うためには、その作法に従って間違いない記述が必要ですが、そういうところをできるだけ簡単にできるようにやってくれているのがACCESSのフォームという機能です


hatenaさん提案のように、もう少し全体像から改善を考える必要もあるのかなと思っています。質問の段階でだいぶ深いところからスタートしているのでその部分をさらに掘り込んで回答していますが、その内容が中途半端なのも別なアプローチの検討の余地があるだろうからです

データベース設計は正規化からスタートするわけですが、実務上は動くクエリのためにテーブル構造を変えるというのも避けられないことだと思っています(特に、ACCESSは貧弱なので)
VBAはデータベースの世界から見ればかなり特殊な対処法になるので、SQLでの対処も同時に考えて、必要ならテーブル構造の見直しから、なるべくメンテナンス性を犠牲にしたい手法を考えたいところです

6
kameo 2026/02/06 (金) 14:19:32 9eaf4@2128d

hatena様ありがとうございます。なるほど売上毎に持たせるといいですね(1レコードづつオートナンバーでユニークになっていますので)。売上テーブルにフィールド追加して過去の分は引っ張り出せるのでそれで代入しておき、新規レコードにはフォーム等で仕組み作る形で。
それが一番楽に出来る様な気がします、試してみます。
アイデア大変参考になりました。

7

正規化を突き詰めすぎるとテーブル構成が複雑になりパフォーマンスが悪くなったりすることがあります。
今回の質問もそのような状況だといえます。

そのような場合にあえて冗長なデータを追加して正規化を崩すことでパフォーマンスが改善できる場合があります。このような手法を「正規化崩し」といいます。「正規化崩し」について調べてみるとテーブル設計の幅がひろがると思いす。

9
名前なし 2026/02/06 (金) 21:31:28 7f9f4@0e907

その支払条件が変更になる場合が発生しました…過去の売上分も改訂された締め日になってしまうので、

 正規化崩し云々以前に、正規化が杜撰だったと気づいてどうしよう?というだけでしょ。設計から全部やり直しましょう。

10
hatena 2026/02/07 (土) 11:31:34 修正 >> 9

おっしゃる通り最初の設計は正規化としてはよろしくない部分はありますね。
下記の部分
E:変更前支払条件テーブルを作成

そこから指摘して説明すると長くなるので対処法の一例を回答しました。

この手法が正規化崩しといえるかどうかについては異論はあるかとは思います。

11
kameo 2026/02/09 (月) 08:24:03 9eaf4@2128d

hatena様ありがとうございます。アドバイス頂いた方法で試し正常動作することが確認出来ました。この方法であれば売上毎に可変可能ですのでイレギュラーな事案にも対応出来ます。後は入力フォームの工夫だけです。助かりました。

hiroton様ありがとうございます。フォームを使っての処理がほとんどなのでレコードセットは色々試してみます。

事前に色々想定しているのですが、どうしても想定外の事案が発生しますのでその都度改訂が必用になります。その時のアプローチが重要ですね。大変勉強になりました。