質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
SQL Server

SQL Serverはマイクロソフトのリレーショナルデータベース管理システムです。データマイニングや多次元解析など、ビジネスインテリジェンスのための機能が備わっています。

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

Q&A

解決済

5回答

29599閲覧

SQL 最新レコードとの結合方法

seesaajira-

総合スコア107

SQL Server

SQL Serverはマイクロソフトのリレーショナルデータベース管理システムです。データマイニングや多次元解析など、ビジネスインテリジェンスのための機能が備わっています。

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

2グッド

1クリップ

投稿2016/09/01 02:59

編集2016/09/02 00:33

イメージ説明
イメージ説明

顧客別品名マスタ(以下、品名M)と顧客品名変更履歴ファイル(以下、履歴F)があります。
(履歴Fは品名Mを変更する都度、レコードが増えていきます。)
※履歴レコードは品名Mに対して存在しない場合もあります

<ユニークキー>
品名M:顧客コード、品名コード
履歴F:顧客コード、品名コード、No

以下の条件でSQLを作成したのですが、問題ないでしょうか?
他に良い案があれば教えて下さい。

<絞込条件>
・指定した顧客のみの品名を絞込む
・品名Mとリンクする履歴Fのレコードが存在する場合、最新レコードのみ結合する

(使用DB:SQLSERVER)

SELECT 品名,変更者,変更日,変更理由 FROM 品名M LEFT JOIN 履歴F ON 品名M.顧客コード = 履歴F.顧客コード AND 品名M.品名コード = 履歴F.品名コード AND 履歴F.No = (SELECT MAX(No) FROM 履歴F R2 WHERE 品名M.顧客コード = R2.顧客コード AND 品名M.品名コード = R2.品名コード GROUP BY R2.顧客コード,R2.品名コード)
yodel, Gyoubu👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答5

0

SQLServerの分析関数の使用例の「4. 最大値の行の取得」
https://oraclesqlpuzzle.ninja-web.net/sqlserver2008-sql1-olap.html#1-4
を見ながら作ってみました :-)

sql

1with tmp履歴F as( 2select 顧客コード,品名コード 3 from (select 顧客コード,品名コード,No, 4 max(No) over(partition by 顧客コード,品名コード) as MaxNo 5 from 履歴F) a 6 where No = MaxNo) 7select 品名,変更者,変更日,変更理由 8 from 品名M Left Join tmp履歴F 9 on 品名M.顧客コード = tmp履歴F.顧客コード 10 AND 品名M.品名コード = tmp履歴F.品名コード

Outer Applyを使う方法もあるようですが、
まだ勉強中なのであった。

投稿2016/09/03 05:20

編集2019/03/12 06:36
AketiJyuuzou

総合スコア1147

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2016/09/03 05:28

別名定義(WITH句)で指定している名称と LEFT JOIN している 名称が違う気が
AketiJyuuzou

2016/09/03 05:46

名称が間違ってたので、修正しました。
seesaajira-

2016/09/05 00:54

AketiJyuuzouさん、回答ありがとうございます。 「SQLServerの分析関数の使用例」のリンクありがとうございます。 非常に詳しく説明されているので勉強になります。 SQLって奥が深いんですね。 初心者の私には思いつきもしないSQL文です。
guest

0

ベストアンサー

つっこんだので私なりの回答

sql

1SELECT M1.品名 2 , F1.変更者 3 , F1.変更日 4 , F1.変更理由 5FROM 6 品名M M1 7 LEFT JOIN 履歴F F1 8 ON M1.顧客コード = F1.顧客コード 9 AND M1.品名コード = F1.品名コード 10 -- グルーピング項目で絞ってるため GROUP BY 不要 11 -- LEFT JOIN の条件として指定することで 存在しない場合でも マスターは表示される 12 AND F1.No = ( 13 SELECT MAX(F2.No) 14 FROM 履歴F F2 15 WHERE F2.顧客コード = F1.顧客コード 16 AND F2.品名コード = F1.品名コード 17 ) 18;

ふと思った別案 SQLServer で対応可能かは知らないが

sql

1WHERE NOT EXISTS ( 2 SELECT 1 3 FROM 履歴F F2 4 WHERE F2.顧客コード = F1.顧客コード 5 AND F2.品名コード = F1.品名コード 6 AND F2.No > F1.NO 7)

でも行けそうな予感

投稿2016/09/02 03:39

編集2016/09/03 02:01
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2016/09/02 03:47

まあ 履歴テーブルに lasted (最終更新)というカラムを追加したほうが性能はいいです。 ※サブクエリを行わないでいいため
seesaajira-

2016/09/02 04:38

haruka-kanataさん、回答ありがとうございます。 試したところ、確かにGroup byは要らなかったですね。 成程、lasted(最終更新)の案、その手がありましたね。 難しく考えすぎて思いつきませんでした。
Panzer_vor

2016/09/02 05:21

> haruka-kanataさん > seesaajira-さん テーブル定義の変更が可能ならそれが一番すっきりしますね。 その場合だと履歴作成時に、 最終履歴の更新し直しも発生するので、 ストアド化されていないのなら、ストアド化するのが更新ミスを抑制する上でも良い気がします。
guest

0

パフォーマンス上問題が発生していないのであれば、
掲示されておられるコードが一番やりたいことを明確に表現したクエリとなっていますので特に変更はいらないかと思われます。

敢えて何か助言するとしましたら、
履歴テーブルが2度出てくるのでテーブル別名を付けてあげるとか、
SELECT句のカラムにどちらのテーブルから取得しているかが一目で分かるようにテーブル名、またはテーブル別名を指定した方が親切かなといったぐらいですかね。

以下蛇足ですが、
仮に上記のクエリでパフォーマンス上に問題が出ているなどがある場合は、
顧客コード、品名コード、履歴No.の最大値を取得するサブクエリを、
外部結合でくっつけるという方法の方が速度が出るかもしれません。
(※こればかりは実行計画の違いがでるかどうか確認しないと最終的な判断はできませんが^^;)

SQL

1SELECT 2 M.品名 3, H.変更者 4, H.変更日 5, H.変更理由 6FROM 7 品名M M 8 LEFT JOIN( 9 SELECT 10 H1* 11 H2.MAX_NO 12 FROM 13 履歴F H1 14 INNER JOIN ( 15 SELECT 16 顧客コード 17 , 品名コード 18 , MAX(NO) AS MAX_NO 19 FROM 20 履歴F 21 GROUP BY 22 顧客コード 23 , 品名コード 24 ) H2 25 ON H1.顧客コード = H2.顧客コード 26 AND H1.品名コード = H2.品名コード 27 AND H1.NO = H2.MAX_NO 28 ) H 29 ON M.顧客コード = H.顧客コード 30 AND M.品名コード = H.品名コード

書いてる途中にこっちの方がダメそうな気がしてきた・・・

投稿2016/09/01 11:06

Panzer_vor

総合スコア1636

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

seesaajira-

2016/09/02 00:22

KotoriMaturiさん、回答ありがとうございます。 テーブル別名にすると、確かに分かり易いですね。 サブクエリを外部結合でくっつけるという方法もあるのですね。 パフォーマンスに影響が出てきたら試してみたいと思います。
guest

0

SQL Serverの微妙な文法の違いはよくわかりませんが
私はこんな感じでよくやります

SQL

1SELECT 品名,変更者,変更日 2FROM 品名M 3LEFT JOIN 履歴F 4ON 品名M.顧客コード = 履歴F.顧客コード 5AND 品名M.品名コード = 履歴F.品名コード 6AND (履歴F.No,履歴F.顧客コード,履歴F.品名コード) IN 7(SELECT MAX(No),顧客コード,品名コード FROM 履歴F 8GROUP BY 顧客コード,品名コード)

#結局
冗長になりますがこうなんでしょうね・・・

SQL

1SELECT 品名,変更者,変更日 2FROM 品名M 3LEFT JOIN 4(SELECT 変更者,変更日,履歴F.顧客コード,履歴F.品名コード 5FROM 履歴F INNER JOIN 6(SELECT MAX(No) AS No,顧客コード,品名コード FROM 履歴F 7GROUP BY 顧客コード,品名コード) AS SUB 8ON 履歴F.No=SUB.No 9AND 履歴F.顧客コード=SUB.顧客コード 10AND 履歴F.品名コード=SUB.品名コード) AS SUB 11ON 品名M.顧客コード = SUB.顧客コード 12AND 品名M.品名コード = SUB.品名コード ;

ちなみに、INの複数指定ができないということはUSINGの複数してもできないのでしょうか?

SQL

1SELECT 品名,変更者,変更日 2FROM 品名M 3LEFT JOIN 4(SELECT 変更者,変更日,顧客コード,品名コード 5FROM 履歴F INNER JOIN 6(SELECT MAX(No) AS No,顧客コード,品名コード FROM 履歴F 7GROUP BY 顧客コード,品名コード) AS SUB 8USING(No,顧客コード,品名コード)) AS SUB 9USING(顧客コード,品名コード);

投稿2016/09/01 03:57

編集2016/09/02 03:39
yambejp

総合スコア114767

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

seesaajira-

2016/09/01 04:04

なるほど、Inを使用する方法があるのですね。 早速、試してみます!
seesaajira-

2016/09/01 04:04

回答、ありがとうございました。
Panzer_vor

2016/09/01 08:19

> yambejpさん SQL Serverは行値構成子の書き方をサポートしていなかった記憶が・・・。 勘違いだったらすみません。 SQL99から標準の仲間入りしてるので、まだサポートされてない状況なら対応してほしかったりしますが^^;
yambejp

2016/09/01 08:27

KotoriMaturiさん、ご指摘ありがとうございます 勉強になります。 なにせMySQL使いなもので文法違いについては配慮いたしますが 方言の吸収については各自個別対応ということでご容赦ください
seesaajira-

2016/09/02 00:05

yambejpさん、KotoriMaturiさんコメントありがとうございます。 やはりSQLSERVERだと複数項目にINを使うとエラーになりました。 勉強になりました。
Panzer_vor

2016/09/02 03:18

> seesaajira-さん やはりまだ未サポートだっとんですね^^; ただ標準SQL(SQL仕様の定義みたいなもの)では行値構成子は定義されてますし、 Microsoft製のDBMS以外の主要なDBMS(Oracle、MySQL、PostgreSQLなど)はサポート済なので、 よほどのことがない限りはいずれサポートされるものかと思います。
yambejp

2016/09/02 03:42

動かないままソースを晒しておくのもなんなんで、普通に結合するソースを追記しておきました。INがだめならUSINGもだめでしょうね・・・
Panzer_vor

2016/09/02 11:21

> yambejpさん 僕の方が普段USING句を使う習慣がなかったので分からないのと、サポート状況は気になったので少しググってみました。 → https://en.m.wikipedia.org/wiki/Join_(SQL)#cite_ref-5 英語サイトで申し訳ないのですが、「Natural Join」の節のすぐ上で申し訳程度にUSING句に触れているのですが、 それを見るとUSING句自体SQL Serverではサポート外のようです。(それとSybase)
guest

0

問題ありません。
HAVING句やEXISTS句や
ORDER BY + Limit 1を使って抽出も可能ですが、
私はseesaajira-さんが記載してあるSQL文が好みです。
強いて言えば、JOIN の向きを逆にするかな、というくらいです。

SQL

1SELECT M1.品名 2 , F1.変更者 3 , F1.変更日 4 , F1.変更理由 5FROM 履歴F F1 6 LEFT JOIN 品名M M1 ON ( 7 M1.顧客コード = F1.顧客コード 8 AND M1.品名コード = F1.品名コード 9 ) 10WHERE F1.No = ( 11 SELECT MAX(F2.No) 12 FROM 履歴F F2 13 WHERE F2.顧客コード = F1.顧客コード 14 AND F2.品名コード = F1.品名コード 15 GROUP BY F2.顧客コード 16 , F2.品名コード 17 ) 18;

投稿2016/09/01 03:49

編集2016/09/01 04:04
tomari_perform

総合スコア760

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

seesaajira-

2016/09/01 04:09

回答、ありがとうございます。 JOINの向きが逆との事ですが、履歴Fのレコードは品名Mのレコードに対して有ったり無かったりするのですが、やはり逆なのでしょうか? 試してみたいと思います。
tomari_perform

2016/09/01 04:14 編集

おっと。。。失礼しました。 履歴Fのテーブルにあったりなかったりする場合、逆にしてはダメです。 なので、私もseesaajira-さんが記載してあるSQL文にします。 ちなみに、今回のケースにおいて、 IN句はデータ量が多いと性能が出ないため、 私なら使いませんが、試してみてくださいな。
seesaajira-

2016/09/01 04:20

早速のコメントありがとうございます。 やはり逆はまずいですよね。 私の質問もまずかったので、後で訂正しておきます。 ありがとうございました!
tomari_perform

2016/09/01 04:22

質問の仕方はまずくないです! 申し訳ないですが、私の見落としです。 ちゃんと、データ例にて表現されていますので、問題ありません!
退会済みユーザー

退会済みユーザー

2016/09/02 03:25

WHERE F.カラム = ( サブクエリ ) ってしてる場合に 履歴テーブルがない M レコードがでてこないから WHERE F.カラム = ( サブクエリ ) OR F.カラム IS NULL じゃないかなとおもったり
Panzer_vor

2016/09/03 01:52

凄まじく蛇足となりますが、以下参考までに。 SQL ServerではLimit句というものはないので、 ORDER BY + LIMITの戦略でいく場合は、 同様機能TOP句を使う方法となります。 SELECT TOP(1) t.hoge FROM table t ORDER BY t.hoge
tomari_perform

2016/09/03 03:42

haruka-kanata 様 補足ありがとうございます。 根本的に私のSQLは間違っておりまして、すみません。  FROM 履歴F F1 の時点で、F.カラム IS NULL という行自体が出てこないため、 私のSQLでは、JOINの順番がおかしいんです。(汗 KotoriMaturi 様 補足ありがとうございます。 SQL Serverであることを見落としてました。。 ご指摘の通りでございますm(__)m
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問