Microsoft Access 掲示板

副クエリを使ったSQLを作りたい

6 コメント
views

副クエリを使って、SQL文1つにしたいのですが、
副クエリを使ったSQL文が正しく書けません。
JOIN操作の構文エラーです
というエラーが出ます。

最終的にはEXCELで動作している自作アプリに入れたいです。
ADOを使ってExcelVBAでSQLを使うことはできています。
"00165"の部分は変数で扱っています。

【Q_FとQ_Rを保存してデザインビューで作成したクエリのSQL】※これをSQL文1つで実行したい
SELECT M_棟.物件ID, M_棟.棟名, Q_F.[フロア数], Q_R.部屋数
FROM (M_棟 INNER JOIN Q_F ON (M_棟.棟ID = Q_F.棟ID) AND (M_棟.物件ID = Q_F.物件ID))
INNER JOIN Q_R ON (M_棟.棟ID = Q_R.棟ID) AND (M_棟.物件ID = Q_R.物件ID)
WHERE (((M_棟.物件ID)="00165"));

これを、Q_FとQ_Rのクエリを保存しないでSQL1本で作るために、
上記のSQLのQ_Fの部分にしたいので、SQL文の中の
Q_Fのところに、【Q_F】のSQL文を
Q_Rのところに、【Q_R】のSQL文を
入れてみて作成したのが、以下のSQL文です。

【エラーの出るSQL文】
SELECT M_棟.物件ID, M_棟.棟名, Q_F.[フロア数], Q_R.部屋数
FROM (M_棟 INNER JOIN
(SELECT M_棟.物件ID, M_棟.棟ID, M_棟.棟名, Count(M_フロア.[フロアID]) AS フロア数
FROM M_棟 INNER JOIN M_フロア ON (M_棟.棟ID = M_フロア.棟ID) AND (M_棟.物件ID = M_フロア.物件ID)
GROUP BY M_棟.物件ID, M_棟.棟ID, M_棟.棟名
HAVING (((M_棟.物件ID)="00165")) AS Q_F)
ON (M_棟.棟ID = Q_F.棟ID) AND (M_棟.物件ID = Q_F.物件ID)) INNER JOIN
(SELECT M_部屋.物件ID, M_フロア.棟ID, M_棟.棟名, M_部屋.[フロアID], Count(M_部屋.部屋ID) AS 部屋数
FROM (M_棟 INNER JOIN M_フロア ON (M_棟.物件ID = M_フロア.物件ID) AND (M_棟.棟ID = M_フロア.棟ID))
INNER JOIN M_部屋 ON (M_フロア.物件ID = M_部屋.物件ID) AND (M_フロア.棟ID = M_部屋.棟ID) AND (M_フロア.[フロアID] = M_部屋.[フロアID])
GROUP BY M_部屋.物件ID, M_フロア.棟ID, M_棟.棟名, M_部屋.[フロアID]
HAVING (((M_部屋.物件ID)="00165") AND ((M_部屋.[フロアID])="00001")) AS Q_R)
ON (M_棟.棟ID = Q_R.棟ID) AND (M_棟.物件ID = Q_R.物件ID)
WHERE (((M_棟.物件ID)="00165"));

副クエリを使ったことがないので、基本的に間違っているのかもしれませんが、
どのようなSQL文を書いたら良いか、どなたかご教授していただけないでしょうか。

よろしくお願いいたします。

---↓【仕様】↓---
Microsoft365
Windows11

ACCESS
 【テーブル】
  M_棟
  M_フロア
  M_部屋

 【M_棟】
  物件ID  テキスト型(文字列5文字) 例:"00001"、"00002"…
  棟ID   テキスト型(文字列5文字) 例:"00001"、"00002"…
  棟名称  テキスト型(日本語文字) 例:"A"、"B"…

 【M_フロア】
  物件ID  テキスト型(文字列5文字) 例:"00001"、"00002"…
  棟ID   テキスト型(文字列5文字) 例:"00001"、"00002"…
  フロアID テキスト型(文字列5文字) 例:"00001"、"00002"…
 
 【M_部屋】
  物件ID  テキスト型(文字列5文字) 例:"00001"、"00002"…
  棟ID   テキスト型(文字列5文字) 例:"00001"、"00002"…
  フロアID テキスト型(文字列5文字) 例:"00001"、"00002"…
  部屋ID  テキスト型(文字列5文字) 例:"00001"、"00002"…

 【Q_F】
  SELECT M_棟.物件ID, M_棟.棟ID, M_棟.棟名, Count(M_フロア.[フロアID]) AS フロア数
  FROM M_棟 INNER JOIN M_フロア ON (M_棟.棟ID = M_フロア.棟ID) AND (M_棟.物件ID = M_フロア.物件ID)
  GROUP BY M_棟.物件ID, M_棟.棟ID, M_棟.棟名

 【Q_R】
  SELECT M_部屋.物件ID, M_フロア.棟ID, M_棟.棟名, M_部屋.[フロアID], Count(M_部屋.部屋ID) AS 部屋数
  FROM (M_棟 INNER JOIN M_フロア ON (M_棟.物件ID = M_フロア.物件ID) AND (M_棟.棟ID = M_フロア.棟ID))
  INNER JOIN M_部屋 ON (M_フロア.物件ID = M_部屋.物件ID)
  AND (M_フロア.棟ID = M_部屋.棟ID) AND (M_フロア.[フロアID] = M_部屋.[フロアID])
  GROUP BY M_部屋.物件ID, M_フロア.棟ID, M_棟.棟名, M_部屋.[フロアID]

ヘンリー
作成: 2025/02/17 (月) 09:43:38
通報 ...
1

質問の結構複雑なSQLを読み解くのは骨なので、
私が複数クエリをサブクエリを使って一つにまとめる場合の手法を紹介しておきます。

まず、クエリデザインビューから作成したSQLは不必要な()が多くなり読みづらいので、不要なカッコは除去します。
その後、階層関係が分かりやすいように適切なインデントを付けて読みやすくします。
インデントの付け方は人それぞれの好みがあるので工夫して自分が読みやすいようにしてください。

例えば質問の最初のSQLなら、自分なら下記のようにします。

SELECT
  M_棟.物件ID, M_棟.棟名, Q_F.[フロア数], Q_R.部屋数
FROM
  (M_棟 INNER JOIN Q_F
     ON M_棟.棟ID = Q_F.棟ID AND M_棟.物件ID = Q_F.物件ID)
  INNER JOIN Q_R 
    ON M_棟.棟ID = Q_R.棟ID AND M_棟.物件ID = Q_R.物件ID
WHERE M_棟.物件ID="00165";

つぎにサブクエリにしたいクエリ名(Q_F, Q_R)の前に下記のように () AS を挿入します。

SELECT
  M_棟.物件ID, M_棟.棟名, Q_F.[フロア数], Q_R.部屋数
FROM
  (M_棟 INNER JOIN () AS Q_F
     ON M_棟.棟ID = Q_F.棟ID AND M_棟.物件ID = Q_F.物件ID)
  INNER JOIN () AS Q_R 
    ON M_棟.棟ID = Q_R.棟ID AND M_棟.物件ID = Q_R.物件ID
WHERE M_棟.物件ID="00165";

この挿入したカッコの中に、それぞれのクエリのSQLを挿入します。

もちろん挿入する前にそれぞれのSQLも読みやすいようにインデントを入れておきます。

すると下記のような感じになります。

SELECT
  M_棟.物件ID, M_棟.棟名, Q_F.[フロア数], Q_R.部屋数
FROM
  (M_棟 INNER JOIN
    (     
      SELECT
        M_棟.物件ID, M_棟.棟ID, M_棟.棟名, Count(M_フロア.[フロアID]) AS フロア数
      FROM
        M_棟 INNER JOIN M_フロア
          ON M_棟.棟ID = M_フロア.棟ID AND M_棟.物件ID = M_フロア.物件ID
      GROUP BY M_棟.物件ID, M_棟.棟ID, M_棟.棟名
    ) AS Q_F
     ON M_棟.棟ID = Q_F.棟ID AND M_棟.物件ID = Q_F.物件ID
  )
  INNER JOIN
    (
      SELECT
        M_部屋.物件ID, M_フロア.棟ID, M_棟.棟名, M_部屋.[フロアID], Count(M_部屋.部屋ID) AS 部屋数
      FROM
        (M_棟 INNER JOIN M_フロア
          ON M_棟.物件ID = M_フロア.物件ID AND M_棟.棟ID = M_フロア.棟ID)
        INNER JOIN M_部屋
          ON M_フロア.物件ID = M_部屋.物件ID AND M_フロア.棟ID = M_部屋.棟ID AND M_フロア.[フロアID] = M_部屋.[フロアID]
      GROUP BY M_部屋.物件ID, M_フロア.棟ID, M_棟.棟名, M_部屋.[フロアID]
    ) AS Q_R 
      ON M_棟.棟ID = Q_R.棟ID AND M_棟.物件ID = Q_R.物件ID
WHERE M_棟.物件ID)="00165";

提示のSQLを機械的につなげただけのものなので、希望の結果になるかどうかはそちらでしっかり確認してください。

2

"00165"の部分は変数で扱っています。

M_部屋.[フロアID])="00001"

[M_部屋]の[フロアID]に対する条件値は固定なのでしょうか。
それとも、その部分も変数で指定しているのでしょうか。

もし前者である場合、"00001"を指定しているのはどのような理由からなのでしょうか。

3

Accsessでは一番外側のWHERE以下以外で項目以外を条件に指定するとなぜかエラーになったような。
【エラーの出るSQL文】で途中に入ってる((M_棟.物件ID)="00165")がエラーの原因でしょうか。
提示されている【Q_F】【Q_R】にはないのに、なぜいきなり出てきた?という気はしますが。

4
ヘンリー 2025/02/20 (木) 10:14:06 df814@74a06

皆様、ご回答ありがとうございます。
おかげさまでできました。

hatena様、副クエリの作り方を教えていただきありがとうございます。

sk様、"00001"が決め打ちなのは、M_部屋がの構成が以下の通りだからです。
【M_部屋】
物件ID
棟ID
フロアID
部屋ID


部屋IDは、棟IDごと、フロアIDごとに、"00001"、"00002"…とあり、
棟ごとフロアごとの部屋数ではなく、棟ごとの部屋数だけを表示したいためです。

朱様
hatena様の例をもとに、1から作り直してみたので、
エラーの原因は後ほど確認してみます。

ユーザレビューを行った結果、少し仕様が変わり、M_部屋にデータがなくても、
棟名と棟ごとのフロア数は表示してほしいとの事でした。
出来上がったものを掲載しておきます。

SELECT
  M_棟.物件ID, M_棟.棟名, Q_F.[フロア数], Q_R.部屋数
FROM
  (M_棟 INNER JOIN
     (
      SELECT M_棟.物件ID, M_棟.棟ID, M_棟.棟名, Count(M_フロア.[フロアID]) AS フロア数
      FROM M_棟
      INNER JOIN M_フロア
      ON M_棟.物件ID = M_フロア.物件ID AND M_棟.棟ID = M_フロア.棟ID
      GROUP BY M_棟.物件ID, M_棟.棟ID, M_棟.棟名
     ) AS Q_F
   ON M_棟.物件ID = Q_F.物件ID AND M_棟.棟ID = Q_F.棟ID
  )
  LEFT JOIN
     (
      SELECT M_部屋.物件ID, M_フロア.棟ID, M_棟.棟名, M_部屋.[フロアID], Count(M_部屋.部屋ID) AS 部屋数
      FROM
      (M_棟 INNER JOIN M_フロア ON M_棟.物件ID = M_フロア.物件ID AND M_棟.棟ID = M_フロア.棟ID)
      INNER JOIN M_部屋
      ON M_フロア.物件ID = M_部屋.物件ID AND M_フロア.棟ID = M_部屋.棟ID AND M_フロア.[フロアID] = M_部屋.[フロアID]
      WHERE M_部屋.[フロアID]='00001'
      GROUP BY M_部屋.物件ID, M_フロア.棟ID, M_棟.棟名, M_部屋.[フロアID]
     ) AS Q_R
   ON M_棟.物件ID = Q_R.物件ID AND M_棟.棟ID = Q_R.棟ID
WHERE M_棟.物件ID='00165'

5

棟ごとフロアごとの部屋数ではなく、棟ごとの部屋数だけを表示したいためです。

つまり次の SQL と同等の実行結果を得られればよい、ということでしょうか。

SELECT [M_棟].[物件ID], 
       [M_棟].[棟ID], 
       [M_棟].[棟名称], 
       (SELECT Count(*) 
        FROM [M_フロア] 
        WHERE [M_フロア].[物件ID] = [M_棟].[物件ID] 
          AND [M_フロア].[棟ID] = [M_棟].[棟ID]) AS [フロア数], 
       (SELECT Count(*) 
        FROM [M_部屋]
        WHERE [M_部屋].[物件ID] = [M_棟].[物件ID] 
          AND [M_部屋].[棟ID] = [M_棟].[棟ID]) AS [部屋数] 
FROM [M_棟] 
WHERE [M_棟].[物件ID] = "00165" 
ORDER BY [M_棟].[物件ID], 
         [M_棟].[棟ID];
6
ヘンリー 2025/02/22 (土) 11:08:24 df814@74a06

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

sk様が掲載していただいたSQLでも同様の結果が得られました。
※部屋数のカウントをするSQL文のWHERE句には、
 [M_部屋].[フロアID]="00001"
 を追加して、期待通りの結果となりました。

sk様のようなSQLの書き方もあること
大変参考になりました。