OmniAICreator/Qiita-1.07M huggingface dataset:
SREの学習2


SREにおけるSLI and SLOについて教えてください。
--
SRE（Site Reliability Engineering）において、サービスレベル指標（SLI）とサービスレベル目標（SLO）は、サイトの可用性を測定し、維持するために重要な役割を果たします。

SLI（Service Level Indicator）は、サービスの性能を測定するために使用されるメトリックのことで、通常はシステムの可用性、応答時間、エラー率、スループットなどが含まれます。SREでは、SLIはサービスのパフォーマンスの正確な測定に役立ちます。

SLO（Service Level Objective）は、サービスのレベル目標であり、SLIに基づいて設定されます。SLOは、サービスの可用性に関する目標を示し、時間の経過とともに実行結果が監視され、定期的に再評価されます。SLOは、エンドユーザーの体験に基づいて設定されるため、顧客満足度を向上させるために非常に重要です。

SLOが満たされない場合、これはサービスレベルの違反（SLAの違反）であると考えることができます。SLOの違反が頻繁に発生する場合、それはシステムの問題を示しており、改善が必要であることを示唆しています。

SREチームは、SLIとSLOを監視し、サービスの問題を特定し、改善するための戦略を決定します。SREの目標は、サイトの可用性を最大化し、システムの問題を最小限に抑えることです。SLIとSLOは、この目標を達成するための貴重なツールです。

SREにおけるSRE Metrics (Golden Signals)について教えてください。
---
SRE（Site Reliability Engineering）において、SREメトリック（ゴールデンシグナルとも呼ばれます）は、サービスの健全性とパフォーマンスを測定するための重要な指標です。SREメトリックは、次の4つのカテゴリーに分類されます。

Latency（レイテンシー）：ユーザーからのリクエストを処理するのにかかる時間を測定する指標で、応答時間、ネットワークレイテンシー、データベースクエリのレスポンス時間などが含まれます。

Traffic（トラフィック）：システムへのリクエストの数を測定する指標で、通常はリクエストの数、リクエストの分布、ユーザーの数、接続数などが含まれます。

Errors（エラー）：システムのエラー率を測定する指標で、通常はHTTPエラー率、データベースエラー率、タイムアウト率などが含まれます。

Saturation（サチュレーション）：システムのリソース使用率を測定する指標で、通常はCPU使用率、メモリ使用率、ディスク使用率などが含まれます。

これらの指標は、サービスの健全性とパフォーマンスを監視し、サービスレベル指標（SLI）とサービスレベル目標（SLO）の設定に役立ちます。SREチームは、これらのメトリックを使用して、サービスの問題を特定し、解決するための戦略を決定します。

SREメトリックは、システムのパフォーマンスを監視するだけでなく、システムの予測能力を向上させるためにも役立ちます。これらのメトリックは、システムの問題を特定し、サービスの可用性を向上させるための改善点を決定するための貴重なツールです。

SREにおけるAgility- Stabilityについて教えてください。
---
SRE（Site Reliability Engineering）において、Agility（アジリティ）とStability（スタビリティ）は、相反する2つの要素であり、SREの目的は、この2つの要素をバランスよく維持することです。

Agilityは、迅速かつ効率的な変更の実施を可能にする能力を指します。システムの機能やパフォーマンスを改善するために、コードやシステムの変更が必要になる場合があります。SREチームは、変更を迅速かつ正確に実施し、迅速に展開して、市場競争力を維持します。

Stabilityは、サービスの可用性や信頼性を確保する能力を指します。システムは、安定性を維持するために適切な保守とメンテナンスが必要であり、システム障害を回避し、最小限のダウンタイムでシステムを復旧するための予防措置を講じる必要があります。

SREチームは、AgilityとStabilityをバランスよく維持するために、次のようなアプローチを採用します。

標準化：SREチームは、標準化されたプロセスやツールを使用して、リスクを最小限に抑え、迅速な変更を実施することができます。

自動化：SREチームは、自動化されたプロセスやツールを使用して、安定性を維持しながら変更を迅速かつ正確に実施することができます。

監視：SREチームは、システムの健全性を監視し、潜在的な問題を早期に特定することができます。

テスト：SREチームは、システム変更の影響を最小限に抑えるために、変更前にテストを実施することができます。

AgilityとStabilityのバランスを維持するために、SREチームは、迅速な変更を実施しながら、サービスの信頼性と可用性を維持することが重要です。
【Node.js】LINE NotifyでLINEに通知を送る

LINE Notifyを用いてLINEに通知を送る一連のフローを整理する。
最終的にはAWS Lambdaで動かすことを目的にしているため、それを意識した中身になっている。

## 環境
LINE Notify：2023年2月25日版
Node.js：v16.13.1
"axios": "^1.3.4"
"dotenv": "^16.0.3"


## 流れ
1. LINE NotifyへLINEアカウントでログインする。
1. アクセストークンを発行する。
1. メッセージを送信するプログラムを作成する。
1. 作成したプログラムを実行する。

## LINE NotifyへLINEアカウントでログインする。
LINE Notifyのページにアクセス
LINE Notify: [https://notify-bot.line.me/ja/](https://notify-bot.line.me/ja/)

画面右上の「ログイン」ボタンをクリックし、LINE アカウントでログインする。

<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2338940/2a886f09-cc90-c2d4-a128-eaddf5101d03.png">

<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2338940/d52d1300-5266-e269-6455-42ddd20e02b2.png">

## アクセストークンを発行する。
ログイン後に表示される画面下部の「トークンを発行する」をクリックする。
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2338940/a01ca00b-b514-bff4-3c23-d90b172afc84.png">

トークン名と通知を送信するトークルームを選択する。
トークン名はLINEに通知される際に下記のように表示される。
```
[トークン名]メッセージ
```
自分にだけ通知したい場合は、トークルームで
「1:1でLINE Notifyから通知を受け取る」を選択する。

トークン名とトークルームを選択したら「発行する」をクリックする。
トークンが発行されるのでコピーしどこかにメモを残す。
コピーするチャンスはこの1回限りなので必ずコピーする。
トークンをコピーしどこかにメモで残したら「閉じる」をクリックする。
<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2338940/cd019dae-c27f-965d-1ab9-5ac6bd145db2.png">

## メッセージを送信するプログラムを作成する。
LINE Notifyのドキュメントを元にメッセージを送信するプログラムを作成していく。
LINE Notify API Document: [https://notify-bot.line.me/doc/ja/](https://notify-bot.line.me/doc/ja/)

メッセージを通知するには、下記エンドポイントを利用する。
<img width="700" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2338940/7c5fce03-2ebf-5af9-f0af-ab103e3f44e1.png">

最低限のメッセージを通知するには、下記情報が必要となる。messageが通知内容となる。
**メソッド**
Method: POST
**ヘッダ**
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer \<access_token(取得したアクセストークン)\>
**パラメータ**
message: String

今回は「axios」を利用してメッセージを通知するためのプログラム(index.js)を作成する。
アクセストークンをコードと分けて管理するために、.envを作成する。
(エンドポイントとメッセージを.envに切り出した理由は特にはない。)
.envを読み込むために「dotenv」を利用する。


```index.js
const dotenv = require("dotenv");
const axios = require("axios");

//LINE Notifyのアクセストークン
const token = dotenv.config().parsed.lineToken;

//LINE Notifyのメッセージ送信用のAPI エンドポイント
const url = dotenv.config().parsed.lineUrl;

//LINE Notifyで通知させたいメッセージ
const message = dotenv.config().parsed.message;

const handle = async function(){
    const pubMsg = await axios.post(
        url, 
        {
            message,
        }, 
        {
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
                'Authorization': 'Bearer ' + token
            }
        }
    ).then(function (response) {
        console.log(response.data);        
    }).catch(function (error) {
        console.log(error);        
    });;
};

handle();
```


```.env
lineToken=取得したアクセストークン
lineUrl=https://notify-api.line.me/api/notify
message=通知テストです。
```

## 作成したプログラムを実行する。
index.jsファイルを実行する。
```
node index.js
```

LINE に下記メッセージが通知される。
```
[トークン名]通知テストです。
```
SREの学習


SREの分野でのKaizen Mindsetについて教えてください。
---
SRE（Site Reliability Engineering）とは、ソフトウェアの開発や運用において、サイトの信頼性を確保するための分野です。SREにおいてKaizen Mindsetを取り入れることで、より効率的かつ高品質なサイト運用を実現することができます。

具体的には、以下のような点が挙げられます。

監視・分析の改善
SREでは、サイトの監視やトラブルシューティングが非常に重要なタスクです。Kaizen Mindsetを持っていると、常に新しい監視ツールや技術を導入し、分析方法を改善していくことができます。これにより、サイトの問題をより早く発見し、解決することができます。

自動化の改善
SREでは、自動化が重要な要素です。自動化により、手作業でのヒューマンエラーを減らし、サイトの信頼性を高めることができます。Kaizen Mindsetを持っていると、自動化の改善を行い、より多くのタスクを自動化することができます。

コミュニケーションの改善
SREでは、開発者やエンジニア、マネージャーなど、様々な役割の人たちと協力して作業を進める必要があります。Kaizen Mindsetを持っていると、コミュニケーションの改善に取り組むことができます。例えば、スムーズなコミュニケーションを図るために、チャットツールの利用方法を統一したり、コミュニケーションに必要な情報を共有するシステムを整備したりすることができます。

以上のように、SREにおいてKaizen Mindsetを取り入れることで、より高品質で信頼性の高いサイト運用を実現することができます。

SREの分野でのError-Budget Outcomesについて教えてください。
---
SREにおいて、Error-Budget Outcomesは非常に重要な概念の1つです。Error-Budget Outcomesは、サイトの信頼性を測定する指標であり、SREが目標とするサイトの信頼性を達成するために使用されます。

具体的には、以下のような流れでError-Budget Outcomesが決定されます。

サイトの信頼性目標を定義する
SREでは、サイトの信頼性目標を定義します。これは、サイトがどの程度の信頼性を維持する必要があるかを示すものです。例えば、99.9％のアップタイムを維持することが目標である場合、サイトは年間で約8.8時間のダウンタイムを許容できます。

エラーバジェットを計算する
エラーバジェットは、サイトが目標の信頼性を達成するために許容できるエラーの最大数を示します。例えば、99.9％のアップタイムを目標とし、年間で8.8時間のダウンタイムを許容できる場合、エラーバジェットは43,800秒になります。

エラーバジェットを分配する
エラーバジェットは、SREが計画する変更のリスクに対する予備費用として使用されます。SREは、エラーバジェットを適切に分配し、システムへの変更による信頼性への影響を予測します。

Error-Budget Outcomesを監視する
SREは、定期的にError-Budget Outcomesを監視し、エラーバジェットを遵守しているかどうかを確認します。もしエラーバジェットを超過するようなトラブルが発生した場合、SREは優先度の高い改善に取り組むことが必要です。

以上のように、Error-Budget Outcomesは、サイトの信頼性を維持するために必要な指標です。SREは、Error-Budget Outcomesを常に監視し、エラーバジェットを遵守することで、サイトの信頼性を維持するための取り組みを継続的に行っています。

SERの分野でのDeployment Strategyについて教えてください。
---
SREにおけるDeployment Strategyは、新しいソフトウェアリリースをシステムに展開する方法を指します。Deployment Strategyは、サイトの可用性を高め、エラーの発生率を低下させるために重要な役割を果たします。以下に、SREにおけるDeployment Strategyの一般的なアプローチを説明します。

Canary Deployment
Canary Deploymentは、新しいリリースを一部のユーザーに限定してリリースし、リリースの影響を評価することができます。Canary Deploymentでは、一部のユーザーに新しいリリースを展開し、一定期間後にその結果を確認します。もし問題がなければ、残りのユーザーにも展開することができます。

Blue/Green Deployment
Blue/Green Deploymentでは、二つの環境を持ち、一方で新しいリリースを展開し、もう一方の環境で古いバージョンを維持します。新しいリリースの影響を評価し、問題がなければトラフィックを新しい環境に切り替えます。Blue/Green Deploymentは、アプリケーションを安全に更新する方法として広く使われています。

Rolling Deployment
Rolling Deploymentは、新しいリリースを徐々に展開し、一度に少量のリソースをアップグレードすることにより、システムの可用性を高めます。Rolling Deploymentでは、一部のリソースをアップグレードし、問題がなければ残りのリソースにもアップグレードします。

A/B Testing
A/B Testingは、新しいリリースを一部のユーザーに対して展開し、古いバージョンと新しいバージョンを比較することができます。A/B Testingでは、ユーザーに異なる体験を提供し、どちらのバージョンがより優れているかを決定します。

SREでは、Deployment Strategyを選択する前に、リスクや影響を評価する必要があります。それぞれのDeployment Strategyには、利点と欠点があります。選択されたDeployment Strategyが適切に実施されることで、サイトの可用性を高め、問題の発生率を低下させることができます。
Leetcode 121. Best Time to Buy and Sell Stock

https://leetcode.com/problems/best-time-to-buy-and-sell-stock/

```java
class Solution {
    public int maxProfit(int[] prices) {
        int minimal = Integer.MAX_VALUE;
        int result = 0;
        for (int i = 0; i < prices.length; i++) {
            if (prices[i] < minimal) {
                minimal = prices[i];
                continue;
            }
            result = Math.max(prices[i] - minimal, result);
        }
        return result;
    }
}
```
RancherOS後継BurmillaOSをUEFIからbtrfs上にインストールする

* 参考URL

https://burmillaos.org/

* スクリプトのリポジトリ

https://github.com/Yougain/rancher_os_efi/tree/burmilla_os_efi

* スクリプトの紹介

## create_burmillaos_efi_iso

UEFIでブート可能なBurmullaOSのインストーラーiso(burmillaos-vx.x.x.efi.iso)を作成するスクリプト。Passwordは'rancher'でログイン可能なようにカーネルパラメータを設定してある。BurmillaOSのリポジトリから必要なファイルをダウンロードして自動的に作成する。引数を省略すると安定最新版で、'rc'あるいは'beta'を指定すると、開発中の最新版で作成される。引数に具体的なバージョン名を指定することも可能。UEFI用のファイルはUbuntuから取っているので、Ubuntuのisoもダウンロードして中身を流用している。isoは~/burmillaos_efiに作成される。
注意：rufusなどでisoをUSB化する際には、ラベルを必ず'rancheros'にしなければならない。他のラベル(例えば'burmillaos')にすると、インストーラーがエラーを起こす。
  ```
  $ ./create_burmillaos_efi_iso [|rc|beta]
  　...... # Downloading BurmillaOS and Ubuntu iso images.
  　...... # Copy files with EFI function from Ubuntu iso image to a new BurmillaOS iso image.
  　# You get ~/burmillaos_efi/burmillaos-vx.x.x.efi.iso finally.
  $ ls ~/burmillaos_efi
  burmillaos-vx.x.x.efi.iso  burmillaos-vx.x.x.iso  ubuntu-22.04.1-live-server-amd64.iso
  ```

## install_burmillaos_on_btrfs

上記のスクリプトで作成した、インストーラーisoで起動した新しい物理サーバーに普段使いのローカルターミナルから実行して、自動的にBurmillaOSをbtrfs上にインストールするためのスクリプト。サーバー名、新サーバーのsshdポート番号、DHCPで割り当てられた暫定IPアドレス(一つ目のIPアドレス引数)、新サーバーで使うIPアドレス(二つ目のIPアドレス引数)を引数として渡す。ポート番号を省略すると、スクリプトが未使用ポート番号からランダムに決定する(ローカルの~/.ssh/configに保存するので、後からssh [server name]でログイン可能)。ローカルの~/.ssh/id_ed25519.pubが新サーバーの~rancher/.ssh/authorized_keysに登録される。DNSやネットマスクは、DHCPで割り当てられたパラメータを流用する。タイムゾーンはローカルターミナルで使っているタイムゾーンに設定される。ルートファイルシステムはbtrfsでフォーマットされた/dev/sda5の/state_rootに作成される。
注意：/dev/sdaに強制的にインストールするので、全部のデータは消去されます。

 ```
$ ./install_burmillaos_on_btrfs \
	[server name] \
	[server's ssh port number after installation] \
	[dhcp-provided ip address during installation] \
	[server's fixed ip address after installation]

For example,
$ ./install_burmillaos_on_btrfs mainsv 20122 192.168.0.11 192.168.0.201
 ```
Three.jsで簡単に3Dモデルを動かす方法

# 完成品
今回はピングーに活躍いただきました。
角度をマウス操作で自由に変えられるよ！
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1227793/95897264-3582-de80-0d01-37514aa2d927.png">

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1227793/5b46322f-6c46-a243-d8dc-2e8c98f08b8a.png">


# STEP1: 3Dモデルを手に入れる
3Dモデルを作りたいところだが、今回は省略。
自作でなくてもOKな人はライセンスフリーなのを適当に[Sketchfab](https://sketchfab.com/3d-models/pingu-3efb1bf4e0ce4c97b6538b48d0ba2ecc)から拾ってこよう
自作で作りたい！という方は無料3Dモデリングソフトの[Blender](https://blender.jp/)がおすすめ
# Three.jsを使用する
ここから本題
#### Three.jsとは何か
本来であれば3Dモデルの描画はWebGLで表現する必要がある
でも大丈夫
Three.jsを使えばjsだけで気軽にできる
[公式ドキュメント参照](https://threejs.org/docs/index.html#manual/en/introduction/Creating-a-scene) 
#### じゃあWebGLとは何か
二次元および三次元の描画をHTMLのcanvasタグにおいて行うことができるAPI
GPUにアクセスしてWeb上で表現できる幅が広がったもの
[公式ドキュメント参照](https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API) 

# Three.jsのインストール
使い方は3つ
#### ローカルサーバを立てないとき
1. [公式サイト](https://threejs.org/)からダウンロードして必要ファイルを持ってくる
2. CDNがあるのでそれを使う
#### ローカルサーバを立てるとき
3. npm install three　で入れる

ローカルサーバを立てなくてもCORSを許可したらjsがモデルを見に行けるが...あまりおすすめしない

今回は自前の3Dモデルピングーを使うのでローカルサーバを立てる

# Three.jsでコーディング
githubに完成したのをあげましたhttps://github.com/pingu-noots/threejs-pingu
#### 必要な要素
大きく分けて以下が必要
・レンダラー
・シーン
・カメラ
・物質(モデル)
それらの構成が上手いことまとまっている図がこちら(画像は[ICS MEDIA](https://ics.media/entry/14771/)から)
![スクリーンショット 2023-02-25 2.37.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1227793/fc9a873d-5971-9d68-83b0-feeccb6c4fb9.png)

## コード解説
https://github.com/pingue-noots/threejs-pingu のコードの中で重要なものを抜粋して紹介
レンダラー、シーン、ライト、カメラを作る
```Javascript
// レンダラー
const renderer = new THREE.WebGLRenderer({
  canvas: canvas, 
  alpha: true, 
  antialias: true, 
});
// シーン
const scene = new THREE.Scene();
// ライト
const pointLight = new THREE.PointLight();
// カメラ
const camera = new THREE.PerspectiveCamera()
```
モデルを読み込ませる
```Javascript
loader.load(
  // モデルの場所
  url,
  // 3Dモデルをsceneに入れる
  function (gltf) {
    model = gltf.scene;
    model.position.set(0, -0.5, 0);
    scene.add(model);
    //ANIMATION
    mixer = new THREE.AnimationMixer(model);
    gltf.animations.forEach((animation) => {
      actions.push(mixer.clipAction(animation).play());
    });
    tick();
  },
  function (error) {
    console.log("An error happened");
    console.log(error);
  }
);
```
ピングーをマウス操作できるようにするなどのアニメーションの追加
```Javascript
const tick = () => {
  const delta = clock.getDelta();
  mixer.update(delta);

  // Update Orbital Controls マウス操作の有効化
  controls.update();

  // Render
  renderer.render(scene, camera);

  // Call tick again on the next frame ここが大事！次フレームにアニメーションを渡す
  window.requestAnimationFrame(tick);
};
```

# 見てみよう!
githubのコードはwebpackでbundleしてます
npm run devでサーバを立ち上げlocalhost:8080に行くと...
ピングーだ！
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1227793/95897264-3582-de80-0d01-37514aa2d927.png">

# 好きな３Dモデルに置き換える
今回はピングーでしたがgithubのソースコードの /static/textures/ にお好きな3Dモデルを入れて以下の部分を変えたらお好きなものを表示できます。
```javascript
const url = "/textures/pingu.glb";
```
【Selenium】とりあえずこれだけあればOK？ Python selenium

# はじめに
Seleniumって定期的に忘れますよね(？)
操作を一通りメモ書きしておきます。

とりあえずimportしておくもの
```python
from selenium import webdriver # ブラウザ操作そのものに使用
from selenium.webdriver.common.by import By #要素を探す際に使用
from selenium.webdriver.chrome.options import Options #ブラウザを非表示状態で操作する際に使用
from webdriver_manager.chrome import ChromeDriverManager #入っているchromeと同じバージョンのドライバーをインストールするために使用
```

# ブラウザを起動/終了する
```python
# ブラウザを非表示で動かすための設定
options = Options()
options.add_argument("--headless") #コメントアウトでブラウザ表示

# 現在のブラウザに対応するドライバーをインストールしてブラウザを開く
browser = webdriver.Chrome(ChromeDriverManager().install(),options=options)

# ブラウザを閉じる
browser.quit()
```
# URLを開く
```python
# ブラウザでURLを開く
url = "https://www.google.com/"
browser.get(url)
```

# 要素のオブジェクトを取得
```browser.find_element()```または```browser.find_elements()```を使用する。
引数は```(by,value)```
```find_elements```の場合はリストが返ってくる。

xpathをchromeでコピーしてくると中身に"を含むので、xpath全体を'でくくる必要がある。


```python
# idで取得
elem = browser.find_element(By.ID,"***")
# xpathで取得
elem = browser.find_element(By.XPATH,'***')
# htmlタグ名で取得
elems = browser.find_elements(By.TAG_NAME,'***')
```

# 要素に対する操作
```python
# キー送信
elem.send_keys("***")
# クリック
elem.click()
```

# 要素から何かを取得
```python
# テキスト取得
elem.text
# リンク先の取得
elem.get_attribute("href")
# htmlの取得
elem.get_attribute("outerHTML")
```
# 要素の調べ方
Chromeで目当てのサイトを開きF12でDevToolを起動すると要素のxpathやcss styleなどが取得できる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2716347/cf58be05-0c5b-655d-3acf-f8d2f5592531.png)
Rustのハッシュマップについてのメモ

## ハッシュマップとは

ハッシュマップとはキーと値のペアを保存するためのデータ構造の一種で、Javascriptでいうところのオブジェクトに似ている。

```rust
use std::collections::HashMap;

fn main() {
    let mut map = HashMap::new();
    map.insert("one", 1);
    map.insert("two", 2);
    map.insert("three", 3);
    println!("{:?}", map);
    // => {"two": 2, "one": 1, "three": 3}
    println!("{}", map["two"]);
    // => 2
}
```

## ハッシュマップを使うメリット

1. __検索・追加・削除が高速__

     キーをハッシュ値に変換して、そのハッシュ値をインデックスとして配列の要素にアクセスするため、検索・追加・削除の操作を高速に行うことができる。

2. __要素数に依存しない処理速度__
    
    要素数に依存しない一定の処理速度を持つため、ハッシュマップに格納されている要素数が多くなっても一定の処理速度が維持される。

3. __キーと値のペアの格納__

    キーと値のペアを格納できるため、複数のデータを管理することができる。例えば、単語とその意味のように、1つのキーに対して複数の値を持たせることもできる。

4. __メモリ使用量の削減__

    キーと値のペアを格納するために必要なメモリ量を最小限に抑えることができる。そのため、メモリ使用量を削減することができる。
SQLAlchemy 2.0 (2.0.4) 新機能 sort_order でcreate table文のカラム順をコントロール

SQLAlchemyでテーブル定義を記述するときに, いくつかのテーブルで定義するカラムは既定クラスやMixinクラスに書くことがあります.

SQLAlchemy 1.4以前だと, そのときに出力されるDDLのカラムの順序がコントロールしづらいと思っていましたが, SQLAlchemy 2.0でそのあたりが改善していたことに気づいたのでかんたんに紹介します.

# SQLAlchemy 1.4 の場合

SQLAlchemy 1.4 で既定エンティティクラス Base でPKを記述し, メタデータ相当のカラム created_at をMixinクラスで定義すると, こんな感じの書き方になると思います.

```python
class _Base(object):

    id = Column(Integer, primary_key=True, autoincrement=True)

    @declared_attr
    def __tablename__(cls):
        return cls.__name__.lower()

Base = declarative_base(cls=_Base)

class TimestampMixin(object):
    created_at = Column(DateTime, nullable=False)

class Item(TimestampMixin, Base):
    name = Column(String, nullable=False)
```

この class Item から次のようなDDLを生成できます.

```sql
CREATE TABLE item (
        id INTEGER NOT NULL,
        created_at DATETIME NOT NULL,
        name VARCHAR NOT NULL,
        PRIMARY KEY (id)
)
```

気持ち的にはちょっと違う感じしますね. created_at を一番下に持ってきてほしい.

SQLAlchemyと一緒に使われるマイグレーションツール Alembic でも, 同じようなスクリプトが出力されます.

```python
def upgrade() -> None:
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_table('item',
    sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
    sa.Column('created_at', sa.DateTime(), nullable=False),
    sa.Column('name', sa.String(), nullable=False),
    sa.PrimaryKeyConstraint('id')
    )
    # ### end Alembic commands ###
```

Alembicのスクリプトのなかに「please adjust!」とあるように, 適宜入れ替えればいいだけですし, `Base.metadata.create_all(engine)` をプロダクションコードで使うことも少ない気がしますが…….

SQLAlchemy 2.0 ではそのあたりがコントロールできるようになっていました.

# SQLAlchemy 2.0 ではsort_orderでカラムの順序を指定できる

SQLAlchemy2.0でカラム定義に使う mapped_column の sort_order 引数でカラムの順序をコントロールできるようになりました.

```python
class Base(DeclarativeBase):
    id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True, sort_order=-10)

    @declared_attr.directive
    def __tablename__(cls) -> str:
        return cls.__name__.lower()

class TimestampMixin(object):
    created_at: Mapped[datetime.datetime] = mapped_column(sort_order=10)

class Item(TimestampMixin, Base):
    name: Mapped[str]
```

次のDDLが出力されます. 期待したとおりですね.

```sql
CREATE TABLE item (
        id INTEGER NOT NULL,
        name VARCHAR NOT NULL,
        created_at DATETIME NOT NULL,
        PRIMARY KEY (id)
)
```

sort_order はデフォルト 0 なので, 負数をしておけば上に, 正数をしておけば下にカラムが出力されます.

SQLAlchemy 2.0 で利用できるようになった機能のなかでは目立たないものかなと思いますが, 雑にコードを書き捨てたい僕にはちょっとうれしいものでした. ちなみに使えるのは2.0.4 からみたいです.


# 参考

https://docs.sqlalchemy.org/en/20/changelog/whatsnew_20.html#orm-declarative-applies-column-orders-differently-control-behavior-using-sort-order
Laravel 10 で sweetalert2の使用方法


GitHub で詳細をご覧ください: SweetAlert2 
https://github.com/sweetalert2/sweetalert2

# 1. NPM の使用 
ターミナルを開き、Laravel プロジェクトのルート ディレクトリに移動します。
次のコマンドを実行して、SweetAlert2 をインストールします。
```php
npm install sweetalert2
```

```app.js
// ES6 Modules or TypeScript
import Swal from 'sweetalert2'
window.Swal = Swal;
```

```app.scss
@import '~sweetalert2/src/sweetalert2.scss';
```

アセットをコンパイル
```
npm run dev
```

# 2. CDN ファイルの使用 
```
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
```
または
```
<link href='https://cdn.jsdelivr.net/npm/sweetalert2@10.10.1/dist/sweetalert2.min.css'>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@10.16.6/dist/sweetalert2.all.min.js"></script>
```
VSCodeでAWS EC2に接続する

## はじめに
船井総研デジタルのoswです。AWSの学習中、EC2のファイルをviで編集するの嫌なのでVSCodeから直接編集できるようにします。
（私がvi慣れていないだけです）

## 学習環境
- Windows 11 Home 22H2
- VSCode 1.75.1

## 前提条件
- 既にAWSにログイン出来る状態であること
- EC2のインスタンスが作成されていること

AWSアカウントをお持ちでない方は[公式サイト](https://aws.amazon.com/jp/free/?nc2=h_ql_pr_ft&all-free-tier.sort-by=item.additionalFields.SortRank&all-free-tier.sort-order=asc&awsf.Free%20Tier%20Types=*all&awsf.Free%20Tier%20Categories=*all)より作成できます。EC2はアカウント登録した日から12ヶ月無料で使え、期間が経過した後は750時間/月を超えない限り無料、超えた場合に従量課金となるようです。

## VSCodeに「Remote - SSH」をインストールする
画像のようにRemote - SSHを選択、インストールします。
![remote - sshをインストール.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820803/ac780c95-193d-e218-aa60-2e70f177aaa3.png)

## Remote - SSHの設定
1. Remote - SSHインストール後、画像と同じアイコンを押下し、Remote - SSHの詳細画面を表示
![ギアアイコンを押下.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820803/9e68838f-4d49-c257-6c25-c8631677ac1f.png)

1. 「SSH」にマウスホバーしてギアアイコンを押下後、ホームディレクトリ側を選択
![ホームディレクトリを選択.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820803/4cfc2657-aa19-db44-d2fa-85e077fa49fb.png)

1. 生成されたsshのconfigファイルに接続先となるEC2の情報を記述します
    1. 接続先情報は[EC2ダッシュボード] - [インスタンス] - [対象のインスタンスを選択して「接続」] - [SSHクライアント]
![EC2接続先情報.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820803/3e3a99ba-fd71-55ae-b201-b17bfd356fc4.png)
    1. configファイルに下記情報を記述して保存。私はjenkins環境を構築したいのでHostをjenkinsとしています。
        1. Host: VSCode上の「SSH」に接続先として表示される名前
        1. Hostname: EC2接続先情報の例で表示される@より後ろの部分
        1. User: @より前の部分
        1. IdentityFile: インスタンス作成時に生成したpemファイルを指定
![configファイル作成.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820803/e0044682-97af-543b-a3d1-907c5dcbb0d3.png)

1. 画像のように再読み込みすると、Hostで指定した名前で接続先が表示される
![再読み込み.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820803/814d6936-834e-f5a8-daee-0d3fbe8ada92.png)

1. 作成した接続先にマウスホバーし、「→」を押下
![EC2へ接続.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820803/f4ab0ebc-f39c-515f-c22f-dedbf30870d2.png)

1. 画像左下の「SSH: jenkins」のように表示されれば接続完了
![接続完了.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2820803/8b12f29c-aa47-7b97-d253-734c8f24cbcc.png)

## おわりに
これでVSCodeからEC2のファイル編集が可能になりました。viなどで編集する必要がなくなります。

私がvi慣れていないので嫌なだけですが。大事なことなのではじめとおわりで2回言いました！
【Python】基本文法/リスト/ソート

※学習状況に合わせて追記していきます。

# 標準出力
print関数はデフォルトで末尾に改行が入る
```python:
print("Hello World")
// 半角スペース区切り
print("Hello", "World", "paiza")
```

print関数の末尾を改行以外にする
```python:
print("Hello", end=",")
# 空文字
print("Hello", end=",")
```

int型と浮動小数点型で区別される
```python:
#int型
print(8)

#不動小数点型
print(8.13)

#不動小数点型 → int型
print(int(8.13))
#実行結果 8
#小数点以下が切り捨てられる
```

整数と少数の演算時にコンピュータが少数をうまく判断ずに誤差が生じることがある。

割り算で結果を少数ではなく整数で出力
```python:
print(4383 // 7)
```

べき乗
```python:
#3の3乗
print(3 ** 3)
```

文字列のインデックスを取得
```python:
str = "Hello, World"
print(str[0])

#末尾の文字を取得
print(str[-1])
```

スライス記法
文字列の部分文字列を取得

```python:
str = "Hello World"
#インデックス2~4の文字を取得
print(s[2:4])
#先頭の文字列省略
print(s[:5])
print(s[7:])
```

文字列の長さを取得
```python:
str = "Hello World"
print(len(str))
```

文字列の反復
```python:
str = "Hello World"
print(str * 10)
```
変数を文字列の中で使う
(整数を文字列に変換)
```python:
time = 3
place = "公園"
print(f"{time}から{place}でおやつを食べる。")
```

数値から文字列に変換
```python:
time = 3
print(str(time) + 時からおやつを食べる
```

文字列から数値に変換
```python:
#整数
str = "10"
ans = int(str)

#浮動小数点数
str = "8.43"
ans = float(str)
```

# リスト

インデックスを通じて複数のデータをまとめて管理できる
リストの要素→リストで管理する値

リストを生成
```python:
li = [3, 5, 7]

#インデックス1を取得
print(li[1])

#インデックス1の要素を変更
li[1] = 8

#リストの長さ(要素数)取得
print(len(li))

#リストの結合
li2 = [9, 5, 2]
print(li + li2)

#リストの末尾に要素を追加
li.append(4)

#リストの末尾の要素を削除しながら取得
a = li.pop()

#リストの要素を指定して削除
li.remove(2)
del li[2]
```
# ソート
破壊的ソート→リストの要素そのものの順番を変える
非破壊的ソート→リストの要素そのものの順番を変えない
```python:
li.sort()

#ソートしながら新しいリストを生成
sorted(li)

#降順
li.sort(reverse=True)
sorted(li, reverse=True)
```
リスト→文字列
※joinは文字列以外には使用できない
```python:
li = ["apple", "orange", "banana"]

#liの各要素を半角スペース区切りで結合
print(" ".join(li))
```

文字列→リスト
```python:
# 文字列の各文字がリストの要素になる
str = "apple"
print(list(str))

# 「,」区切りで文字列をリストの要素にする
str = "apple,orange,banana"
print(str.split(","))

```

# タプル

インデックスを通じて複数のデータをまとめて管理できる。
リストと違い要素の変更ができない。

タプルを生成
型が混合しても平気
```python:
a = (1, 2, "apple")
b = 1, 2, "apple"
c = ([1, 2, 3], ["apple", "orange", "banana"], [4, 5, 6])
d = (a, b, c)

```

# アンパック
中身を取り出して変数に代入する

```python:
a,b,c = 1, 2, 3
print(a, b, c)
ALB+EC2で送信元IPがほしかった話

AWSの公式ドキュメント見たらええよ、というだけの話ではあるんですが。

## 背景
```mermaid
%%{init:{'theme':'base','themeVariables':{'primaryColor':'#20b2aa','primaryTextColor':'white','primaryBorderColor':'#20b2aa','lineColor':'gray','textColor':'#6A7FABCC','fontSize':'20px'}}}%%

graph LR;
A(("‥<br>ー"))--"リクエスト"-->B("ALB")--"転送"-->C("EC2インスタンス")-."出力".->D["アプリケーションログ"]
```
こんな構成の場合。
※ちょっと図が微妙ですが、アプリケーションログはEC2インスタンス内に存在するファイルです。

EC2インスタンス内のアプリケーションで出力するログで「リクエスト元の情報がほしいな」と思い、
リクエストヘッダに入っているホストをそのまま出力すると、当然ながら**ALBのIP**が出力されます。

一応、ALBでもアクセスログを出しておけばリクエストの送信元のIPはわかるし
それとアプリケーション側で出したログを突き合わせれば、そのままでも欲しい情報を調べることはできなくもないです。

しかし実際にログを調べたくなるような場面において、都度そんな作業をするのは手間なので
ALBにリクエストを送信してきたホスト情報をそのままEC2インスタンス内でも使用したい。

## ALBではX-Forwarded-Forがそのまま取れる
[公式ドキュメント](https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/application/x-forwarded-headers.html)によると、X-Forwarded-ForをALBが渡してくれるのでそれを使ってくれよな！とのこと。

デフォルトではオリジナルのリクエストに含まれているX-Forwarded-Forに、リクエスト元のIPを追加する仕様になっています。
ちょびっと設定を変更すれば、オリジナルのリクエストのものをそのまま送らせたり、逆に削除させるといったことも可能です。

## 具体例
今回はTomacatのログでこれを出力したく、下記のような設定を記述しました。

Tomcat8での設定はこんな感じです。
```server.xml
<Server>
  〜中略〜
  <Service name="Catalina">
    <Engine name="Catalina" defaultHost="localhost">
      <Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="false">
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
               prefix="access.log"
               fileDateFormat=".yyyyMMdd"
               renameOnRotate="true"
               pattern='{ "host":"%{X-Forwarded-For}i","user":"%u","time":"%{yyyy-MM-dd&apos;T&apos;HH:mm:ss.SSSZ}t","method":"%m","path":"%U%q","protocol":"%H","status":%s,"size":"%b","referer":"%{Referer}i","location":"%{Location}o","agent":"%{User-Agent}i","cookie":"%{cookie}i","set_cookie":"%{Set-Cookie}o","authorization":"%{Authorization}i","response_time":%D }' />
    …
```
JSON形式にしたくてpattern指定しているのですが、そこで`"host":"%{X-Forwarded-For}i"`と書いているのが該当箇所です。

この結果出力されるのが下記の様な感じ。
```json
{ "host":"60.127.230.44","user":"-","time":"2023-02-20T10:17:12.738+0900","method":"GET", ...
```
無事出力されていてニッコリ :smile: ほっこり:relaxed:

ということで、特別なことはなくすんなり欲しい情報が取れました。

ちなみにALBからターゲットグループへのヘルスチェックなど、ALB自身からのアクセスの場合X-Forwarded-Forの値はないので`-`と出力されていました。
```json
{ "host":"-","user":"-","time":"2023-02-20T12:08:45.396+0900","method":"GET","path":"/","protocol":"HTTP/1.1","status":200,"size":"-","referer":"-","location":"-","agent":"ELB-HealthChecker/2.0","cookie":"-","set_cookie":"-","authorization":"-","response_time":0 }
```
他のフィールドでヘルスチェックだとがわかるし、特に必要な情報でもないのでこれは問題なさそう。

-----
#### 参考
* https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/application/x-forwarded-headers.html#x-forwarded-for
* https://aws.amazon.com/jp/premiumsupport/knowledge-center/elb-capture-client-ip-addresses/
* https://tomcat.apache.org/tomcat-8.5-doc/config/valve.html#Access_Logging
カスタムフック作成時にUncaught Error: Invalid hook callが出てしまった

## エラーメッセージ
Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app

function componentの中でのみHooksを呼ぶことができますとエラーが出た

## 問題&解決策
カスタムHook内のcallback関数内でuseXXXを使用していたのが問題でした。
callback関数ではなく1つ外でuseXXXを呼んであげることでエラーが解消しました。

## エラーになるコード例
``` App.tsx
import useTextInput from "./useTextInput";

const App = () => {
  const nameInput = useTextInput("");

  const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    console.log(`Hello, ${nameInput.value}!`);
    nameInput.clear();
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Name:
        <input type="text" {...nameInput} />
      </label>
      <button type="submit">Submit</button>
    </form>
  );
};

export default App;
```

```hooks.ts
import { useState } from "react";

const useTextInput = (initialValue: string = "") => {
  // ここはuseXXXを呼んでも問題ない
  const [value, setValue] = useState<string>(initialValue);

  const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    // callback関数の中でhook(useXXX)を使ってしまうとエラーが発生する
    const [dummy, setDummy] = useState<string>(initialValue);
    setValue(event.target.value);
  };

  const clear = () => {
    setValue("");
  };

  return {
    value,
    onChange: handleChange,
    clear,
  };
};

export default useTextInput;

```
【Python概要】Pythonとは

# Python とは

1991年に登場
オランダ人にグイド・ヴァン・ロッサムさんが開発。
ライブラリが豊富にある。

人工知能(AI)や機械学習にも対応。
データ収集、解析などの統計処理や作業の効率化にも有効。
身近なサービスだとYouTube、インスタ、Dropbox、Evernote、ペッパーくんが有名。

# スクリプト言語を採用

スクリプト言語
    コードの理解がしやすい言語
    ※厳密な定義なし
    →インタプリタ言語を採用(事前のコンパイル不要:実行速度が遅い)

人気な理由
    開発速度が速い→必要なときに必要なサービスを提供
    読み書きしやすい
    コードの記述量が少ない
    C言語と相性がいい
    
# フレームワーク

・Djano
・Flask
・Bottle


Djano
    質の高いアプリケーションを短いコードで(インスタ)
    セキュリティ対策〇 (ログインシステム)
    拡張性〇 (用途に合わせてファイルを拡張、それぞれの部品をカスタマイズする感覚で)
    網羅性〇 (管理画面、リンク表示、ユーザー認証システムが既に搭載)
Dockerコマンド これだけ覚えておけばOK

# Docker コマンド  

Docker 初心者の私が、よく使いそうなコマンドをまとめておく。（なぜなら、使わなくなるとすぐ忘れちゃうから。）  
ありもののDocker イメージを引っ張ってきて、使うだけであれば、これくらいでいけるかな。  
まだ、DockerFileを作るレベルには至っていないので、そのくらいのレベル感で。

## Docker イメージ取得

`docker pull NAME[:TAG]` でDocker イメージを取得する。

```shell
docker pull ubuntu:16.04
docker pull ubuntu:latest
```

## Docker イメージ一覧

```shell
> docker images
REPOSITORY            TAG       IMAGE ID       CREATED         SIZE
ubuntu                16.04     fe3b34cb9255   16 months ago   119MB
```

## コンテナの作成・起動

`docker run -it IMAGE` でイメージからコンテナを新規に作成して、起動する。  
`--name NAME` のオプションで、コンテナに名前を付けられる。
`-it` は、ttyをコンテナプロセスに割り当てる。ttyって何？って思うけど、擬似端末のことで、この例の場合、ubuntuのターミナルに接続する。ってことを覚えておけばOKかな。  
`-d` を付けると、バックグラウンドで実行。  
`-itd`と`-it`にくっ付けても、`-it -d`でも同じ。  

```
docker run -it ubuntu:16.04
docker run -it --name ubuntu1604 ubuntu:16.04
```
`run`は実行するたびに、新しいコンテナが作られるので注意。  
作成済みのコンテナを起動する場合は、`start`を使用する。

## 起動中のコンテナ一覧  

起動中のコンテナ一覧を確認。  
`-a` を付けると、停止中のコンテナも表示される。  
`-s` で、コンテナサイズも表示。


```shell
> docker ps 
CONTAINER ID   IMAGE                     COMMAND             CREATED        STATUS                             PORTS                                                          NAMES
a48b4a8eb26e   ubuntu:16.04              "/bin/bash"         6 months ago   Up 7 seconds                                                                                      ubuntu1604
```

## コンテナに接続  

`docker exec CONTAINER bash` で起動中のコンテナに接続する。  
`CONTAINER`には、作成時に`--name`で名前を付けたものを指定する。（この場合、`ubuntu1604`）


```shell
docker exec -it ubuntu1604 bash
```

`docker exec CONTAINER COMMAND` で、コマンドを実行。

```shell
docker exec -it ubuntu1604 ls
```

## ファイルコピー  

`docker cp SRC_PATH CONTAINER:DEST_PATH` でローカルのファイルをコンテナ内にコピーする。

```shell  
docker cp ./some_file ubuntu1604:/work
```

## コンテナの起動・停止  

コンテナを起動、停止するためには、`start`、`stop`を使用する。
`restart`で両方同時に可能。  

```shell  
docker start ubuntu1604
docker stop ubuntu1604
docker restart ubuntu1604
```

## コンテナの削除  

コンテナを削除するためには、`rm`を使用する。(もちろん、削除前には停止が必要)

```shell  
docker rm ubuntu1604
```

## コンテナの詳細情報  

コンテナの詳細な情報は、`inspect` で見ることができる。（lessで見るのがいいかな）

```shell  
docker inspect ubuntu1604 | less
```

## コンテナのCPU・メモリ使用率  

コンテナのCPU・メモリ使用率などのリソースの使用状況は、`stats` で確認する。

```shell  
docker stats ubuntu1604
```

## コンテナのログ  

コンテナのログは、`logs` で確認できる。

```shell  
docker logs ubuntu1604
```
以下のオプションが便利。`docker logs -n 100 ubuntu1604` など。

| オプション | 説明 |
|:-:|:-:|
| --tail 100, -n 100 | 最後から100行 |
| --follow, -f | ログ出力を続ける |
| --timestamps, -t | タイムスタンプを表示 |
| --since 2023-02-20T00:00:00Z<br>--since 43m | 2023-02-20T00:00:00〜を表示<br>43分前~を表示 |
| --until 2023-02-25T00:00:00Z<br>--until 43m | 〜2023-02-25T00:00:00を表示<br>~43分前を表示 |

コンテナのログは、以下のファイルに保存されている。  
```
/var/lib/docker/containers/[CONTAINER ID]/[CONTAINER ID]-json.log
```
`docker inspect [CONTAINER] | grep LogPath`でパスを確認できる。
いつの間にか巨大サイズになっているので、要注意。
M1 Mac に pyenv で Python 環境（2023.02.25）


2023年2月19日（日）より仕事でマレーシアに来ている。
夜は寝っ転がって iPad で YouTube を見たりネット検索などをやっているのだが、そんな中、昨晩 M2 Mac に pyenv で環境構築する記事を見つけた。

(1) Python 環境構築
[https://analytics-note.xyz/mac/python-apple-silicon/](https://analytics-note.xyz/mac/python-apple-silicon/)

Mac が M1 になってからは、scipy などを入れるには Miniforge 一択だったのだが、最近は pyenv で scipy や scikit-learn を含む Python 環境が作れるらしい。

ということで、リスクはあるが、これまで問題なく動いていた Miniforge を削除し、pyenv による環境を作ってみることにした。

(2) Miniforge アンインストール
[https://zenn.dev/karaage0703/articles/f3254b14898b4d](https://zenn.dev/karaage0703/articles/f3254b14898b4d)

まずは上記 (2)の記事に従い、Miniforge をアンインストール。

次に (1) の記事に従い、pyenv での Python 環境を作る。
Home brew はすでにインストール済なので、`brew install pyenv`  から始める。
Python のバージョンは 3.11.2 を選択した。

Python が入ってしまえばあとは pip で必要なライブラリをインストールしていく。

```
pip install numpy
pip install scipy
pip install matplotlib
pip install pandas
pip install openpyxl
pip install scikit-learn
pip install jupyter
pip install jupyterthemes
pip install PyPDF2
```

### （コメント）
##### scikit-learn
`scikit-learn` はなぜか１回めのインストールは失敗。２回めに何も考えずpipしたら問題なくインストールできた。

##### pillow
`pillow` は `patplotlib` と同時にインストールされている模様。
個別にインストールしようとすると、以下のようにでる。

```
~$ pip install pillow
Requirement already satisfied: pillow in ./.pyenv/versions/3.11.2/lib/python3.11/site-packages (9.4.0)
```

##### jupyter notebook
`jupyter notebook` のテーマは `oceans16` としている。以下のコマンドで 
 `jupyter notebook` のテーマとフォントサイズなどを指定。フォントは特にこだわらず、`monospace` でいい。

```
jt -t oceans16 -fs 12 -ofs 12 -cellw 1000 -lineh 120 -N -T
```
なお、Mac のデフォルトのブラウザである Safari では Jupyter notebook の立ち上がりやスクロールがかなり遅い。このため、Jupyter notebook を立ち上げるブラウザは、FireFox にしている、変更方法は、自分の投稿で恐縮であるが、以下を参照。

[https://qiita.com/damyarou/items/5c319f5def57c6e36a37](https://qiita.com/damyarou/items/5c319f5def57c6e36a37)


##### cartopy （インストール失敗：未解決）
たまに地図作成用ライブラリである `cartopy` を使うのだが、これはインストールできない。「pip の問題ではなく、内部の subprocess の問題である。。。」的なメッセージがでる。`cartopy` の使用頻度は高くないので、現時点では放っておくことにするが、どなたかインストールを成功させた方がいたら是非ご教授をお願いしたい。


##### （追記）cartopy （うまくいったかも！）
ネットで検索していたら以下の記事を見つけた。

Cartopyのインストール
[https://labo-code.com/python/cartopy-install/](https://labo-code.com/python/cartopy-install/)

色々いじってしまったので、確実ではないのだが、現時点では、以下で行けそう。

```
brew install geos
pip install pyshp
pip install shapely
pip install cartopy
```

なおついでに `geopy` もインストールした。

```
pip install geopy
```


#### pip でいれたライブラリの管理

```
pip install pip-review
```
`pip-review` の使い方は以下を参照。

[https://zenn.dev/kittybbit/articles/bf8dd1db0e307d](https://zenn.dev/kittybbit/articles/bf8dd1db0e307d)


### マシン
申し遅れたが、私のメインマシンは MacBook Pro 14" である。

+ Chip: Apple M1 Pro
+ Memory: 16 GB
+ macOS: Ventura 13.2.1

以　上
【Flutter】ThemeDataのスタイルを受けたテキストの一部だけ色を変えるには？

# はじめに
テキストの一部だけ色を変えたい場合があるかと思います。
実はFlutterの場合はHTML/CSSで言う`<span class="red">`のようにサクッとできないんです:sweat:

通常使う`Text`とは少し違うウィジェットを使わないといけません。。

# RichTextを使うとThemeDataが反映されない？
とりあえず試したみたのは`RichText`ウィジェット。中に入る`TextSpan`ウィジェットは入れ子構造なので途中でスタイルを変えることができます。

```dart
              RichText(
                text: const TextSpan(
                  children: [
                     TextSpan(text: "参加する"),
                      TextSpan(
                      text: '女性',
                      style: TextStyle(color: Pallete.primaryColor)),
                      TextSpan(text: '陣の名前を入力してな〜'),
                  ]
                ),
              ),
```

しかし！！！
これではmain.dartで設定している`ThemeData`が反映されないようです。。。
個別で同じスタイル設定をつけるのも嫌なので、別の方法を探してみました:sweat_smile:


# Text.rich()を使えばそのままでOK

```dart
              const Text.rich(TextSpan(children: [
                TextSpan(text: '参加する'),
                TextSpan(
                    text: '女性', style: TextStyle(color: Pallete.primaryColor)),
                TextSpan(text: '陣の名前を入力してな〜'),
              ])),
```

`Text`ウィジェットのrich()コンストラクタを使う方法もあるようです。
これなら設定している`ThemeData`からスタイルをそのまま反映されてました:relieved:


https://api.flutter.dev/flutter/widgets/Text/Text.rich.html





# 参考

https://stackoverflow.com/questions/51379194/richtext-does-not-style-text-as-expected
【オープンなモデルでChatGPT】Google Colab 上で GPT-NEOX-20B + LangChainを用いたChatGPT clone を作成

# ChatGPTをオープンなモデルで作成したい

どうも、ChatGPT面白いですよね。私もよく遊んでいます。

今回はオープンなモデルであるGPT-NEOX-20Bを用いてChatGPTのクローンを作成していきたいと思います。

簡単に言うとLangChainのCostomLLMでGPT-NEOX-20Bを使えるようにしてやりました。

あと、結構コピペで作成しているので詳しく理解したい方は参考URLを読んでください。非常に参考にしています。

特にColabでのGPT-NEOX-20Bの使い方の部分は一人では難しかったです。ありがとうございました。

colabのリンクだけならこちら
https://colab.research.google.com/drive/1y3Dfv12mmr5O8nwzn4MrvDVKsMQQKwt6?usp=sharing

# 肝となる部分

```Python:
class CustomLLM(LLM):
    
    n: int

    @property
    def _llm_type(self) -> str:
        return "custom"
    
    def _call(self, prompt: str, stop: Optional[List[str]] = None) -> str:
        if stop is not None:
            raise ValueError("stop kwargs are not permitted.")
        else:
            # 推論
            input_ids = tokenizer(prompt, return_tensors="pt").input_ids
            input_ids = input_ids.to('cuda')
            output_tokens = model.generate(
                input_ids,
                do_sample=True,
                max_length=500, # 最長の文章長
                min_length=100, # 最短の文章長
                temperature=0.9, # 温度   
                num_return_sequences=1, # 生成する文章の数
            )
            output = tokenizer.batch_decode(output_tokens)[0]
            return output
```

LangChainのCustomLLMはCall関数があればいいらしいので、そこにGPT-NEOX-20Bの出力できるようにしています。

あと、意味のないnとかも残ってしまってるんでいい感じに直してください。とりあえず動くものを作りました。

# 注意事項

参考記事にもありますが、GoogleColab pro+とGoogledriveのスタンダード以上の課金が必須です。

逆に言えばそれだけでOpenAIに依存せずにChatGPTに近いことはできます。

GPT-NEOX-20BはOpenAIのモデルと比べるとCurieとDavinciの線形補完ぐらいの精度があり、本家ChatGPTはDavinciなのでそこそこいけるかなと思っていたのですが、あんまりいい結果が返ってきていません……

そもそも一対一対応の返事がこない（かってにChatで一人芝居を始めてしまう）であったりとか、そもそも返答の質もあんまりよくないように感じます。


# 参考にした記事
https://note.com/npaka/n/n09aeb00bf72b

https://langchain.readthedocs.io/en/latest/modules/memory/examples/chatgpt_clone.html

https://langchain.readthedocs.io/en/latest/modules/llms/examples/custom_llm.html

# 余談
ChatGPTの仕組が実際にクローンを作成してみて少しだけ理解出来ました。本当に文章生成しているだけで、あたかもChatしているように見えるのはプロンプトエンジニアリングの成果ということなのですね。プロンプトによっていい感じに返答を生成できるようにコントロールしつつ、過去のChatをメモリーとして記憶することが出来る。

実際にこうやって動かすとわかることも多いのですね。

# 今後のタスク
- [ ] プロンプトエンジニアリングによって改善するか試す
- [ ] CustomLLMの使い方はわかったので他のOpenModelを試す（OPTとかBLOOMとか）
IBM Cloud docsがpdfで参照できるようになりました




例えば、[IBM CloudのVPCのdocs](https://cloud.ibm.com/docs/vpc?topic=vpc-release-notes&locale=ja)を例に見てみます。
以下のハンバーガーボタンを押すと、`PDFで表示`という選択肢が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/5a8dee4f-8c6e-c014-9b25-1c3fb1a3be6b.png)

これを選択することで、、、1489ページものpdfが表示されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/aed9287e-0d70-96ff-f1d7-86e8d32fd71b.png)


以下に注意点を挙げます
- ドキュメントは英語版が最新です。他言語に翻訳されるまでには一定のタイムラグがあります。特にrelease notesなどの最新情報は英語版を参照した方がいいでしょう。
- この手順で生成されるpdfもHTML版と比べて常に最新とは限らないようです。例えば、英語版でpdfを生成しても、以下のような記載があります。もし最新情報を参照したいのであればやはりHTML版を見ましょう。![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/62127/053c53e9-8d32-36b2-9d14-ee9e98eaef9a.png)
Nervesやっている人でディスク容量が少ない人は~/.nervesを消すとよいかもしれません

<b><font color="red">$\huge{元氣ですかーーーーッ！！！}$</font></b>
<b><font color="red">$\huge{元氣があればなんでもできる！}$</font></b>

<b><font color="red">$\huge{闘魂とは己に打ち克つこと。}$</font></b>
<b><font color="red">$\huge{そして闘いを通じて己の魂を磨いていく}$</font></b>
<b><font color="red">$\huge{ことだと思います}$</font></b>


# はじめに

[Nerves](https://nerves-project.org/)は、[Elixir](https://elixir-lang.org/)でIoTを楽しめる仕組みです。
[Nerves](https://nerves-project.org/)やっているあなたへ、ディスク容量が少なくて困っている方はいらっしゃいませんか？
そんな方にお知らせしたいことです。

```elixir
iex> "Elixir" |> String.graphemes() |> Enum.frequencies()
%{"E" => 1, "i" => 2, "l" => 1, "r" => 1, "x" => 1}
```

# rm -fr ~/.nerves

ありがとう！　@mnishiguchi さん！

https://github.com/nerves-project/nerves/commit/63c4e5c94a8e1f1a5f29b3496f89300fe19f948a

![スクリーンショット 2023-02-25 11.03.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131808/19f139c6-9100-0e37-0836-a08da9026be5.png)

このTipsを書いてくださったのは、@mnishiguchi さんです。

https://hexdocs.pm/nerves/getting-started.html#building-and-deploying-firmware

私の場合、`rm -fr ~/.nerves`で32GBあきました。以前、Nerves Systemのカスタマイズビルドとかやっていましたのでそういったものが溜まっていたようです。
必要になったら`mix deps.get`でまたダウンロードなどしてくれます。


# さいごに

Nervesやっている人でディスク容量が少ない人は`~/.nerves`を消すとよいかもしれません。
「よいかもしれません」と書いております。「~/.nervesを消しましょう！」とは言っていません。
よくよく各自の事情を鑑みて、実行してください。


![スクリーンショット 2023-02-25 11.03.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131808/19f139c6-9100-0e37-0836-a08da9026be5.png)

---

<b><font color="red">$\huge{元氣ですかーーーーッ！！！}$</font></b>
<b><font color="red">$\huge{元氣があればなんでもできる！}$</font></b>
<b><font color="red">$\huge{１、２、３ ぁっダァー！}$</font></b>
【 Java 】Tomcat構築

## ■情報
・Eclipse ： Version 2022-12 (4.26.0)
・Java    ： Version 17

## ■構築フロー
**①「動的Webプロジェクトを作成します」をクリック**
![スクリーンショット 2023-02-25 11.02.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192922/ef8344f8-62b2-7c52-9e25-819f338b927d.png)
***
**②「プロジェクト名」に任意の名前を入力し「完了」をクリック**
![スクリーンショット 2023-02-25 10.50.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192922/2aeaa708-28f9-f3a5-1581-5d8815806de8.png)
***
**③プロジェクトを右クリックし、「新規」 > 「HTMLファイル」を選択し、htmlファイルを作成**
![スクリーンショット 2023-02-25 10.53.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192922/304611eb-b0b9-55b3-47e7-86e394fde25f.png)
***
**④ビュー内のサーバーをクリック。「使用可能なサーバーがありません。〜」の文言をクリック**
![スクリーンショット 2023-02-25 10.54.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192922/3715b80e-7604-2753-f7e3-48315283a38d.png)
***
**⑤任意のサーバータイプを選択し、「次へ」をクリック**
![スクリーンショット 2023-02-25 10.55.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192922/da5af380-74a1-cf67-7a12-4955188d0b17.png)
***
**⑥使用可能なリソース内に作成したプロジェクトがあるので、選択した状態で「追加」をクリック**
![スクリーンショット 2023-02-25 10.55.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192922/aa6d1def-a0c2-828c-675f-c1cf836b5258.png)
***
**⑦「構成済み」の方にプロジェクトが移動していることを確認し「完了」をクリック**
![スクリーンショット 2023-02-25 10.56.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192922/fc35daa9-4b84-caac-2470-b5777cb124ce.png)
***
**⑧ビュー内のサーバーにプロジェクトが追加していることを確認**
![スクリーンショット 2023-02-25 10.56.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192922/24155a64-f5e2-140d-4683-aaf7dd958b54.png)
***
**⑨「localhostの〜」を右クリックし「開始」をクリック**
![スクリーンショット 2023-02-25 10.56.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192922/727f873b-bed1-f567-a831-bbec7b00f557.png)
***
**⑩ビュー内のコンソールを見て、サーバーが起動済みになっていることを確認**
![スクリーンショット 2023-02-25 10.57.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192922/127a09d8-9566-8549-cc2e-e520df9ed123.png)

**⑪アドレスバーに「localhost:ポート番号/コンテキストパス/作成したhtmlのファイル名」を入力し表示されたら構築完了**
![スクリーンショット 2023-02-25 10.58.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3192922/41f2c2cc-3bde-e785-6cd3-76d6d75c41db.png)

## ■最後に
入社した当初の研修ではSpring Bootを使っていたためTomcatは内包されていて構築する必要がありませんでした。
そのため今まではただ起動するだけで良かったのですが、本来は今回のような構築フローが必要だということを知りました。
go installで複数バージョンを使用する方法

go installを使用して、複数バージョンを扱う方法についての備忘録です。

環境：macOS, zsh, go@1.20

### 1. GOPATHとして使用するディレクトリを作成する
```bash
mkdir $HOME/go
```

すでに、`GOPATH`のディレクトリが存在する場合は行う必要はありません。
GOPATHは、`go env GOPATH` を実行することで確かめることができます。

### 2.PATHを追加する

`.zshrc`に以下を追記します。
```bash
export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin
```

その後`source ~/.zshrc` を実行して設定を読み込んでください。

### 3. 使用したいバージョンをインストールする

```bash
go install golang.org/dl/go1.16.3@latest
go1.16.3 download
```
使用できるバージョンについては、以下を参考にしてください。
https://go.dev/dl/

### 4.バージョンを指定して使用する
```bash
go1.16.3 run main.go
Hello World!
```
実行できました。

### 注意点

go download にコケる場合は PATHが通っていません。

```bash
which go1.16.3 # インストールしたバージョンを指定する
/Users/example/go/bin/go1.16.3 # パスが表示される
```

そのため、パスを通すとダウンロードできます。
```
export PATH=$PATH:/Users/example/go/bin/
go1.16.3 download
```



### 参考

https://go.dev/doc/manage-install?s=09#installing-multiple
https://zenn.dev/awonosuke/articles/47336619a4f039
PHPの命名規則を最も有名なコーティング規約のPSRから教えてもらう

# PSRとは
- PHP Standard Recommendation の略。
直訳すると「PHP標準の推奨事項」であり、その名のとおりPHPでコードを書く際の推奨事項をまとめたもの。
PHPで最もスタンダードなコーディング規約である。

- 策定団体はPHP-FIG
PHP Framework Interop Group の略。
直訳すると「PHPフレームワーク相互運用性グループ」
PHPの各フレームワークごとにコーディング規約が微妙に異なる → 統一的なルールの策定のため、各フレームワークの関係者が集まって結成された。
なお当団体はPHPの非公式団体である。

# PSRから読み解く命名規則
- 変数名、定数名、関数名については記載なし。
- クラス名・クラス定数名・プロパティ・メソッドについては[PSR-1](https://www.php-fig.org/psr/psr-1/)に記載あり。
（概要は以下のとおり）
- プロパティについては「書き方はなんでも良いけど合理的な範囲で一貫性を持たせようぜ」と書いてあり、特に記法に定めはない。

```php:クラス名・クラス定数名・プロパティ・メソッドについて
ClassName // クラス名：アッパーキャメルケース（スタッドリーキャップス）
{
    const MIN_VALUE = 0.0;  // クラス定数名：アッパースネークケース

    public $bar = 0;  // プロパティ名：定めなし

    public function calcInclTax(){ // メソッド名：キャメルケース

    }
}
```

# 通常の変数名、定数名、関数名についてはどうすべきか

- クラスと同様に運用するのが分かりやすいと思われる。PSRに準拠するなら以下のような感じ。
定数 → アッパースネークケース
関数 → キャメルケース

# プロパティや変数についてはどうすべきか
- PSR-1においてプロパティ名は、あえて規則を定義していない。おそらく厳密に定めるとかえって開発効率が落ちるからだと思われる。
- PHPの公式マニュアルを確認したが、スネークケースで書いていることもあれば、キャメルケースで書かれていることもある（参考にしたページ[「変数：基本的な事」](https://www.php.net/manual/ja/language.variables.basics.php)[「プロパティ」](https://www.php.net/manual/ja/language.oop5.properties.php)）。
- ネットの各記事を見てもこの辺りの運用はまちまちであり、各プロジェクトでどう命名規則を定めるかによる。
Elixir v1.17ではIO.read(:all)はDeprecateだってよ（2023-02-25現在v1.14）

<b><font color="red">$\huge{元氣ですかーーーーッ！！！}$</font></b>
<b><font color="red">$\huge{元氣があればなんでもできる！}$</font></b>

<b><font color="red">$\huge{闘魂とは己に打ち克つこと。}$</font></b>
<b><font color="red">$\huge{そして闘いを通じて己の魂を磨いていく}$</font></b>
<b><font color="red">$\huge{ことだと思います}$</font></b>


# はじめに

[IO.read/2](https://hexdocs.pm/elixir/IO.html#read/2)のドキュメントをみていました。
[AtCoder](https://atcoder.jp/?lang=ja)などの競技プログラミングでインプットを読み取るのに使います。

引数は2個ですが、第一引数には`:stdio`が指定されていて、標準入力から読み込みますので、実質`IO.read/1`といっても過言ではありません。
引数に何を指定するのかですが、ドキュメントには`正の整数`、`:line`、`:eof`の3種類が書いてあります。


![スクリーンショット 2023-02-25 10.12.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131808/1dcf637c-3ed2-3c28-c5cf-582a7aeead1e.png)


`IO.read(:all)`と書いてすべての入力を読み取る例をどこかで見た記憶があります。実際動きます。

ただですよ！　奥さん、ドキュメントに`:all`なんてものは書いていないのです。
果たして!?






```elixir
iex> "Elixir" |> String.graphemes() |> Enum.frequencies()
%{"E" => 1, "i" => 2, "l" => 1, "r" => 1, "x" => 1}
```

# [IO.read/2](https://hexdocs.pm/elixir/IO.html#read/2)のソースコードを読もう！

[<<//>](https://github.com/elixir-lang/elixir/blob/v1.14.3/lib/elixir/lib/io.ex#L146) こんなやつをぽちっとおせばコードが見れます。

![スクリーンショット 2023-02-25 10.12.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131808/1dcf637c-3ed2-3c28-c5cf-582a7aeead1e.png)

https://github.com/elixir-lang/elixir/blob/v1.14.3/lib/elixir/lib/io.ex#L148-L158

おー、なるほど。

![スクリーンショット 2023-02-25 10.15.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131808/60b8035e-f30e-1de5-cbe2-47280d04a343.png)

**# TODO: Deprecate me on v1.17**

v1.17っていつの話なのですかね。
未来を先取りです。

# すべての入力を読み取るにはどうしたらいいの？

`:eof`を指定すればいいのだとおもいます。
`sample.exs`を作ってみます。

```elixir:sample.exs
IO.puts "なにか文章を入力してください。入力の終了はCtl+Dです。"
string = IO.read(:eof)

IO.puts "-----------------"

string
|> String.split("\n")
|> Enum.join()
|> IO.puts()
```

実行してみます。

```
elixir sample.exs
```

を実行すると、

```
なにか文章を入力してください。入力の終了はCtl+Dです。
```

と促されますので適当に文章を入力します。入力の終了はCtl+Dです。

```
吾輩は猫である。名前はまだ無い。
どこで生れたかとんと見当がつかぬ。
何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。
```

とでも入力して、エンター、Ctl+Dです。
すると、以下が出力されます :tada::tada::tada:

```
-----------------
吾輩は猫である。名前はまだ無い。どこで生れたかとんと見当がつかぬ。何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。
``` 



# さいごに

`IO.read(:all)`はv1.17でDeprecateです。
標準入力から全部読み込みたいときは、`IO.read(:eof)`です。





---

<b><font color="red">$\huge{元氣ですかーーーーッ！！！}$</font></b>
<b><font color="red">$\huge{元氣があればなんでもできる！}$</font></b>
<b><font color="red">$\huge{１、２、３ ぁっダァー！}$</font></b>
データ分析・活用　SQL入門を読んで

# TL;DR
https://www.amazon.co.jp/SQL%E3%83%87%E3%83%BC%E3%82%BF%E5%88%86%E6%9E%90%E3%83%BB%E6%B4%BB%E7%94%A8%E5%85%A5%E9%96%80-%E3%83%87%E3%83%BC%E3%82%BF%E3%82%B5%E3%82%A4%E3%82%A8%E3%83%B3%E3%82%B9%E3%81%AE%E6%89%89%E3%82%92%E9%96%8B%E3%81%8F%E3%81%9F%E3%82%81%E3%81%AE%E6%8A%80%E8%A1%93-MySQL-PostgreSQL-%E4%B8%A1%E5%AF%BE%E5%BF%9C/dp/4802612265/ref=sr_1_5?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=27S21HUVI7MRJ&keywords=%E3%83%87%E3%83%BC%E3%82%BF%E5%88%86%E6%9E%90%E3%81%A8%E3%83%87%E3%83%BC%E3%82%BF%E3%82%B5%E3%82%A4%E3%82%A8%E3%83%B3%E3%82%B9&qid=1676728235&sprefix=%E3%83%87%E3%83%BC%E3%82%BF%E5%88%86%E6%9E%90%E3%81%A8%E3%83%87%E3%83%BC%E3%82%BF%E3%82%B5%E3%82%A4%E3%82%A8%E3%83%B3%E3%82%B9%2Caps%2C165&sr=8-5

これを読んだメモです。自分が知らなかったことととか書いてきます。個人的には、長いSQL文を読む、というところに1章使っているのがいいなと思いました。（その割に薄っぺらい感想ですが、まあSQL文読むってのは実践あるのみだと思うので・・・）

# SQLの実行順について
select文の実行順はFROM→JOIN→ON→WHERE→GROUP BY→HAVING→SELECT→OVER(ウィンドウ関数)→ROWS/RANGE(ウィンドウ関数)→PARTITIOON BY(OVER句内)→ORDER BY(OVER句内)→ORDER BY→LIMITとなる。これを意識するだけでも実行性能に効いてくる
例えば、HAVINGとWHEREはどちらも条件でデータを絞る、ものだが、できればWHEREで絞れるものは絞ったほうがいい。なぜなら、先に実行されるWHEREで条件を絞ったほうが、SQL実行順の関係で早くデータを絞ることができるため。

# NULLとCOALESCE関数
SQLでは、NULLと文字列を結合するとNULLとなる（NULL伝播という）。そのため、四則演算をしたいが、NULLカラムがある場合は、置き換えが必要。そこでCOALESCE関数を使う。
COALESCE(A, B)で「カラムAがNULLじゃなかったらその値を、NULLだったらBを返す」という感じ。例えば、以下のようなpurchaseテーブルがあるとする
| purchase_id  |  price  | coupon |
| ---- | ---- | ---- |
|  100001  | 2000 | NULL |

price - discountを求めたいとき、そのまま引き算するとNULLとなる。NULLの場合は0として計算したい場合、以下のようにする
```
SELECT purchase_id, price, coupon, price - COALESCE(coupon, 0) AS discount_price from PURCHASE: 
```

# 自己結合
二つの別々のテーブルを外部キーにより結合するだけでなく、同じテーブルを結合することができる。
```
SELECT * FROM employees FROM employee emp1 INNER JOIN employee emp2 ON emp1.position = emp2.position AND emp1.department <> emp2.department; 
```
このSQLにより、employeesテーブル内で、役職は一緒だけど、部署が異なる、という人を抽出できる。行同士を細かい条件で絞り込むなどすることが可能

# 長いSQLの読み方
内側のSQL（サブクエリなど）から読むことと、句の処理順に読むことを意識する。