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

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

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

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

SQL

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

Q&A

解決済

1回答

1363閲覧

SQLserverでIFで処理分岐後、結果を結合(union)したい

thizm

総合スコア5

SQL Server

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

SQL

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

0グッド

0クリップ

投稿2023/08/15 03:10

編集2023/08/16 04:24

実現したいこと

以下の利用履歴(history)のテーブルから、手持ちのscidリスト(.csv)を元にselect文を作って、かつ usedateの最新のレコードを抽出し、scid が存在しない場合は意図的に1行のダミーデータを入れて一つの結果にしたいです。(ダミーでなくても、結果(理想2)のように、とにかく1行何もないデータでもOKです。)

テーブル名:history

scidpcodenameusedate
20391suzuki2004-11-18
15825hasegawa2016-08-10
22153tanaka2020-05-15
15822hasegawa2021-11-11
20392suzuki2015-10-05
20395suzuki2023-07-25

結果(理想1)

scidpcodeusedate
203952023-07-25
nonenonenone
158222021-11-11

結果(理想2)

scidpcodeusedate
203952023-07-25
   
158222021-11-11

前提

別に scid のみの一覧のリストがあって、select分を大量に発行し、実行しているのですが、scidリスト を元に select してレコードが無い場合、以下のコードのように if で処理分岐させて空のデータを意図的に流しこんでいます。

※手持ちの scid のリスト(.csv)は 約5万件ほどあります。

sql

1IF EXISTS(SELECT scid,usedate,pcode from "history" where usedate = (SELECT MAX(usedate) FROM "history" WHERE scid = '2039') and scid = '2039') 2 BEGIN 3 select scid,usedate,pcode from "history" where usedate = (SELECT MAX(usedate) FROM "history" WHERE scid = '2039') and scid = '2039' 4 END 5ELSE 6 BEGIN 7 SELECT 'none' AS scid, 'none' as usedate,'none' as pcode 8 END

単純に1処理であれば、問題ないのですが、以下のように複数同時に取得したいと思い単純にunionで結合してみました。

sql

1IF EXISTS(SELECT scid,usedate,pcode from "history" where usedate = (SELECT MAX(usedate) FROM "history" WHERE scid = '2039') and scid = '2039') 2 BEGIN 3 select scid,usedate,pcode from "history" where usedate = (SELECT MAX(usedate) FROM "history" WHERE scid = '2039') and scid = '2039' 4 END 5ELSE 6 BEGIN 7 SELECT 'none' AS scid, 'none' as usedate,'none' as pcode 8 END 9UNION 10IF EXISTS(SELECT scid,usedate,pcode from "history" where usedate = (SELECT MAX(usedate) FROM "history" WHERE scid = '99999') and scid = '99999') 11 BEGIN 12 select scid,usedate,pcode from "history" where usedate = (SELECT MAX(usedate) FROM "history" WHERE scid = '99999') and scid = '99999' 13 END 14ELSE 15 BEGIN 16 SELECT 'none' AS scid, 'none' as usedate,'none' as pcode 17 END 18

発生している問題・エラーメッセージ

unionで結合しようとすると、次のエラーが出てしまいます。。。

/* SQL エラー (156): キーワード 'IF' 付近に不適切な構文があります。 */

試したこと

SQLに関しては素人なので、方法を考えたのですがまったく思いつきません。。

補足情報(FW/ツールのバージョンなど)

使用しているSQL
Microsoft SQL Server 2008 R2 (SP2)

使用しているSQLクライアント
Heidi SQL

※その後について記入しましたが、あくまでも素人による解決の仕方です。ご指摘

その後について

neko_the_shadow さんに提示していただいたコードで見事に実現できました!が、3000件を超えたあたりでタイムアウトエラーに…
紆余曲折を得て、さらに別の方法でのコードを書いてみました。

SQL

1SELECT scid,pcode,usedate FROM "history" T1 2 WHERE NOT EXISTS ( 3 SELECT * FROM "history" T2 WHERE T1.scid = T2.scid AND T1.usedate < T2.usedate 4 ) AND scid = '2039 ' 5UNION ALL 6SELECT NULL, NULL, NULL -- 必要なだけNULL値を追加 7WHERE NOT EXISTS ( 8 SELECT scid,pcode,usedate FROM "history" T1 9 WHERE NOT EXISTS ( 10 SELECT * FROM "history" T2 WHERE T1.scid = T2.scid AND T1.usedate < T2.usedate 11 ) AND scid = '2039 ');

こちらのコードでも実現できました。
ただし、こちらでも大量にセレクト文を発行するとタイムアウトやメモリ不足でエラーが出てしまいました。

その他の解決方法へ

  • メモリ不足については、一時テーブルを作って回避できるかな?
  • タイムアウトについては、処理を分割して実行すれば問題ないかな?

ということで、試してみました。

一時テーブルを作成

SQL

1CREATE TABLE "#tmp_history"( -- テーブル名の頭に # をつけることで一時的なテーブルを作成できる 2 scid VARCHAR(100) NULL 3 ,usesdate date NULL 4 ,pcode varchar(100) NULL 5)

セレクトしつつ、作成した一時テーブル #tmp_history へインサートする。
これ1処理1行にまとめて手持ちのscid分コードを生成…手持ちのscidリスト(.csv)は、全部で5万件あるので
一度にすべてでなく SQLファイルに 処理を3000件ずつ分けて list_01.sql~list_17.sql ファイルを生成。

SQL

1INSERT INTO "#tmp_history" (scid,usedate,pcode) 2SELECT TOP(1) scid,usedate,pcode FROM "history" T1 3 WHERE NOT EXISTS ( 4 SELECT * FROM "history" T2 5 WHERE T1.scid = T2.scid AND T1.usedate < T2.usedate 6 ) AND scid = '69112' 7UNION ALL 8SELECT NULL, NULL, NULL 9 WHERE NOT EXISTS ( 10 SELECT scid,pcode,usedate FROM "history" T1 11 WHERE NOT EXISTS ( 12 SELECT * FROM "history" T2 13 WHERE T1.scid = T2.scid AND T1.usedate < T2.usedate 14 ) AND scid = '69112' 15 );

HeidiSQLクライアントにて、[ファイル]→[SQLファイルを実行]で作成したSQLファイルすべて選択して
全ての処理を終えました。

取り急ぎこのような流れで解決しましたので、参考になればと思います。

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

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

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

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

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

guest

回答1

0

ベストアンサー

手持ちのscidリスト

これがどういう形式なのかわからないのですが、scidの一覧とその順番があると仮定すると、以下のようなSQLで実現できると思います。

SQL

1SELECT H.scid, H.pcode, H.usedate 2FROM ( 3 SELECT 1, '2039' 4 UNION ALL SELECT 2, '9999' 5 UNION ALL SELECT 3, '1582' 6) T (seq, scid) 7LEFT OUTER JOIN ( 8 SELECT *, ROW_NUMBER() OVER (PARTITION BY scid ORDER BY usedate DESC) AS rn 9 FROM history 10) AS H 11ON T.scid = H.scid AND H.rn = 1 12ORDER BY T.seq

投稿2023/08/15 03:37

neko_the_shadow

総合スコア2379

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

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

thizm

2023/08/15 03:44

手持ちのscidリストについて説明が無くて申し訳ありません。 これは単純にCSVデータになります。(質問文を修正しました。) これを元に、select文を作っていました。 コードのご提示ありがとうございます。早速試して、フィードバックを送りたいと思います。
thizm

2023/08/15 04:15

素晴らしい!実現できました!!!!!2日ほど悩んでいたのですが、大変助かりました。。 実際に300件selectしてみましたが、最初の処理7~8秒ほどかかってパッと表示されました。 後はコピペだけじゃなくて、このコードの流れ・仕組みを自分なりに理解して解読してみます。 私は社内でデザイナーとして従事してる傍ら、SE的なこともしています。周りはITとは程遠い方達で、相談できる人がまったくいないので、大変助かりました。 にしても、専門的なプロは、さすがですね。要件をくみ取ってサッとコードをかける…。 ググりながらコードを必死で書いてる自分が情けなくなります。。 余談が過ぎましたが、御礼の意味も込めてベストアンサーにさせていただきます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.31%

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

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

質問する

関連した質問