pythonでsqlite3操作

データベース作成

# db_create.py

import sqlite3

dbname = 'Test.db'
conn = sqlite3.connect(dbname)

conn.commit()
conn.close()
# ターミナル

python db_create.py # 実行
sqlite3 Test.db # Test.dbが存在すれば接続できる

テーブル作成

# table_create.py

import sqlite3

dbname = 'Test.db'
conn = sqlite3.connect(dbname)
cur = conn.cursor()

cur.execute('CREATE TABLE persons(id integer, name text'))

conn.close()
# ターミナル

python table_create.py # 実行
sqlite3 Test.db # Test.dbへ接続
.tables # テーブル確認

データ追加

# data_insert.py

import sqlite3

dbname = 'Test.db'
conn = sqlite3.connect(dbname)
cur = conn.cursor()

cur.execute('INSERT INTO persons(id,name) VALUES(1,ichiro)'))
cur.execute('INSERT INTO persons(id,name) VALUES(2,jiro)'))
cur.execute('INSERT INTO persons(id,name) VALUES(3,saburo)'))

conn.close()

データ取得

# data_insert.py

import sqlite3
import pandas as pd

dbname = 'Test.db'
conn = sqlite3.connect(dbname)

df = pd.read_sql('SELECT * FROM persons', conn)
print(df)
# ターミナル

python data_insert.py # 実行
   id     name
0   1     ichiro
1   2     jiro
2   3     saburo

【Javascript】スプレッド構文の使い方

要素の展開

arr = [1, 2, 3, 4, 5];
console.log(arr);  // [1, 2, 3, 4, 5]
console.log(...arr);  // 1 2 3 4 5

要素をまとめる

const [num1, num2, ...arr] = [1, 2, 3, 4, 5];
console.log(num1);  // 1
console.log(num2);  // 2
console.log(...arr);  // 3 4 5

要素コピー

arr = [1, 2, 3, 4, 5];
const num = [...arr];
console.log(num);  // [1, 2, 3, 4, 5]

要素結合

arr1 = [1,2,3]
arr2 = [4,5,6]
arr3 = [...arr1, ...arr2]
console.log(arr3); // [1, 2, 3, 4, 5, 6]

【Javascript】引数、オブジェクトにデフォルト値を設定する方法

アロー関数引数のデフォルト値

1. デフォルト値なし

(1) 引数設定あり

const greeting = (name) => console.log(`こんにちは、${name}`);

greeting("taro");  # こんにちは、taro

(2) 引数設定なし

const greeting = (name) => console.log(`こんにちは、${name}`);

greeting();  # こんにちは、undefined

2. デフォルト値あり

(1) 引数設定あり

const greeting = (name="名無しさん") => console.log(`こんにちは、${name}`);

greeting("taro");  # こんにちは、taro

(2) 引数設定なし

const greeting = (name="名無しさん") => console.log(`こんにちは、${name}`);

greeting();  # こんにちは、名無しさん

オブジェクト分割代入のデフォルト値

1. デフォルト値なし

user = {
  name: "taro",
}
const { name } = user;
const greeting = `こんにちは、${name}`;
console.log(greeting); # こんにちは、taro

2. デフォルト値あり

user = {
  age: 24,
}
const { name="名無しさん" } = user;
const greeting = `こんにちは、${name}`;
console.log(greeting); # こんにちは、名無しさん

【Rails】gem bullet 導入(N+1クエリ問題を警告)

gem bulletとは

N+1クエリ問題を警告してくれるgemです。

導入

# Gemfile

gem 'bullet'
# ターミナル

bundle exec rails g bullet:install

Would you like to enable bullet in test environment? (y/n) y

もし問題があれば以下のようにlogに出力されます。

# log/bulette.log

USE eager loading detected
  Book => [:author]
  Add to your query: .includes([:author])

またブラウザ画面上でもアラートでも警告してくれます。

参考

https://github.com/flyerhzm/bullet

【Rails】N+1クエリ問題

N+1クエリ問題とは

最初に発行するクエリ1回+N回のクエリ発行により、 アプリケーションの処理速度が遅くなることがあります。

例えば、本の一覧を取得して、関連した著者名も一緒に表示させたい 場合があったとします。 booksテーブルからallメソッドで取得して、eachメソッドで本一覧を出力し、 books.authorのようにして、その本の著者を表示させます。 そのときに本一覧の取得1回と本の取得件数分クエリが発行されます。

具体的に以下のようなテーブルデータの場合、どのようなクエリが発行されるかを確認し またその解決方法を記していきます。

<テーブル>

<使用データ>

# 書籍モデル
class Book < ApplicationRecord
  belongs_to :author
end

# 著者モデル
class Author < ApplicationRecord
  has_many :books, dependent: :destroy
end
# 書籍コントローラ

class BooksController < ApplicationController
  def index
    # 本の一覧を取得
    @books = Book.all
  end
end
# 書籍一覧ビュー

<% @books.each do |book| %>
  <%= book.title %> # 書籍名
  <%= book.author.name %> # 著者名
<% end %>

<書籍一覧取得結果>


書籍取得クエリと著者取得クエリ99回を実行しています。

# ターミナル

  SELECT `books`.* FROM `books`
  SELECT `authors`.* FROM `authors` WHERE `authors`.`id` = 1 LIMIT 1
  SELECT `authors`.* FROM `authors` WHERE `authors`.`id` = 2 LIMIT 1
  SELECT `authors`.* FROM `authors` WHERE `authors`.`id` = 3 LIMIT 1
  ・
  ・
  SELECT `authors`.* FROM `authors` WHERE `authors`.`id` = 97 LIMIT 1
  SELECT `authors`.* FROM `authors` WHERE `authors`.`id` = 98 LIMIT 1
  SELECT `authors`.* FROM `authors` WHERE `authors`.`id` = 99 LIMIT 1

解決策

以下のメソッドを使って、生成するクエリを制限します

  • eager_load
  • preload

eager_load

以下のようにコントローラを修正してもう一度実行してみます

class BooksController < ApplicationController
  def index
    # @books = Book.all ←コメント化
    @books = Book.eager_load(:author) ←追記
  end
end

書籍テーブルと著者テーブルが外部結合され クエリ発行数が1回で済みました。

# ターミナル

SELECT
  `books`.`id` AS t0_r0,
  `books`.`title` AS t0_r1,
  `books`.`created_at` AS t0_r2,
  `books`.`updated_at` AS t0_r3,
  `books`.`author_id` AS t0_r4,
  `authors`.`id` AS t1_r0,
  `authors`.`name` AS t1_r1,
  `authors`.`created_at` AS t1_r2,
  `authors`.`updated_at` AS t1_r3
FROM `books`
LEFT OUTER JOIN
  `authors`
ON
  `authors`.`id` = `books`.`author_id`

preload

以下のようにコントローラを修正してもう一度実行してみます

class BooksController < ApplicationController
  def index
    # @books = Book.all
    # @books = Book.eager_load(:author)←コメント化
    @books = Book.preload(:author) ←追記
  end
end

書籍一覧の取得クエリと IN句を使用したクエリの2回で済みました。

# ターミナル

SELECT `books`.* FROM `books`

SELECT `authors`.*
FROM `authors`
WHERE
  `authors`.`id` IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99)

まとめ

N+1クエリ問題に直面した場合は、eager_loadもしくはpreloadを使用して解決します。

両者の使い分けに関しましては、eager_loadはbelongs_to関連の場合に使用したほうがいい、 preloadはhas_many関連で使用したほうがいいなど言われていますが まだ詳しく調べられていないこともあり、それに関しては別途学習していきたいと思います。

参考

https://pikawaka.com/rails/n1 https://pikawaka.com/rails/includes https://tech.stmn.co.jp/entry/2020/11/30/145159

【Rails】テーブル結合方法

テーブル結合とは

違うテーブルからそれぞれデータがほしいとき それらを結合して1つのテーブルからデータ取得することができます。

テーブル例

著者が1つ以上の書籍を出版している例を用いて以後説明していきます。

1. 内部結合(INNER JOIN)

基礎構文

モデル.joins(:関連名)

モデル同士の関連付け

class Author < ApplicationRecord
  #「books」が関連名
  has_many :books, dependent: :destroy
end

class Book < ApplicationRecord
  #「authors」が関連名
  belongs_to :author
end

テーブル結合例

authorsテーブルとbooksテーブルをauthor_idを使用して結合する

# selectを省略するとauthorsテーブルの全カラムを取得する
Author.joins(:books).select("books.*,authors.name")

発行sql
SELECT books.*,authors.name FROM `authors` INNER JOIN `books` ON `books`.`author_id` = `authors`.`id`

結合イメージ

2. 外部結合(LEFT OUTER JOIN)

基礎構文

モデル.left_outer_joins(:関連名)

モデル同士の関連付け

内部結合と同じように設定します

テーブル結合例

Author.left_outer_joins(:books).select("authors.*,books.title")

発行sql
SELECT authors.*,books.title FROM `authors` LEFT OUTER JOIN `books` ON `books`.`author_id` = `authors`.`id`

結合イメージ

authorsテーブルにbooksテーブルがくっつくイメージです。

参考

https://style.potepan.com/articles/17010.html https://atmarkit.itmedia.co.jp/ait/articles/1703/01/news186.html

SQL素人でも分かるテーブル結合(inner joinとouter join)