ACCESSにて在庫の管理を行いたいと思っております
クエリを用いて当月の前月在庫数、当月入庫数、当月出庫数、当月在庫数を出したいと考えておりますが、
どのように行えばいいのかわかりません。
テーブルは以下のように作成しております。
・出荷先
出荷先ID(主)
出荷先名
・出庫
出庫ID(主)
出庫日
出荷先ID
・出庫詳細
出庫詳細ID(主)
出庫ID
製品ID
出庫数量
備考
・製品
製品名(主)
仕様
測温範囲
備考
・入荷先
入荷先ID(主)
入荷先名
・入庫
入庫ID(主)
入庫日
入荷先ID
・入庫詳細
入庫詳細ID(主)
入庫ID
製品ID
入庫数量
備考
最終的には指定した月までの商品毎の前月在庫数、当月入庫数、当月出庫数、当月在庫数を
レポートに表示できればと考えております。
ご助言の程、よろしくお願いします
どの程度のことができるのかわからないのでヒントだけ
最終的には横並びに月と入庫数や出庫数を見たいのだと思いますが、出庫と入庫が別々なテーブルに分かれているのでちょっと面倒です。
ユニオンクエリ、集計クエリあたりをつかって入出庫集計クエリを作ります
「在庫」は「その月までの入庫の合計-出庫の合計」と計算させるのが基本ですが、レポートを使うのであればテキストボックスのプロパティ「集計実行」を使って簡単に実装できます
ご助言ありがとうございます。
私自身、本を見ながらの組んでいたもので基本的には初心者です。
ユニオンクエリと言われてピンときません。
SQL?とかを学習しないと難しいでしょうか?
SQLやユニオンクエリ、集計クエリをどのように行えばいいのかわかりません。
大変申し訳ありません。
SQLを学習するために意気込む程の気持ちはなくても大丈夫です。
ACCESSなら「クエリデザインでデザインビューとSQLビューを切り替えて確認できる」くらいに思っておけば大丈夫です。
違いは、デザインビューは画面操作で簡単にSQLが作れるけれど複雑SQLは作れない(ユニオンクエリもこれに該当)といった程度です。
インターネット上でやり取りをする場合はテキストベースがほとんどですので、SQLを文字列で扱えるとなれば大きなアドバンテージになります。
(扱えると言うのはコピー&ペーストができるというレベルで大丈夫です)
それではもっと基本的なところからもう少し具体的な手順を
1.選択クエリで出庫明細、入庫明細を作る
元の元になるデータとして
出庫明細
と
入庫明細
といったデータが必要になります。選択クエリで問題なく作れますか?
今後のやり取りを楽にするために、それぞれ出来上がったものはQ出庫明細とQ入庫明細(クエリは頭文字にQをつける)としておきましょう
2.ユニオンクエリでデータを縦につなげる(前段階)
それぞれ出来上がったQ出庫明細とQ入庫明細を使いやすいように手を入れます。
Q出庫明細
↓
Q入庫明細
↓
クエリビューではユニオンクエリが作れないので作業を簡単にするためにQ出庫明細とQ入庫明細を同じ列数、同じ列名に揃えておきます。
さらに、合体させた後に入庫なのか出庫なのかわかるように[入出庫フラグ]フィールドを追加しておきます(このフィールドのデータはすべて「出庫」または「入庫」で固定)
3.ユニオンクエリで入出庫明細をつくる
Q入出庫明細
事前準備をしてあるので、最も基本的なユニオンクエリの使い方でつくれます
4.集計クエリで入出庫数を横に並べる
Q入出庫集計_sub
これも集計クエリとしては基本的な使い方をやることになります。
まだ月ごとの集計になっていないのでQ入出庫集計_subとしておきます。
とりあえずここまで
どこまでできますか?
躓いたら出来上がったところまでのクエリをSQLビューで表示してこの掲示板にコピペしつつ質問しましょう
(SQLビューを開けばSQL文がすべて反転表示されているはずなのでそのまま右クリックからコピーするだけです)
hiroton 様
返信が遅くなり申し訳ございません。
上記4番で躓いてしまいました。
1から3迄は以下のように行いました。
Q入庫 SQL
SELECT 入庫.入庫日 AS 入出庫日, 製品.製品ID, 入庫詳細.入庫数量 AS 入出庫数, "入庫" AS 入出庫区分
FROM (入荷先 INNER JOIN 入庫 ON 入荷先.入荷先ID = 入庫.入荷先ID) INNER JOIN (製品 INNER JOIN 入庫詳細 ON 製品.製品ID = 入庫詳細.製品ID) ON 入庫.入庫ID = 入庫詳細.入庫ID;
Q出庫 SQL
SELECT 出庫.出庫日 AS 入出庫日, 製品.製品ID, 出庫詳細.出庫数量 AS 入出庫数, "出庫" AS 入出庫区分
FROM 製品 INNER JOIN ((出荷先 INNER JOIN 出庫 ON 出荷先.出荷先ID = 出庫.出荷先ID) INNER JOIN 出庫詳細 ON 出庫.出庫ID = 出庫詳細.出庫ID) ON 製品.製品ID = 出庫詳細.製品ID;
SELECT 入庫.入庫日 AS 入出庫日, 製品.製品ID, 入庫詳細.入庫数量 AS 入出庫数, "入庫" AS 入出庫区分
FROM (入荷先 INNER JOIN 入庫 ON 入荷先.入荷先ID = 入庫.入荷先ID) INNER JOIN (製品 INNER JOIN 入庫詳細 ON 製品.製品ID = 入庫詳細.製品ID) ON 入庫.入庫ID = 入庫詳細.入庫ID
UNION ALL SELECT 出庫.出庫日 AS 入出庫日, 製品.製品ID, 出庫詳細.出庫数量 AS 入出庫数, "出庫" AS 入出庫区分
FROM 製品 INNER JOIN ((出荷先 INNER JOIN 出庫 ON 出荷先.出荷先ID = 出庫.出荷先ID) INNER JOIN 出庫詳細 ON 出庫.出庫ID = 出庫詳細.出庫ID) ON 製品.製品ID = 出庫詳細.製品ID;
>> 5
誤字脱字がありました。大変申し訳ありません。
Q出庫 SQL→ Q出庫明細 SQL
Q入庫 SQL→ Q入庫明細 SQL
無題 → Q入出庫明細 SQL
です。
Q入出庫明細のクロス集計クエリを選択し、行見出しを製品IDと入出庫日をし、列見出しを入出庫区分に選択。値を集計するフィールドを入出庫数にし、集計方法を合計を選択すればよろしいでしょうか?
このように行いました。
TRANSFORM Sum(Q入出庫明細.入出庫数) AS 入出庫数のカウント
SELECT Q入出庫明細.製品ID, Q入出庫明細.入出庫日
FROM Q入出庫明細
GROUP BY Q入出庫明細.製品ID, Q入出庫明細.入出庫日
PIVOT Q入出庫明細.入出庫区分;
(おわび:回答で、クロス集計クエリとすべきところを集計クエリとしていました。わかりにくかったと思います。無事に進んでいるようで何よりです)
大元になる明細ができたら月ごとの集計を作ります。
入出庫日を各月になるように変換する必要がありますが、クエリでは「入出庫区分」を作ったように、計算結果をデータとするフィールドを出力することができます。
Q入出庫明細のクロス集計クエリを元に新しくクエリを作り製品ID,出庫,入庫フィールドと、「年月: format([入出庫日],"yyyymm")」フィールドを作成します。
このクエリ(選択クエリ)で集計機能を使い、製品IDと年月でグループ化、出庫と入庫を合計すれば各製品IDの月ごとの出庫と入庫が出来上がります。
後は、このクエリを基にレポートを作成すれば、>> 2の通り製品IDごとに各月の在庫まで表示できるでしょう。
どのような方法を考えているのか、レポートをどの程度扱えるのかわからないので今回もここまで
Format([入出庫日],"yyyymm")
の解説今回、月ごとの集計を出したということで、日付データから月データを作ります。
日付から月を取り出す関数としては
month([日付])
がありますが、データベースが運用されれば年も跨ぐでしょう。2019年の11月の数字と2020年のそれが合算されたらまずいでしょうから、「年」の情報も含んだ「年月」フィールドにします。format関数そのものについては調べてみてください。
今回は、例えば「2019/12/01」という日付のデータを「201912」のように変換する使い方になっています。
余談
いまさらですが、テーブル構造が先に出ていたのでユニオンクエリを使って~と進めましたが、入出庫ぐらいなら最初から一つのテーブルでいいんじゃないかと思います。
(最初から入出庫区分を付けたテーブルで管理し、必要に応じて入出庫区分='出庫'等のクエリを発行する)
そのほかのテーブルについても入庫・出庫で分かれていますが、内容は同じものになるのでしょう。
これらについてはむしろ、入庫であるか、出庫であるかという区分も必要ないように見えるのでデータを分けてしまうのは良くないでしょう。
その後いかがでしょうか。
レポートで「集計実行」を使うと簡単だと言いましたが、実はこの方法には問題点があります。
とのことだったのでとりあえず形にしてから再修正を、と思っていましたが、(自分の方法のままでは)形にならなさそうな気がしてきたので追記を入れておきます。(ホントはQA形式で進めていきたかったのですが)
まず、レポートの表示を次のように考えることとします。
2019年12月
次に、レポートに表示する月はフォーム上で入力することとします。
・フォーム名:フォーム1
・フォーム1上のテキストボックス:表示月
表示月には表示したい月の1日を入力することとします。(2019年12月のレポートを表示したい場合は2019/12/1)
当月入出庫のデータ、当月在庫用のデータを作る
Q入出庫明細のクロス集計クエリを元に次のクエリを作成します。
Q当月入出庫
Q当月まで入出庫
それぞれ必要に応じた日付の範囲(where条件)で出庫、入庫を合計します。
具体的にはフォーム1の表示月に「2019/12/1」と入っていれば
上は「
Between #2019/12/1# And #2019/12/31#
」、下は「
<= #2019/12/31#
」となるような条件になっています。
Q当月まで入出庫では必要に応じて製品の情報を加えておきます。
クロス集計クエリを使う場合、フォーム上の値を使うためにはパラメーターの設定が必要です。
SQLビューであれば一番上に入っている1行ですが、デザインビューであればデザインビュー上側の適当な位置で右クリックして表示されるポップアップメニューから「パラメーター」を選択して設定できます。
レポート用のソースを作る
上の2つのクエリを基にさらにクエリを発行します。
ところどころにあるNz関数はデータがなかった時に別な値に置き換える関数です。
データがなかった時の値は
Null
になりますがNull
が含まれた計算は結果がNull
になるという特性がありますので、例えば12月の出庫がない製品の在庫を計算しようと在庫+入庫+出庫(Null)
を単純にしてしまうと計算結果がNull(画面上では表示なし)になってしまいます。このクエリを表示すれば、上に挙げたレポートの表示形式のデータが表示されますので、このクエリを基にレポートを作成すれば完成です。
連絡が遅くなり申し訳ございません。
何とかできるかと軽い気持ちで考えてみましたら思ったようなものができず、どうしたものかと悩んでおり遠のいておりました。
回答していただいたものでやってみます。
少々、お時間をくだされば幸いです。
写真が横になって申し訳ありませんがこのようなものができればと考えていました。<a
いくつか書式の違うレポートを作りたいということですね。
表示に差異はあっても元にするデータは同じなのでユニオンクエリ→クロス集計クエリの流れは基本的なデータの整形としてよくとる手です。
(最初から入出庫テーブルにしてしまえばユニオンクエリをやらなくていい分楽できますが)
2枚目の画像(月別在庫表)については新たに提示した手順のほうでうまくできると思います。
1枚目の画像(商品毎入出庫履歴)はちょっと複雑で、集計と明細が含まれた形なのでサブレポートを組み込むのが楽な方法かと思います。
(改ページを考えたときに先頭行をどうするか等で変わってくると思います)
前月末在庫は最後のクエリで計算されていますので、これを基にレポートを作成して、製品IDでグループ化、改ページの設定をすれば製品ごとのページが出来上がります。
明細部分はQ入出庫明細のクロス集計クエリから日付を条件にデータを抽出したデータをサブレポートにして製品IDでリンクして表示させるという形です。
最後の「合計」は吹き出しの説明がコピペになっていませんか?ちょっとよくわかりませんが見た目からすると「当月出庫数の合計」ですかね?レポートで合計する方法もまずは調べてみてください。
すいません。
いただいたレポート用ソースのSQLですが[在庫]を有効なフィールドまたは式として認識できませんと表記されてしまいました。
SELECT Q当月まで入出庫.製品名, Nz([在庫],0)-Nz([当月入庫],0)+Nz([当月出庫],0) AS 前月在庫, Nz([当月入庫],0) AS 入庫, Nz([当月出庫],0) AS 出庫, Nz([入庫の合計],0)-Nz([出庫の合計],0) AS 当月末在庫
FROM Q当月まで入出庫 LEFT JOIN Q当月入出庫 ON Q当月まで入出庫.製品ID = Q当月入出庫.製品ID;
この[在庫]というのはQ当月まで入出庫、Q当月入出庫には入っていないものだと思うのですが…
あぁ、すみません
回答前に少し修正を入れたらミスってました
「在庫」ではなく「当月末在庫」でした
基にしたクエリから持ってきているフィールドではなく、クエリで作ったフィールドをさらに同じクエリで参照しています
当月の在庫と当月の入出庫から逆算して前月の在庫を出しているという計算です
ちなみに一番最初のフィールドを
Q当月まで入出庫.製品ID
にするか製品名
にするか迷った挙句中途半端な形になってましたのでそこも修正してあります(レポートのレコードソースにするつもりだったので「製品名」でいいかと思いますが、さらに情報を追加したいとなったときのために製品IDも入れておいたほうがやりやすいかなぁとも思ってます)
hiroton様
ご回答ありがとうございます。
確認してみます。
hiroton様
確認取れました。ありがとうございました。
明けましておめでとうございます。
上記のクエリを基にレポートをウィザードで作成しようとしますとクエリのフィールド名が表示しません。どのように行えばレポートを作成できるのでしょうか?
>> 1のテーブル構成では、出庫詳細 と 入庫詳細 のフィールド名が、共に「 数量 」なのに
>> 5のSQL文では、出庫詳細 と 入庫詳細 のフィールド名が「 入庫数量 」及び「 出庫数量 」
また、製品テーブルの 製品型番 というフィールドは 投稿文に掲載されておらず、実際の有無が不明。
こういった情報の欠落や矛盾は、ご自身で修正いただくとして
ご希望の結果は 以下のような SQL で表現できるでしょう。
中間クエリを作らず、SQLを一筆書きで記述してますので
内容について少し解説しておきます。ポイントは3つ。
【 1 】
FROM句のサブクエリになっているユニオンクエリでは
製品・入荷先・出荷先 各テーブルの列は不要ですから、内部結合を必要最小限に留め、
抽出条件は、最初に評価されるクエリ( 一番内側 )に設定することで 処理を高速化できます。
【 2 】
SELECT句では IIF関数を使って条件分岐させることで
当月在庫と前月在庫を一度に計算します。
( 入庫・出庫に関しても同様 )
こうすると、クロス集計クエリを作る必要もなく、構造を単純化できます。
【 3 】
指定月のパラメータは
フォームのコントロールによる 日付の入力 を想定していますけど
入力をスキップすると、自動的に現在の月が設定されます。
クロス集計クエリを使ったクエリを基にするとそうなるようです
クロス集計クエリ(Q入出庫明細のクロス集計クエリ)にクエリ列見出しを設定しておくとレポートウィザードでもフィールド名が表示されるようになります
mayu様
返信が遅くなり申し訳ありません。レポートを作成できました。ありがとうございます。
質問ですが作成していただいたSQL文にx.~やy.~と記載されていますがどのような意味があるのでしょうか。(IIF関数に必要な条件のようなものでしょうか)
見当違いで意味がないのであれば申し訳ありません。
x や y は、 エイリアス と言って
SQL文の中で、一時的に テーブルやクエリに 別の名前をつけています。
x ---> 製品 ( 実在のテーブル )のこと
y ---> ユニオンクエリ ( SELECT 入庫詳細.製品ID ... DateSerial( ... , 0 ) ) のこと
私がエイリアスを付与する理由は、主に以下のようなものになります。
ですから、単一のテーブルのみを使用している SQL では
エイリアスの付与も、オブジェクト名の修飾も不要です。
横から失礼。
はじめまして、スナフキンと申します。
少し気になって、期首在庫は必要ないのでしょうか?
それによってはすべてアウトになりそうで^^;;
一応、標準モジュールに
Public Function F_出庫数(int製品ID As Integer, str年月 As String, Optional bln集計区分 As Integer = 0) As Double
Dim strQuery As String
If bln集計区分 = 0 Then
strQuery = "SELECT SUM(S.出庫数量) AS 出庫数量 FROM 出庫詳細 S LEFT JOIN 出庫 SH ON S.出庫ID=SH.出庫ID WHERE S.製品ID=" & int製品ID & " AND Format(SH.出庫日,'YYYYMM')='" & str年月 & "'"
Else
strQuery = "SELECT SUM(S.出庫数量) AS 出庫数量 FROM 出庫詳細 S LEFT JOIN 出庫 SH ON S.出庫ID=SH.出庫ID WHERE S.製品ID=" & int製品ID & " AND Format(SH.出庫日,'YYYYMM')<'" & str年月 & "'"
End If
Dim cn As New ADODB.Connection
Dim rec As New ADODB.Recordset
Set cn = CurrentProject.Connection
Set rec = cn.Execute(strQuery)
If rec.EOF = False Then
F_出庫数 = Nz(rec.Fields("出庫数量"), 0)
Else
F_出庫数 = 0
End If
rec.Close: Set rec = Nothing
cn.Close: Set cn = Nothing
End Function
Public Function F_入庫数(int製品ID As Integer, str年月 As String, Optional bln集計区分 As Integer = 0) As Double
(出庫と同じなので省略)
End Function
上記参照設定でADOにチェックです^^/
を用意したとして
製品IDを1
2020年1月の場合
前月在庫数=F_入庫数(1,"202001",1)-F_出庫数(1,"202001",1)
当月入庫数=F_入庫数(1,"202001")、当月出庫数=F_出庫数(1,"202001")
当月在庫数=F_入庫数(1,"202001",1)-F_出庫数(1,"202001",1)+F_入庫数(1,"202001")-F_出庫数(1,"202001")
上記でどうでしょうか?
クエリーでも使用できると思いますし、少し工夫すれば、期首在庫にも対応できるはず^^;;
かな??
フック船長さんのテーブル構成だと、棚卸をおこなった時点で
のどちらかで実現可能でしょうし、
テーブル設計が きちんと出来ていることから
フック船長さんは こういった業務知識や手法を身につけている可能性が高いのではないか
というのが、私の見解です。
N + 1 問題
に該当しています、とだけ。
皆様お久しぶりです。
いろいろとありがとうございました。
特にmayu様、hiroton様、本当にありがとうございました。
作っていただきました構文を業務で使わせていただいております。
やっていく中で改善点があり、またこちらのサイトで教えていただくことがあるかと思います。
その際はよろしくお願いします。
上段で期首在庫が必要かという質問がありましたので私の見解を伝えさせていただきます。
私自身、簿記を勉強した経験(3級取得ですが…)から、先入先出法の考え方を持っております。
テーブルには入ってませんがで入庫単価(仕入単価)・出庫単価(売上単価)のフィールドを作り、
クエリで入庫金額、出庫金額(売上金額)、期首前在庫単価、期首前在庫価格を作れば
物の流れやお金の流れが分かりやすくなるのではないかと思ったからです。
いずれはここまでもっていけたらと思っていますが……
参考サイト
https://petit-blog.com/boki/shikurikurishi/