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

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

新規登録して質問してみよう
ただいま回答率
85.49%
SQLite

SQLiteはリレーショナルデータベース管理システムの1つで、サーバーではなくライブラリとして使用されている。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

3回答

4472閲覧

PythonによるSQLite上での重複の削除

Shu0101

総合スコア14

SQLite

SQLiteはリレーショナルデータベース管理システムの1つで、サーバーではなくライブラリとして使用されている。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2020/06/11 14:21

編集2020/06/12 07:12

前提・実現したいこと

こんにちは。
Python上でCSVファイルを読み込み、SQLite3にデータベースを作成しています。
その際、重複した行を削除しようとしているのですが、以下のSelect Distinct(変数2つ)ではうまくいきません。
読み込むCSVは毎日更新されるので、Primary Keyによる制限をかけない方針です。
(制限をかけると、CSVファイルそのものが読み込まれないという理解です。)
←理解が間違っておりました。
初めてSQLを使ったため、的外れな質問であれば恐縮です。
よろしくお願いいたします。

該当のソースコード

Python3

1conn = sqlite3.connect('database_name') 2sql = """ 3SELECT 4 DISTINCT `column_name1`, 5 ~~DISTINCT~~ `column_name2` 6FROM 7 'table_name' 8""" 9conn.execute(sql)

【追記】

・CSVはForループで順次読み込まれます
・重複を許してSQLに格納したのち、重複したものを消す手法は試しましたが、Lockされてしまい断念しました。

【追記】該当のソースコード

Python3

1import sqlite3 2db_file_name = database_name 3conn = sqlite3.connect(db_file_name) 4sql1 = """ 5delete from table_name 6where rowid not in 7 ( 8 select min(rowid) 9 from prices 10 group by 11 column_name1 12 , column_name2 13 ) 14""" 15conn.execute(sql1)

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

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

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

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

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

guest

回答3

0

Sqliteにはrowidと言う行を識別するint型の特殊なカラムがあります。

SQL

1select 2 max(rowid), 3 column_name1, 4 column_name2 5from table_name 6group by 7 column_name1, 8 column_name2

とすると(column_name1, column_name2)の組み合わせについて最新の行が一行だけ取得できます。これを応用して古い重複行を削除したければmax(rowid)を含まない行を削除すれば良いわけです。

SQL

1delete from table_name 2where rowid not in ( 3 select 4 max(rowid) 5 from table_name 6 group by 7 column_name1, 8 column_name2 9)

投稿2020/06/12 05:23

編集2020/06/12 05:28
Kenji.Noguchi

総合スコア358

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

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

Shu0101

2020/06/12 05:52

ありがとうございます。 実は、すべての重複を許してからSQLに格納して削除する方法は試したのですが、Pythonからだとdata baseがlockされてしまい断念しました(SQLite上ではうまく作動しました)。 せっかくご教授いただいたのに申し訳ありません。
Kenji.Noguchi

2020/06/12 07:53

Pythonでは動かず、sqliteコマンドで動くならPythonの方になんらかの問題があると思いませんか?例えば他のプロセスでDBを開いているとか。Sqliteは複数の同時アクセスを許さないので。
Shu0101

2020/06/12 10:09

早々にお返事ありがとうございます。 当該のPythonコードを追加しております。 他のプロセスは開いておらず、Jupyter Labで独立させてこの部分のみをRunしているので、他のプロセスでDBを開いているという訳ではないと思います。 このままRunすると、data base is lockedと出るので、断念したという経緯です。
Kenji.Noguchi

2020/06/12 16:28

"lsof | grep データベースのファイ名"とすれば分かります。
guest

0

ベストアンサー

(制限をかけると、CSVファイルそのものが読み込まれないという理解です。)

この理解は間違っています。CSVのインポート時に重複を除くためにUNIQUE制約をつけて下さい。

例えば下記のようなスキーマのデータベースを作成します。

sql

1CREATE TABLE hoge( 2 date TEXT, 3 type TEXT, 4 value INT, 5 UNIQUE(date,type,value) 6);

terminal

1$ sqlite3 test.db < init.sql

次の2つのCSVをインポートしてみます。1つは予め重複のあるCSV、もう一つは既に存在するレコードを持つCSVです。

test1.csv

CSV

12020-06-12,A,1 22020-06-12,A,1 32020-06-13,B,5 42020-06-14,C,3

test2.csv

CSV

12020-06-13,B,5 22020-06-17,D,3

この状態でインポートしてみます。

terminal

1$ sqlite3 -separator , test.db ".import test1.csv hoge" 2test1.csv:2: INSERT failed: UNIQUE constraint failed: hoge.date, hoge.type, hoge.value

警告が表示されましたがデータベースの中身を見てみると

terminal

1$ sqlite3 test.db "SELECT * FROM hoge" 22020-06-12|A|1 32020-06-13|B|5 42020-06-14|C|3

と正しく重複が除かれたデータベースができています。続いて新しいCSVをインポートして見ます。

terminal

1$ sqlite3 -separator , test.db ".import test2.csv hoge" 2test2.csv:1: INSERT failed: UNIQUE constraint failed: hoge.date, hoge.type, hoge.value

やはり警告が表示されましたが、データベースを見てみると

terminal

1$ sqlite3 test.db "SELECT * FROM hoge" 22020-06-12|A|1 32020-06-13|B|5 42020-06-14|C|3 52020-06-17|D|3

と、新たに意図したレコードが追加されています。

pythonによる方法(回答欄での質問に対する返事)

PRIMARY KEYやUNIQUE制約を持ったテーブルに対して、pythonを使ってCSVファイルを追記する方法を示します。
重要なことはINSERTの代わりにINSERT OR IGNOREを用いることです。

python

1import sqlite3 2 3 4def insert_csv(conn, filename): 5 with open(filename, "r") as f: 6 items = [] 7 for line in f: 8 cols = line.strip().split(",") 9 items.append(cols) 10 cur = conn.cursor() 11 cur.executemany( 12 "INSERT OR IGNORE INTO hoge (date,type,value) VALUES (?,?,?)", 13 items, 14 ) 15 conn.commit() 16 17 18def main(): 19 conn = sqlite3.connect("test.db") 20 insert_csv(conn, "test1.csv") 21 insert_csv(conn, "test2.csv") 22 conn.close() 23 24 25if __name__ == "__main__": 26 main()

投稿2020/06/11 23:18

編集2020/06/12 09:49
yymmt

総合スコア1615

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

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

Shu0101

2020/06/12 04:57

ご丁寧な回答に感謝いたします。ありがとうございます。 説明不足で大変申し訳ないのですが、現在やりたいこととしては、PythonのForループで順々にCSVファイルを読み込もうとしています。 いただいた例に即しますと、test2に加え、test3も順に読み込みます。 そうしますと、test2が読み込まれた場合はご回答のように上書きされますが、その時点でIntegrityErrorが出てしまい、test3をInputする前に止まってしまいます。 TryとExceptでIntegrityErrorをスルーしようとしましたが、改善しませんでした。 ループの作り方が悪い可能性は否定できませんが、上記事象についてアドバイスいただけますと幸甚です。 よろしくお願いいたします。
yymmt

2020/06/12 09:42

例外をキャッチして無視できない理由は分かりませんが、行いたいことはpythonでcsvを一行ずつ読み込んでINSERT分を使って追加する、ということで良いでしょうか?ソースコードを示したいので、別途解答欄にてお答えします。
Shu0101

2020/06/12 11:36

ありがとうございます、解決しました! ご理解の通りです。 INSERT OR IGNOREにすることで、例外処理をせずに済みました。 ベストアンサーとさせていただきます。 ちなみに、IntegrityErrorが例外処理できない件は、海外のサイトでも議論されているようですね。 https://stackoverrun.com/ja/q/7449325
guest

0

DISTINCT は最初の1つだけ記述してください。
DISTINCTは重複データを除外して取得する機能で、重複データを削除する機能ではありません。

投稿2020/06/11 14:34

Orlofsky

総合スコア16415

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

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

Shu0101

2020/06/11 15:36

ありがとうございます。 勘違いしておりました。。。 それでは、データベースに保存後の重複削除の方法はあるのでしょうか? 簡便な方法があればご教授いただけますと幸いです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.49%

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

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

質問する

関連した質問