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

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

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

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

SQL

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

Q&A

解決済

2回答

1954閲覧

【SQL Server】重複データをループで削除する方法

zeze

総合スコア27

SQL Server

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

SQL

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

0グッド

0クリップ

投稿2021/08/19 09:19

編集2021/08/20 01:47

前提・実現したいこと

重複データを論理削除でUPDATEするSQL文を作成しています。

以下のようなテーブルがあるとします。

order_idmodelcolor_codecolor_namesize_codesize_nameinsert_datedel_flg
45TD001NULLBLNULLL2021-08-05 14:22..0
54TD001NULLBLNULLL2021-08-05 14:26..0
59TD001NULLBLNULLL2021-08-05 14:30..0
80TD001NULLBLNULLL2021-08-05 14:40..0
46TD002NULLGNNULLM2021-08-06 14:22..0
55TD002NULLGNNULLM2021-08-06 14:26..0
60TD002NULLGNNULLM2021-08-06 14:30..0
81TD002NULLGNNULLM2021-08-06 14:40..0

・order_id(PK,bigint,NULL以外) 一意
・model(nvahar(100)、NULL以外)
・color_code(nvahar(100)、NULL)
・color_name(nvahar(100)、NULL)
・size_code(nvahar(100)、NULL)
・size_name(nvahar(100)、NULL)
・insert_date(datetime、NULL)
・del_flg(bit、NULL以外) 0 or 1

該当のソースコード

要件
・重複データの中で、insert_dateで最も新しいデータ以外の古いデータをUPDATEする。
・UPDATEで削除対象のdel_flgを0→1にする。
・各カラムの値を変数に入れて、ループを回したい。。
┗ 上記例だと抽出後2レコードのため、1回ループで2回試行する。

現状、UPDATEはできているのですが主に
・マジックナンバーで指定している
・ループを回す必要がある
ため改修の必要があります。ご教示お願いします。

※追記
現時点でループを使うパターンでしか考えが及びませんでした
本来、ループ使わない方が望ましいので、使わないパターンでご教授いただけると大変助かります。

※追記2
運用上、最も新しいデータのみ残すのが正解でしたので要件を変更しました。
昇順から降順になります。

SQL

1 2-- 重複データの抽出 3SELECT 4 T2.model,T2.color_code,T2.color_name,T2.size_code,T2.size_name ,count(*) 5FROM 6 dbo.order1_trn AS T2 7WHERE 8 T2.del_flg='0' 9GROUP BY 10 T2.model,T2.color_code,T2.color_name,T2.size_code,T2.size_name 11HAVING 12 count(T2.del_flg) > 1 13 14-- UPDATE対象を確認。(insert_dateで最も古いデータ以外がUPDATE対象) 15 16SELECT order_id,insert_date,record_num FROM ( 17 SELECT 18 order_id 19 ,insert_date 20 ,ROW_NUMBER() OVER (ORDER BY insert_date ASC) AS record_num 21 FROM 22 dbo.order1_trn 23 WHERE 24 del_flg='0' 25 AND model='TD001' 26 AND color_code IS NULL 27 AND color_name='BL' 28 AND size_name='L' 29 AND size_code IS NULL 30) AS T2 31WHERE 32 record_num <=(SELECT count(*)-1 FROM dbo.order1_trn WHERE del_flg='0' AND model='TD001' AND color_code IS NULL AND color_name='BL' AND size_name='L' AND size_code IS NULL ) 33 34-- 対象をUPDATEする 35UPDATE dbo.order1_trn SET del_flg='1' WHERE order_id IN( 36SELECT order_id FROM ( 37 SELECT 38 order_id 39 ,insert_date 40 ,ROW_NUMBER() OVER (ORDER BY insert_date ASC) AS record_num 41 FROM 42 dbo.order1_trn 43 WHERE 44 del_flg='0' 45 AND model='TD001' 46 AND color_code IS NULL 47 AND color_name='BL' 48 AND size_name='L' 49 AND size_code IS NULL 50) AS T2 51WHERE 52 record_num <=( 53 SELECT 54 count(*)-1 55 FROM 56 dbo.order1_trn 57 WHERE del_flg='0' AND model='TD001' AND color_code IS NULL AND color_name='BL' AND size_name='L' AND size_code IS NULL ) 58) 59 60-- 結果確認用 61 62SELECT 63 T2.model,T2.color_code,T2.color_name,T2.size_code,T2.size_name ,count(*) 64FROM 65 dbo.order1_trn AS T2 66WHERE 67 T2.del_flg='0' 68GROUP BY 69 T2.model,T2.color_code,T2.color_name,T2.size_code,T2.size_name 70HAVING 71 count(T2.del_flg) > 1 72 73

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

SQL Server Management Studio 15.0.18382.0

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

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

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

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

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

ishina_yum

2021/08/19 10:19

要件が良く分からないのですが、これは何かの課題でしょうか? 『ループを使う』というのは言語は何を想定しているのでしょう? ストアドプロシージャ(Transact-SQL)ですか? それともC#やVB.Net? もしくはJava? PHP、Ruby、Pythonなどのスクリプト? > 抽出後2レコードのため、2回ループさせたい ちょっと意味が分からない…… ループは1回じゃないの?? というか、ループせずにSQL一発でUPDATEしたらダメなの?
zeze

2021/08/19 12:47

分かりにくくてすみません。。 > これは何かの課題でしょうか? 仕事でツールを作成しています。SQLは初心者でして勉強しています。。 >『ループを使う』というのは言語は何を想定しているのでしょう? > ループせずにSQL一発でUPDATEしたらダメなの? Transact-SQLを想定していますが、ループしなくて済むなら一発で実行したいです。 ループで回すことしか現時点では考えられませんでした。。 > ループは1回じゃないの?? 1回ですね。。間違えました。1回ループで2回試行します。
guest

回答2

0

ベストアンサー

削除対象の一覧はこんな感じで出せるわけです。

SQL

1SELECT * 2FROM 3( 4SELECT 5 order_id 6, model 7, color_code 8, color_name 9, size_code 10, size_name 11, insert_date 12, ROW_NUMBER() OVER 13 (PARTITION BY -- 重複検査をしたいカラムをここで並べる 14 model 15 , color_code 16 , color_name 17 , size_code 18 , size_name 19 ORDER BY insert_date ASC -- 一番古い物を残すため昇順 20 ) AS record_num 21FROM 22 dbo.order1_trn 23) x 24WHERE x.record_num > 1

重複を探したいものをROW_NUMBER() OVERPARTITION BYに指定してやればその中で1から順に番号が振られます。
もし、重複しているレコードがなければ、1だけですね。

そして、1を除去してやれば、重複している残りだけになります。

あとはこれをUPDATEに組み込めば終わりです。

基本的には、対象となるレコードを抽出するSELECT分を作ることをとにかく考えることです。

重複の抽出には色々と方法がありますが、たとえばGROUP BYしてinsert_date の最小値を取ってそれ以外とする場合、万が一、insert_dateまで同じレコードがあった場合に処理が漏れることになります。

そんなデータは絶対にない!という場合でも、重複の検査に使う model などにNULLがある場合を考えると面倒なことになります。

それも絶対にありえない!という確証があればそれでもいいですけれど、確認を怠ると面倒なことになります。

そういう細かい条件(実際にどのようなデータがあるか)は作るSQLにも影響しますので、可能な限り事前に確認するよう心がけましょう。

投稿2021/08/19 13:43

編集2021/08/19 14:13
ishina_yum

総合スコア509

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

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

zeze

2021/08/20 02:06 編集

回答ありがとうございます。UPDATEできました! 万が一を想定して各カラムの事前確認が大事ですね。精進します!
guest

0

重複データの中で、insert_dateで最も古いデータ1つ以外をUPDATEする。
-- UPDATE対象を確認。(insert_dateで最も古いデータ以外がUPDATE対象)

2021/08/19 22:21 のコメントに基づき、SQL文を編集

model 毎に 最新の insert_date に該当しなくなったレコード
del_flg列 を 1 に更新したい
ということですよね。

  • model
  • color_code
  • color_name
  • size_code
  • size_name

毎に
最古の insert_date に該当しないレコードの del_flg 列 を 1 に更新
というロジックに変更。

色々な記述方法があるのですけど、ここでは2種類のSQLを載せておきます。

SQL

1UPDATE q 2 SET del_flg = 1 3FROM 4( 5 SELECT order_id 6 , model 7 , del_flg 8 , row_number() 9 over( partition by model 10 , coalesce( color_code, N'' ) 11 , coalesce( color_name, N'' ) 12 , coalesce( size_code, N'' ) 13 , coalesce( size_name, N'' ) 14 order by insert_date asc 15 , order_id asc 16 ) grank 17 FROM order1_trn 18 WHERE del_flg = 0 19) q 20WHERE grank > 1 21;

SQL

1UPDATE x 2 SET del_flg = 1 3 FROM order1_trn x 4 WHERE x.del_flg = 0 5 AND EXISTS ( 6 SELECT 1 FROM order1_trn y 7 WHERE y.del_flg = 0 8 AND y.model = x.model 9 AND coalesce( y.color_code, N'' ) = coalesce( x.color_code, N'' ) 10 AND coalesce( y.color_name, N'' ) = coalesce( x.color_name, N'' ) 11 AND coalesce( y.size_code, N'' ) = coalesce( x.size_code, N'' ) 12 AND coalesce( y.size_name, N'' ) = coalesce( x.size_name, N'' ) 13 AND ( y.insert_date < x.insert_date 14 OR 15 y.insert_date = x.insert_date 16 AND 17 y.order_id < x.order_id 18 ) 19 ) 20;

投稿2021/08/19 11:49

編集2021/08/19 21:41
mayu-

総合スコア335

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

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

zeze

2021/08/19 13:21

回答ありがとうございます。 ご提案いただいた内容、現時点で再現できていませんが model 毎に 最新の insert_date に該当しなくなったレコード ではないです。 データの例が悪かったですね。。すみません。 model,color_code,color_name,size_code,size_name の5カラムで重複を確認して 得られた中で古いレコード1つを残して、残りをUPDATEしたいです。 こちらですと、下記のようにmodel TD001が同じで、他4つが異なっている場合もデータとしては含まれています。 TD001 NULL BL NULL L 2021-08-05 14:22.. 0 TD001 NULL GN NULL L 2021-08-05 14:23.. 0 TD001 NULL PI NULL M 2021-08-05 14:24.. 0
mayu-

2021/08/19 21:44 編集

- color_code(X) と color_name(Y) - size_code(X) と size_name(Y) は、それぞれ関数従属(X → Y)、 つまり、(key → value)の関係にはならないのでしょうか。 そうすると、母集団( グルーピング項目 )に指定する列は - model - color_code - size_code の3つに限定できそうなのですけど、何か事情がありそうですね。
zeze

2021/08/20 01:35

回答ありがとうございます。 従属関係にはなりません。
zeze

2021/08/20 02:02 編集

1つめのSQL試しましたが、無事処理できました。 ありがとうございます。coalesceやNプレフィックスなど理解して使いこなしたいと思います。
mayu-

2021/08/20 02:16 編集

入れ違いになりました。エラーの原因はフィールドの型違いだったようですね。 ご自身で処理できたようで何よりです。 テーブル定義の箇所: ・del_flg(bit、NULL以外) 0 or 1 SQL文の記述: ( 単一引用符で値を括る記述はbit型では使いません。varcharなどの文字列型 ) del_flg='0'
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.37%

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

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

質問する

関連した質問