データ分析エンジニアとしての第一歩を歩んだ所感

f:id:monozukuri-bu:20191018151721j:plain

こんにちは、ちょねです。
データ分析系の研修を経て、実際に業務としてデータ分析を行ってきましたので、所感をまとめたいと思います。

自己紹介

大学では教育学と情報系を専攻していました。
プログラミング経験としては、統計や機械学習の勉強をしてきた過程でPythonを扱ってきました。

現在は機械学習技術を用いたデータ分析の業務を行っています。
好きな言葉は「苦手じゃない。未熟なだけ。」です。

研修で学んだこと

入社後の研修では、GUIツールとPythonを用いたデータ分析や 機械学習の基礎を学びました。
その他、Linuxコマンド入門、Flaskを使った簡単なWeb開発を行いました。

この研修のよかったところは、今まであまり扱ったことのない 画像やテキストなどのデータの分析を学習できたことです。
研修の後半では、出された課題がわからない場面が多々ありましたが、ネットで調べれば大体同じような場面で躓いている人が質問していて、解決の糸口になるという感覚もつかむことができました。

求められたスキル

初めての業務は、機械学習モデルへ入力するデータの前処理がメインだったため、データを扱える技術が必要でした。

私はPythonをやってきたことからPandasでデータを扱っており、以前より使いこなせるようになっていく実感がありました。
特にgroupbyやapplyをよく使用していました。

また、機械学習の前処理の理解も必要でした。
データの処理方法によって予測精度が変わってくるため、どのような前処理を行い、どのような特徴量を用いれば精度が向上するのかを考えながら業務を行う必要がありました。

不足していたスキル

1つめは、データ処理の技術です。
実際の業務では、整ったデータから分析をスタートできるわけではないと痛感しました。
いくつかのデータを結合して思い通りの形に整形できるように、実際に手を動かして慣れておいたほうが良かったと思いました。

2つ目は、機械学習の評価指標を詳しくなっておくべきでした。
精度といえばAccuracyだという感覚だったので、Precision、Recall、F値、AUC、ROC曲線などの評価指標の知識があれば、 より理解がスムーズだったかと思います。

成長したポイント

業務を通じて私が学んだことは、自分本位に分析を進めてはならないことです。
こうすれば精度が上がるのではないかと推測して進んでいくのはお客様の要望と乖離していく可能性があるため、慎重になるべきです。

加えて、なぜその方法をとったのか説明できなければ、価値は生まれにくいです。
そのため、お客様の要望を満たしつつ、データ分析の専門家ではない人に対しても、分析の結果や過程について、十分な説明ができることが重要であると学びました。

意気込み

入社後、初のデータ分析エンジニアとしての業務を行ったことで、一歩前進することができたと感じています。
業務内では、自分で勉強するだけでは到底得られないような知識を身に付けることができ、良い刺激になりました。

データ分析に関してはまだまだ未熟なので、これからも精進していきます。

Oracle Database ロックについて

f:id:monozukuri-bu:20191016173821j:plain

みちです。
Oracle Databaseのロックについて勉強していきたいと思います。

ロック

 複数のユーザーが同時に書き込み処理を行うと、矛盾が起きてしまう場合があります。
その矛盾が起こらないようにOracleサーバーでは、書き込み処理に対してロックがかかります。
変更の対象の行ごとに排他ロックをかけてからデータの変更処理が行われ、トランザクションが終了したときに解除されます。 ロックがかかっている行に対して変更処理を行おうとする場合、解除されるまで待機し、そのあとに変更処理が行われます。

実際に試してみましょう。
id、name、num、という列を持った表を作成します。

CREATE TABLE ex1
(
    id NUMBER(2),
    name VARCHAR2(100),        
    num NUMBER(10)
);

INSERT INTO ex1 VALUES (1, 'りんご', 100); 
INSERT INTO ex1 VALUES (2, 'バナナ', 50); 
select * from ex1;
ID NAME NUM
1 りんご 100
2 バナナ 50

ここまでのsqlを打つと結果として上の表のような結果が出ます。

USER Aでデータ確認をします。

SELECT * FROM ex1
WHERE ID = 1;
ID NAME NUM
1 りんご 100

USER Bでデータを確認します。

SELECT * FROM ex1
WHERE ID = 1;
ID NAME NUM
1 りんご 100

USER AがここでID=1の行に排他ロックをかけてデータを更新します。

UPDATE ex1
SET NUM = 25
WHERE ID = 1;

USER BはID=1の行に排他ロックをかけようとしますが、USER AがID=1の行に排他ロックをかけているため、待機状態になります。

UPDATE ex1
SET NUM = 200
WHERE ID = 1;

USER Aでデータを確認します。
NUM列が変更されていますね。

SELECT * FROM ex1
WHERE ID = 1;
ID NAME NUM
1 りんご 25

USER Aでロックを解除します。

COMMIT;

USER Bでデータを確認します。

SELECT * FROM ex1
WHERE ID = 1;
ID NAME NUM
1 りんご 200

USER Aの排他ロックが解除されたため更新処理がされました。

この状態で、USER Aでデータを確認します。
USER Bの変更は確定していないため、USER A側ではUSER Bの変更内容が入っていません。

SELECT * FROM ex1
WHERE ID = 1;
ID NAME NUM
1 りんご 25

USER Bで変更内容を確定し、ロックを解除します。

COMMIT;

USER Aでデータを確認してみると、USER Bが行った変更が反映されていることが確認できます。

SELECT * FROM ex1
WHERE ID = 1;
ID NAME NUM
1 りんご 200

まとめ

 今回はロックついて勉強しました。
身近に使われているシステムの仕組みを知れてよかったです。

Python開発の初現場を終えて、学んだこと

f:id:monozukuri-bu:20191016153457j:plain

4月に新卒として入社し、先輩エンジニアと一緒にPythonでの開発を経験しました。
そこで学んだことを整理したいと思います。

開発環境

・言語
 Python 3.7

・ORM
 SQLAlchemy

・データベース
 PosgreSQL

コーディングにはVSCodeを使用しました。

プロジェクトで携わった部分

プロジェクトでは、比較的納期が長く、簡単な以下の機能開発を担当させていただきました。

・DBから特定のデータを取得、整形し、所定のフォーマットでエクスポートする機能
・エクスポートされたデータを、別環境のDBにインポートする機能

機能自体はシンプルですが、最大で同時に約5000万件ほどのデータを扱うため、Pandasなどで処理できない量ではありませんが、少なくもないという絶妙なデータ量でした。

苦労した点

設計には関わっていなかったため、設計書の内容をそのまま実装しただけでしたが、処理自体はそこまで複雑なものではありませんでした。
ただ、データ量が想像していたよりも多く、メモリ不足や速度の面で試行錯誤が必要になったことが苦労しました。

データのエクスポート機能

まずは自力で実装

設計書には実現したい機能と処理の流れが記載されていましたので、Pandasとfor文で実装しました。
この段階では、自分一人で要求されていた機能を実装することができました。
ただ、いざ動かしてみると、1日分のデータをエクスポートするのに約30分掛かっており、実際の利用シーンで想定されるのは1ヶ月〜3ヶ月分とのことだったので、この処理速度では話になりませんでした。

原因の特定と解決策

というわけで先輩エンジニアに相談し、コードの改良に入りました。
まずはじめに、pstatsというライブラリを使用してコードのボトルネックを特定する作業に入りました。
その結果、やはりfor文の使い方が適切でなく、無駄にループしていたりネストが深いのではないかということがわかりました。

この課題の解決策として、Pandasの関数を使用して可能な限り列や行に対してまとめて処理するようにと先輩エンジニアからアドバイスを頂きました。
この修正を行うことで処理時間を大きく減らすことに成功し、1か月分で約15分という許容範囲に収めることができました。

また、ロジックの共通化やコメントの追加なども、ご指摘を頂いていました。
修正作業と合わせて指摘を反映していくと、自分が書いたコード量の約半分の記述で、より高速に動作し、理解しやすいコードに変身しました。

データのインポート機能

まずは自力で実装

こちらはエクスポート機能と同じ要領でまとめて処理するように実装し、データを整形する部分ではそれほど時間は掛かりませんでした。
ただ、データをテーブルにINSERTする部分が2時間近く掛かる状態になってしまいました。
このINSERT処理は、SQLAlchemyというORMを介して行っていました。

課題の原因と解決策

先輩エンジニアに状況を伝えたところ、INSERTを実現する処理には様々な方法があるので、色々試してみるとよいとのことでした。

最初に考えたのは、Pandasのdf.to_sqlを使用してDataFrameを直接テーブルにINSERTする方法です。
これでも処理時間は大幅に短くなりましたが、それでもまだ1時間近く掛かっていました。
他にも色々と試行錯誤を重ねた結果、最終的にはORMは使用せず、Psycopg2を使用してPosgreSQLのcopy_fromでINSERT処理を行うことにしました。
その結果、2時間掛かっていた処理を30分程度まで短縮することができ、許容範囲内に収めることができました。

プロジェクトの経験を通して勉強になったこと

・ロジックの共通化
・コードが短くなるアルゴリズムの組み方
・第3者が読んで理解しやすいコードの書き方
 (PEP8などのコード規約や変数名の定義、コメントの記述)
・テストコードや仕様書の書き方

今まではプログラムが動けば問題ないと思っていました。
しかし、ロジックをできる限り関数で共通化し、コードをわかりやすくしておくと仕様変更があった際にも比較的容易に対処でき、かつ保守性も高まります。
分かりやすいコードを書くということが実際の業務において必要不可欠だということが、今回の案件で身をもって理解できました。

またアルゴリズムの組み方だけでなくテストや開発書類の書き方についてなども、スキルの高いエンジニアの方々にフォローしてもらったことが非常に勉強になりました。

Python:リストとNumpy配列の違い

f:id:monozukuri-bu:20191007172946j:plain

ふっちーです。
今回は戻ってPythonです。

研修で触ったNumpyについてまとめたいと思います。

目次

・Numpyの用途
・Numpyのリスト
・リストと配列の変換
・スライシング
・データ型は統一せよ
・四則演算

環境

今回はGoogle Colabolatory(Colab)を使用しました。
colab.research.google.com 本来Numpyはインストールが必要なモジュールです。
しかし、colabは機械学習を目的に作られているので、インポートするだけで使用できます。

Numpyとは

そもそもNumpyとは何でしょうか。
Wikipedia先生に頼ってみます。

Pythonは動的型付け言語であるため、プログラムを柔軟に記述できる一方で、純粋にPythonのみを使って数値計算を行うと、ほとんどの場合C言語Javaなどの静的型付き言語で書いたコードに比べて大幅に計算時間がかかる。そこでNumPyは、Pythonに対して型付きの多次元配列オブジェクト (numpy.ndarray) と、その配列に対する多数の演算関数や操作関数を提供することにより、この問題を解決しようとしている。NumPyの内部はC言語 (およびFortran)によって実装されているため非常に高速に動作する。したがって、目的の処理を、大きな多次元配列(ベクトル・行列など)に対する演算として記述できれば(ベクトル化できれば)、計算時間の大半はPythonではなくC言語によるネイティブコードで実行されるようになり大幅に高速化する。さらに、NumPyは BLAS APIを実装した行列演算ライブラリ (OpenBLAS, ATLAS, Intel Math Kernel Library など)を使用して線形代数演算を行うため、C言語で単純に書いた線形代数演算よりも高速に動作しうる

NumPy - Wikipedia

な、なるほど!
つまり、普通にPythonで書くと遅いけど、Numpyを使えば内部でC言語Fortranが動作して高速に演算ができる、ということですね。

NumpyはPythonのモジュールとだけあってPythonと似たように動かすことができます。
しかし、異なる部分も多くありPythonに似てる分ややこしいこともあります。
今回はこのNumpyをPythonとどう違うのかを見ていきましょう。

Numpyのリスト

早速Numpyに触ってみましょう。
NumpyはPythonで言うところのリストによく似ています。
それもそのはずでNumpyはリストと同じ構造の配列を基本としています。

しかし、リストとは異なり、Numpyには数学の行列の概念が存在します。
Numpyでは行を「次元数」、列を「要素数」など表記する場合があります。
また、Pythonの組み込み関数で使えるものを「リスト」、Numpyで使われるものを「配列」または「多次元配列」と呼びます。
今回は上記の書き方で統一します。

では、早速Numpyに触れていきましょう。

import Numpy as np

x = np.array([[1, 2, 3], [4, 5, 6]])#arrayで具体的に配列を作る
print(x)
# [[1 2 3]
#  [4 5 6]]

これでNumpyの配列が作成できました。
Numpyではこれをndarrayというデータ型として扱います。
list型とは異なるので、間違えないようにしましょう。

type(x)
# Numpy.ndarray

配列の作り方は他にもあります。

arange関数

y = np.arange(0, 12).reshape(2,6)#reshape(n,m)でn×mの行列に変換
print(y)
# [[ 0  1  2  3  4  5]
#  [ 6  7  8  9 10 11]]

0から11の配列ができました。
arange関数はrange関数のように配列を作ることができます。

linspace関数

z = np.linspace(1, 25, 12).reshape(3,4)
print(z)
# [[ 1.          3.18181818  5.36363636  7.54545455]
#  [ 9.72727273 11.90909091 14.09090909 16.27272727]
#  [18.45454545 20.63636364 22.81818182 25.        ]]

1から25の間を12等分した値の配列ができました。
linspace関数は始点と終点を決めて、その間を好きな数で等分した作ることができます。
また、linspace関数はarange関数とは違って、終点を含める点に注意しましょう。

リストと配列の変換

PythonのリストとNumpyの配列は互いに変換することができます。
最初に作った多次元配列のxをリストにしてみましょう。

多次元配列からリストへ

x_list=x.tolist()
print(x_list)
print(type(x_list))
# [[1, 2, 3], [4, 5, 6]]
# <class 'list'>

to_list()を使うことでこのようにリストに変換できます。

今度はx_listを多次元配列に変換して、元に戻るか試してみましょう。

リストから多次元配列へ

x_ndarray = np.array(x_list)
print(x_ndarray)
print(type(x_ndarray))
# [[1 2 3]
#  [4 5 6]]
# <class 'Numpy.ndarray'>

多次元配列を作ったarray関数で変換することができました。
最初に作ったxと同じ結果になっているのが分かります。

スライシング

Numpyでもスライシングは可能です。

print(x[1][:2])
# [4 5]

また、このような書き方も認可されています。

print(x[1, :2])
# [4 5]

行と列の順番は最初の例と変わりありません。

また、Numpyでは配列という特性から行と列という概念を持っていますと説明しました。
そのため列でスライシングすることができます。

print(x[:, 2])
# [3 6]

それぞれの行のインデックス値が2の要素を抜き出しました。
このようにNumpyでは縦横ともにスライシングできます。

当然ですが、リストはできません。

print(x_list[:, 1])
# TypeError: list indices must be integers or slices, not tuple

リストではそもそも[n, m]でスライシングできないため、列の要素を抜き出すといったことはできません。
どうしてもというなら以下の方法があります。

a = [row[2] for row in x_list]
print(a)
# [3, 6]

データ型は統一せよ

Numpyの多次元配列ではPythonのリストと違って要素のデータ型をすべて統一する必要があります。
xに文字列を入れてみましょう。

x[0][1]='g'
# ValueError: invalid literal for int() with base 10: 'g'

intに変換できないとエラーが出てきました。
これは多次元配列xのデータ型がintで固定されているからです。
配列のデータ型はdtypeで調べることができます。

print(x.dtype)
# int64

zでもやってみましょう。

z[0]='g'
# ValueError: could not convert string to float: 'g'

こちらもstrはfloatにできませんとエラーが出てきました。
このように配列のデータ型は統一する必要があります。

一方リストにはそのような制約はありません。

x_list[0][1]='g'
print(x_list)
# [[1, 'g', 3], [4, 5, 6]]

四則演算

Numpy配列の四則演算は各要素ごとにその計算が行われます。
また、Numpyでは配列の形が異なっていても、一定の条件で四則演算することができます。

print(x*3)
# [[ 3  6  9]
#  [12 15 18]]

このように配列ではない相手でもすべての要素に対して計算を行います。
これはスカラー倍と呼ばれています。

次に配列同士で計算してみましょう。

b = np.array([1,2,3])
print(x+b)
# [[2 4 6]
#  [5 7 9]]

列数は両方とも3ですが、bは次元数が1で、xは次元数が2ですが計算できました。
このように次元数が異なる場合は、次元数が少ないほうの配列が多いほうに次元数を合わせます。
つまり、aは計算する瞬間だけ
[[1,2,3]
[1,2,3]]
となり、それぞれの要素の積を計算できたということです。

最後に列数が異なる場合の計算も見てみましょう。

c = np.array([[5], [7]])
print(x-c)
# [[-4 -3 -2] 
#  [-3 -2 -1]]

こちらもcが
[[5,5,5]
[7,7,7]]
のようになり、計算することができました。

このように計算するために配列の形を合わせるのをブロードキャスト機能といいます。

ちなみに、このような場合は計算できません。
+ (n,m)と(n,l)

print(x-y)#(2,3)と(2,6)
# ValueError: operands could not be broadcast together with shapes (2,3) (2,6) 

これはブロードキャスト機能が適用されるのは条件があるからです。
それは計算に使える配列は次元数か要素数が1か最大値と同じ場合のみと限られているからです。
要するに次元数が2において計算できる配列の形は
・(n, m)と(n, m)
・(n, m)と(n, 1)
・(n, m)と(1, m)
・(n, 1)と(1, m)
・(n, m)とk(実数)
だけになります。

また、これは行列との計算とは異なるということに注意しましょう。
例えば、(a, b) 行列と (c, d) 行列の乗算を行う場合、b=cでないと計算できません。
Numpy配列で行列の計算を行う場合は、dot関数を使いましょう。

d = np.array([[3,3],[3,3],[3,3]])
print(np.dot(x,d))
# [[18 18]
#  [45 45]]

リストに対して同じようにすると以下のようになります。

print(x_list*3)
print(x_list+3)
# [[1, 'g', 3], [4, 5, 6], [1, 'g', 3], [4, 5, 6], [1, 'g', 3], [4, 5, 6]]
# TypeError: can only concatenate list (not "int") to list

最後に、Numpyの計算速度が速いことを確かめてみましょう。

d = np.arange(1,1000) 

%timeit sum(d)
%timeit np.sum(d)
# 10000 loops, best of 3: 93.9 ?s per loop
# The slowest run took 21.44 times longer than the fastest. This could mean that an intermediate result is being cached.
# 100000 loops, best of 3: 4.3 ?s per loop

上がPythonのsumで計算した場合の時間で、下がNumpyのnp.sumで計算した場合の時間です。
このようにNumpyの方が速いとわかります。

まとめ

Numpyの配列とPythonのリストの違いをまとめました。
Pythonに似ているからこそ、覚えやすい面もあれば、混乱する面もあります。
しっかり細かな違いを把握していきましょう。

NULLール〜SQLの基本とNULLについて〜

f:id:monozukuri-bu:20190827200842j:plain

ふっちーです。
SQLのブログは初投稿です。

Oracle MASTER Bronze 12c SQLの資格取得を目指して勉強しております。
今回は基礎の部分とNULLを中心に取り上げていきます。

目次

・用語
・NULLについて
・記述の順番

テーブル

テーブル名:cope

| number | name | Salary | perpay |
|--------|------|--------|--------|
|      1 |佐々木| 180000 | (null) |
|      2 | 富田 | 200000 | (null) |
|      3 | 桑原 | 730000 | (null) |
|      4 | 衛藤 | 360000 |  70000 |
|      5 | 田中 | 310000 |  40000 |
|      6 |  林  | 270000 | (null) |
|      7 | 高木 | 190000 |  50000 |
|      8 | 藤原 | 450000 |  90000 |

以下のSQL文ではこのテーブルを使用しています。

用語

select文の機能とSQL文の分類

機能名 役割 具体例
射影 特定の列を取り出す select
選択 特定の行を取り出す where, having等
結合 表と表を横に繋げる join等

SQLと言ったら外せないselect文についてです。

個人的な経験ですが、試験において意外とつまづきやすいのがこのような名称の問題だと思っています。
他にもSQL文の分類である

DML(Data Manipulation Language)
DDL(Data Definition Language)
・DCL(Data Control Language)
トランザクション制御

SQLにはselect文の様なデータ表を表示する機能だけでなく、内容を書き換えたり、データに接続できる権限の制御まで行えます。
上の4つはその種類分けにつけられている名称です。

select文はDMLに当てはまります。

関数のタイプ

SQLには様々な関数があります。
これはSQLには数値や日付、文字データなどのデータが使用可能です。

SQLの関数には2種類あります。

・単一行関数
・グループ関数

単一行関数

単一行関数は表に対して、一行の結果を出力します。
引数には数値だけではなく、列名や式を入力できるものもあります。

いくつか例を挙げてみましょう。

trunc関数

trunc関数は数値の切り捨てを行います。

select trunc(m, n) from cope;

mを小数点n桁以下を切り捨てします。
nは任意で入力でき、入力しない場合は小数点以下を切り捨てします。
-(マイナス)で入力した場合は小数点の左側を数える。

concat関数

concat関数は文字列の結合を行います。

 select concat('m', 'n') as test from cope;
| TEST |
|------|
|   mn |

2つまでしか結合できないので、3つ以上結合したい場合は(||)を使いましょう。

グループ関数

グループ関数は列に対して、一行の結果を出力します。

こちらもいくつか例を見てみましょう。

avg関数

指定した列の平均値を求めます。
そのため、数値のみの行にしか適用できません。

 select avg("Salary") as test from cope;
|   TEST |
|--------|
| 336250 |

count関数

指定した列内のNULL以外の行数を出力します。

select count("perpay") as test from cope;
| TEST |
|------|
|    4 |

ただし、引数に*(アスタリスク)を入れるとNULLを含めて数えます。

select count(*) as test from cope;
| TEST |
|------|
|    8 |

max関数

指定した列内の最大値を出力します。

select max("Salary") as test from cope;
|   TEST |
|--------|
| 730000 |

ここでいう最大値は数値だけではなく、日付や文字列も含めます。
日付の場合は一番新しい日付が出力されます。
文字列の場合は文字コードが最も大きい文字列が出力されます。

NULLについて

SQLにおけるNULLがややこしかったのでここでまとめてみたいと思います。

四則演算子

NULLは四則演算(+, -, *, /)のどれを適用してもNULLを返します。

select 100*NULL from cope;
| 100*NULL |
|----------|
|   (null) |

NULLを0として扱いたい場合はNVL関数が使えます。
NVL関数については後述しています。

比較演算子

比較を行った場合、基本的にTrueかFalseが返ってくるのが普通ですが、ここでもNULLはNULLを返します。

select * from cope where "perpay" = NULL;

上記も四則演算同様にエラーは発生しませんが、該当なしと出力されます。

しかし、比較自体が行えないわけではありません。
where句でisを使うとNULLかどうかを判断することができます。

select * from cope where "perpay" is NULL;
| number | name | Salary | perpay |
|--------|------|--------|--------|
|      1 |佐々木| 180000 | (null) |
|      2 | 富田 | 200000 | (null) |
|      3 | 桑原 | 730000 | (null) |
|      6 |  林  | 270000 | (null) |

ORDER BYでNULLはどこに来る?

order byは表のソートを行う機能です。

select * from table order by 列名;

この時列にNULLが含まれている場合どうなるでしょうか。
まず、昇順で並べてみましょう。

select "perpay" from cope order by "perpay";
| perpay |
|--------|
|  40000 |
|  50000 |
|  70000 |
|  90000 |
| (null) |
| (null) |
| (null) |
| (null) |

次に、降順で並べてみましょう。

select "perpay" from cope order by "perpay" desc;
| perpay |
|--------|
| (null) |
| (null) |
| (null) |
| (null) |
|  90000 |
|  70000 |
|  50000 |
|  40000 |

このように、ソートでは1番大きいものとして扱われます。

NULLを使った関数

前述した汎用関数はNULLを使用できます。

NVL関数

select nvl("perpay", 0) as test from cope 
|  TEST |
|-------|
|     0 |
|     0 |
|     0 |
| 70000 |
| 40000 |
|     0 |
| 50000 |
| 90000 |

式がNULLの場合、設定された値を戻します。
例の場合はNULLの時0を返します。

NVL2関数

select nvl2("perpay", 1, 0) as test from cope 
| TEST |
|------|
|    0 |
|    0 |
|    0 |
|    1 |
|    1 |
|    0 |
|    1 |
|    1 |

式がNULLでないなら1つ目の値を、NULLなら2つ目の値を戻します。 例の場合はNULL以外で1、NULLで0を出力します。

NULLIF関数

select nullif("Salary", 180000) as test from cope 
|   TEST |
|--------|
| (null) |
| 200000 |
| 730000 |
| 360000 |
| 310000 |
| 270000 |
| 190000 |
| 450000 |

2つの式を比較し、等しい場合はNULLを返す。異なる場合は1つ目の式を戻します。
例の場合はSalaryが180000のときNULLを返します。

まとめ

今回はSQLの用語とNULLについてまとめました。

特にNULLは先月まで勉強していたPythonでは見られない動作をするので、なんとかして慣れたいです。
また、個人的に今一番の壁が相関副問合せなので、これも克服していきたいです。

OracleDatabase SQL VIEW、順序について

f:id:monozukuri-bu:20190827194855j:plain

みちです。
Oracle DatabaseのVIEWと順序の作成・削除についての勉強していきたいと思います。

VIEW

 表や他のVIEWをもとに作成されるオブジェクトです。VIEWはデータを持っていません。
VIEWのもととなる表を「実表」、VIEWを「仮想表」といいます。

VIEWの種類

単一VIEW

 一つの表から構成され、関数やGROUP BY句を含まないVIEWのことです。
VIEWから実表の操作をすることが可能です。(条件による)

複合VIEW

 二つ以上の表から構成されているVIEWもしくは、1つの表から構成され関数やGROUP BY句を含むVIEWのことです。
VIEWから実表の操作をすることは特定の条件を満たす場合可能です。

VIEWの作成

 CREATE VIEW権限を持っていればVIEWの作成が可能となります。

CREATE [OR REPLACE] [FORCE | NOFORCE] VIEW VIEW名[(別名 [,別名 …])]
AS
副問合せ
[WITH CHECK OPTION [CONSTRAINT 制約名]]
[WITH READ ONLY [CONSTRAINT 制約名]];

CREATE VIEW文のオプション

OR REPLACE

 同名のVIEWが存在した場合そのVIEWに上書きします。
同名のVIEWが存在しているのにも関わらずOR REPLACEを省略するとエラーとなります。
同名のVIEWが存在しない場合新たにVIEWが作成されます。

FORCE

 実表が存在しない場合エラーとなるがVIEWは強制的に作成されます。

NOFORCE

 実表がある場合VIEWが作成されます。実表が存在しない場合はエラーとなります。

WITH CHECK OPTION

 VIEWを通じてアクセスできる行のみ追加、更新できます。

WITH READ ONLY

 VIEWが読み取り専用となります。そのためデータの追加、更新、削除が出来なくなります。

VIEWの削除

DROP ANY VIEW権限を持つユーザーがVIEWを削除することが出来ます。

DROP VIEW VIEW名;

VIEWを使用したデータの変更

データの削除の場合(DELETE)

 GROUP BY句、ROWNUM疑似列、グループ関数、DISTINCTキーワードの要素が含まれていない場合削除することが出来ます。

データの更新の場合(UPDATE)

 GROUP BY句、ROWNUM疑似列、グループ関数、DISTINCTキーワード、式によって定義された列が要素が含まれていない場合変更することが出来ます。
副問合せのSELECT句で計算式や関数を使用している場合、列別名を指定する必要があります。

データの追加(INSERT INTO)

 GROUP BY句、ROWNUM疑似列、グループ関数、DISTINCTキーワード、式によって定義された列、VIEWに含まれていない実表の列にNOT NULL制約が定義されている要素が含まれていない場合変更することが出来ます。

順序の作成・削除

順序の作成

 順序は一意な番号を自動で生成するオブジェクトです。
CREATE SEQUENCE権限を持つことでCREATE SEQUENCEを使用することが出来ます。

INCREMENT BY

 デフォルト値は1です。指定した数だけ増幅します。

START WITH

 デフォルト値は1です。初期値を設定することが出来ます。

MAXVALUE / NOMAXVALUE

 最大値を設定することが出来ます。デフォルトはNOMAXVALUEです。

MINVALUE / NOMINVALUE

 最小値を設定することが出来ます。デフォルトはNOMINVALUEです。

CYCLE / NOCYCLE

 順序値が最大値または最小値に達した場合、初期値に戻り繰り返し順序値を生成します。
デフォルトはNO CYCLEです。

CASHE / NOCASHE

 指定された数順序値をメモリに割り当てます。デフォルト値は20です。

順序の使用方法

 主問い合わせのSELECT句のリスト、INSERTの副問合せのSELECT句のリスト、INSERTのVALUES句、UPDATEのSET句で参照することが出来ます。

NEXTVAL疑似列

 新しい順序値が戻されます。

CURRVAL疑似列

 最後に取得された順序値が戻されます。

順序値の欠番の発生

ロールバックが発生した場合、順序が複数の表で指定されていると、システムがクラッシュした場合に欠番が発生することがあります。

順序の定義の削除

DROP SEQUENCEを使うことによって順序の削除をすることが出来ます。
DROP ANY SEQUENCE権限をもつユーザーは削除を行うことが出来ます。

DROP SEQUENCE 順序名;

順序の定義の変更

 ALTER SEQUENCEを使うことによって順序の定義を変更することが出来ます。
START WITH以外のオプションを指定することが出来ます。
初期値を指定する場合は順序を削除してから再度作成する必要があります。

まとめ

 今回はVIEWの作成と順序について勉強しました。

ハードリンクとシンボリックリンク

f:id:monozukuri-bu:20190816184235j:plain

shadowです。
今回はWindouwsを使っている人ならお馴染みのショートカットについて、
Linuxにはそのショートカットが2種類あるので、詳しく説明したいと思います。

ハードリンクとシンボリックリンクの違い

Linuxにはショートカットの種類として、ハードリンクとシンボリックリンクが存在します。
この2つのリンクの違いについて説明する前にまずは、ファイルのinode番号について書いていきます。

inode番号

Linuxでファイルを作成するとinode番号という一意の番号が割り当てられます。
また、inodeにはファイルの属性情報が格納されています。
・ファイル種別
・ファイルサイズ
・アクセス権
・所有者情報
・リンク
・ブロック番号

$ ls -li sample.txt
7454 -rw-r--r--    1 user     user             0 Aug  9  2019 sample.txt

一番左の7454がinode番号です。

ハードリンク

ハードリンクはファイルに別名をつけるようなものです。

ファイル名A -> ファイルの実体 <- ファイル名B

このようにファイル名は違えど同じファイルの実体(inode)を指しています。
また、ファイルのinodeにアクセスしているため、ファイルが移動しても問題ありません。
そして、異なるファイルシステム上や、ディレクトリにはリンクを作成することが出来ません。

シンボリックリンク

シンボリックリンクWindowsのショートカットに近い概念です。
シンボリックリンクはハードリンクと違い、ファイルの実体ではなくファイルパスを指します。

シンボリックリンクファイル -> ファイル名 -> ファイルの実体

シンボリックリンクはinodeに依存せず、パスで参照しているので、
ハードリンクと違い、異なるファイルシステム上のファイルや、ディレクトリに対してもリンクを作成することができます。
ただし、パスでアクセスしているため、ファイルが移動するとアクセス不能なデットリンクになってしまいます。

lnコマンド

リンクを作成するにはlnコマンドを使います。
オプションを指定せずにlnコマンドを実行した場合、ハードリンクが作成されます。
-sオプションをつけることで、シンボリックリンクが作成されます。

動かしてみる

ハードリンク

実際に動かしてみます。

$ mkdir -p testdir/sampledir

$ touch testdir/sampledir/file.txt

$ echo Test > testdir/sampledir/file.txt

$ cd sampledir

$ ln sampledir/file.txt ./sample_hard

$ cat sample_hard
Test

sample_hardの中を見てみるとfile.txtと同じ内容になっています。

次はinode番号をチェックしてみます。

$ ls -li sample_hard sampledir/file.txt                               
   7461 -rw-r--r--    1 root     root             5 Aug  9  2019 sample_hard    
   7461 -rw-r--r--    1 user     user             5 Aug  9  2019 sampledir/file.
txt

どちらも同じになっています。
ファイルの移動もしてみたいと思います。

$ mv sampledir/file.txt .

$ cat sample_hard
Test

移動しても同じ内容を表示しました。

シンボリックリンク

$ mkdir sampledir2

$ touch sampledir2/file2.txt

$ echo Test2 > sampledir2/file2.txt

$ cat sampledir2/file2.txt                            
Test2

$ ln -s sampledir2/file2.txt ./sample_symbolic

$ cat sample_symbolic
Test2

シンボリックリンクの中身も同じでした。
こちらもファイルの移動をします。

$ mv sampledir2/file2.txt .

$ cat sample_symbolic
cat: sample_symbolic: そのようなファイルやディレクトリはありません

こちらはファイルが移動すると参照出来ませんでした。

まとめ

最後にハードリンクとシンボリックリンクの特徴を、ざっくりまとめると以下のようになります。

特徴 ハードリンク シンボリックリンク
参照 inode パス
異なるファイルシステム できない できる
元ファイルの移動 移動しても消えない 移動すると参照できない
元ファイルの削除 削除しても消えない 削除すると消える

以上です。お疲れ様でした。