OmniAICreator/Qiita-1.07M huggingface dataset:
[React Native]"Can't perform a React state update on an unmounted component..."について

#はじめに
こんにちは。今週も開発のノーハウについて投稿させていただきます。
最近はReact Nativeを用いてweb開発をやっています。React Nativeはとても使いやすいフレームワークですが、たまたま変な問題が出ます。今回私はwebをテストしているときに、コンソールから下記の警告情報が出てきました：
> Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.

この警告はよく見れるものであって、ググったら対策もいっぱいあるけれど、私の場合は若干違う気がします。

#結論は最初に出す
とにかく、コンポーネント関数はデフォルト（メイン）コンポーネントの中に入れないほうが良いかも。

#問題の再現
まずはソースコードを見てみましょう。

```react
function Main() {
  const [text, setText] = useState<string>("");

  const TestComp = () => {
    return(
      <View>
        <Image
          style={{width:150,height:150}}
          source={{uri: 'https://i.imgur.com/QHZtKLh.png'}}
          {/* 画像はimgurから適当にとったもの */}
        />
      </View>
    )
  }

  return(
    <View style={styles.container}>
      <TextInput
        style={styles.textInput}
        onChangeText={text => setText(text)}
        value={text}
      />
      <TestComp />
    </View>
  );
}

const styles = StyleSheet.create({
  // スタイルは適当に
});

export default Main;
```

ご覧の通り、ごく簡単のコードですが、`TextInput`に早い速度で入力したら、上記のような警告情報が出てくるのです。
> Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.

バージョン情報は以下です：

```json
"dependencies": {
  "expo": "~43.0.2",
  "expo-status-bar": "~1.1.0",
  "react": "17.0.1",
  "react-dom": "17.0.1",
  "react-native": "0.64.3",
  "react-native-paper": "^4.10.0",
  "react-native-responsive-screen": "^1.4.2",
  "react-native-vector-icons": "^9.0.0",
  "react-native-web": "0.17.1"
}
```

#問題の分析と改善策
いろいろテストした結果、問題点は`TestComp`が使っているpng画像です。

- `TestComp`の中に画像を使わないと、問題が発生しない
- svg画像を使っているとき、問題が発生しない
- `TestComp`は`Main`の外に入れるとき、問題が発生しない

ちなみに、Expo Snackにはこの警告が再現しません（Expoのバージョンは同じですが）。
さて、問題点を解明した以上、改善策も明らかに見られます。
`TestComp`を`Main`の外に入れれば、問題が回避できます。
ソースコードをこのようにしていきます。

```react
function TestComp() {
  //...
} 
function Main() {
  //...
}

const styles = StyleSheet.create({
  //...
});

export default Main;
```

このようにコードを再アレンジしたら、警告も出なくなります。
果たして、回避策ができているけれど、なぜこのような警告情報が出てくるかわからないままです。
詳しい方が教えていただければ助かります。

#最後に
この記事はあくまでも私個人（初心者として）の経験です。間違っているところ、もっと良い方法がございましたら、ぜひ教えてください。
【Xcode】LaunchScreenが表示されない時の対処法

XcodeでLaunchScreenが表示されない時の対処法。

思いの外躓いたので残しておきます。

## 試したこと

`Info.plist`のLaunchScreenのImage Nameに表示させたい画像名を設定。

`LaunchScreen.storyboard`の`Is Initial View Controller`と`Use as Launch Screen`にチェックを入れた。

しかしビルドしても一切LaunchScreenが表示されない。

1. Xcode再起動
2. Clean Build Folder
3. アプリをSimulatorから削除

これらの施策を行うも変わらず。

## 解決策

GeneralのApp Icons and Launch ImagesのLaunch Screen Fileを空欄にする。

![スクリーンショット 2021-11-21 16.19.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/522445/4a1a14cf-ea2b-b167-028f-7d665e5c1fa4.png)

ここに`Main`や`LaunchScreen`を設定していると、info.plistで設定したLaunchScreenの設定が反映されないためです。

## 参考

https://cpoint-lab.co.jp/article/201912/13035/

https://qiita.com/kboy/items/7de88a834bf01a6e858f


## Swiftのお役立ち情報

https://wagtechblog.com/programing/swift-iphone-application-school

https://wagtechblog.com/programing/swift-textbook.html
コマンド打ち込みで簡単に静的なページの作成と削除

# ほぼ静的なページを用意するのに必要なもの
#### 1.view
#### 2.router
#### 3.controller
#### 4.cssやjavascript

このように静的なページを用意するにも非常に作成しなければならないファイルが多い。もちろん手動で作成することも可能だがrailsにはこのようなファイルを一括で用意してくれるコマンドが用意されている。
以下にrailsで使用する事ができるコマンドラインツールが紹介されたページを貼っておく

https://railsguides.jp/command_line.html


静的なページを作成するために以下のコマンドを入力する

```
$ docker-compose run web rails generate controller ControllerName action1 action2
```

dockerを使っているので以上のようなコマンドになるがIDEや仮想コンテナを使用しないケースではrails以下を打ち込めば問題ないだろう

またrails generate　は rails gでも同様に動作する。

```
$ docker-compose run web rails g controller ControllerName action1 action2
```
#### 1.view
viewsの中に新たに スネークケースで書かれたfolderが用意されさらに、そこにaction1,action2で指定した名前のファイルが用意されている。

![スクリーンショット 2021-11-21 15.47.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1980538/fe322202-5b46-6902-e308-f8b35a617d0e.png)

#### 2.router
config fileの中のroutes.rbにこのような記述になっている

```ruby
Rails.application.routes.draw do
  get 'static_pages/home'
  get 'static_pages/help'
  
end
```

localhost：3000/static_pages/homeのようにrouterが設定されている。static_page/homeはパスとしてダサいのでおそらく変えないといけないが笑

#### 3.controller
controllersに指定した名前のコントローラ名でfileが作成されている

``` ruby
class StaticPagesController < ApplicationController
  def home
  end

  def help
  end
end
```

#### 4.cssやjs
assetsの中のstylesheetやjavascriptに以下のようなfileが用意されていた。

scssの例


```
// Place all the styles related to the StaticPages controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
```

### 作ったfileやfolderを一括で削除する。

これについても一つ一つ確認しながら消す必要はない

```
$ docker-compose run web rails destroy controller ControllerName action1 action2
```

または

```
$ docker-compose run web rails d controller ControllerName action1 action2
```
このコマンドを実行することでさっき作成したファイルやフォルダを一括で削除してくれる。いらなくなったケースやコントローラの名前を変えたくなったケースに利用したい。消すともちろんのことだが、書いたものはリセットされるので名前を変えたいケースは注意したい。

### 最後に

コマンド操作で簡単に静的ページの作成と削除ができることがわかった。destroyというと壊すというイメージがあるので少しコマンドをうつのこわい気がするが、railsではdestroyは削除するという意味で色々なところで使われるみたい

### 参考にした記事

https://railstutorial.jp/chapters/static_pages?version=6.0#cha-static_pages
Linux　プロセス・ジョブ

## プロセスとは
- コマンドを1つ実行するごとに作られる
- psコマンドで実行中のプロセスを確認することができる
- ps auxで他の実行中プロセスも確認できる
- まとめて実行されるプロセスの集まりがジョブ
- 下記の例だと、プロセスは3、ジョブは1

```
$ps aux|grep bash|less 
```

- ジョブには大きく3つあり、フォアグランド、バックグラウンド、停止がある
- ジョブの一覧をだす「jobs」コマンド
- フォアグラウンドにする「fg」
- バックグラウンドにする「bg」※コマンドの最後に＆をつけるとはじめからバックグラウンドで実行できる
- 停止にする「ctrl + Z」
- プロセスとジョブを停止にする「kill」
考古学のためのQGIS〜ポイントサンプリングツールを利用する〜

ポイントサンプリングツールは、背景地図の情報をベクタポイントに取り込むためのプラグインです。
下のような遺跡のポイントベクタに背景の標高ラスタから標高値を取得し結合します。

![01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60438/9abae4f9-6d1d-ba30-18f6-1dc7a843b9a5.png)

## ポイントサンプリングツールのインストール

1. プラグイン→プラグインの管理とインストール
2. Point sampling toolを選択し「インストール」

![02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60438/c74d6782-a24d-1623-c389-b669c869884a.png)

## ポイントサンプリングツールを使用する

- 「プラグイン」→「Analyses」→「Point Sampling Tool」 

![03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60438/d1d807a0-7611-60c4-3c88-3747037a71d6.png)

1. Layer containing sampling points:ポイントベクタを指定
2. Layer with fields/bands to get values from:サンプリングしたいレイヤ（この場合は標高ラスタ）
3. Out put point vector layer:新たなデータを付値したポイントベクタファイル名
4. 「OK」 

![04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60438/7549df6a-3a2a-6ba7-7870-93a08da8f80c.png)

「DEM_utm54」というフィールドをもつベクタデータが生成されます。
フィールド名はサンプリング先のファイル名になります。

![05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60438/4686f68f-f045-1b51-a324-5981eaf5823b.png)

## 元の遺跡ポイントベクタと結合する

新たに生成された標高値をもつポイントベクタは、元の遺跡ポイントベクタの情報をもっていません。そこで、2つのレイヤを結合します。

1. 新たに生成されたポイントベクタを右クリック
2. テーブル結合→「＋」をクリック

![07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60438/305b6a52-95cd-55cc-c357-813eba7177d0.png)
 
1. 結合するレイヤ：元の遺跡ポイントベクタ（結合される側）
2. 結合基準の属性：結合される側のキーとなるフィールド（標高値をもつポイントベクタ）
3. ターゲット属性：結合する側のキーとなるフィールド（元の遺跡ポイントベクタ）
4. 「OK」 

![08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60438/89a53d63-4528-68a7-2093-03eeed21f067.png)

元の遺跡ポイントベクタのフィールドが付値されます。

![09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60438/f78bb9f5-a414-ff32-c876-f6afdfe9351a.png)

## データをエクスポートして保存する

新たに付値されたデータは、QGIS上で仮想的に結合されているため、標高値をもつポイントベクタファイルには書き込まれていません。付値されたデータを保存するためにはエクスポートして書き出します。

![10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60438/419a83c6-f15a-75dc-8014-49c10b55d7e4.png)

## 立地を分析する

遺跡ポイントベクタに付値された標高や傾斜角度はcsvなどにエクスポートして遺跡立地の分析に使用できます。

北海道では縄文時代後期・晩期に低湿地の遺跡が多く見つかる印象がありますが、実際の遺跡標高からも、早期・前期・中期の「縄文前半期」では標高の高いところに遺跡が立地し、後期・晩期になると低標高の場所に遺跡が形成される様子がわかります。

![11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60438/3e0eba5f-fdde-8ed6-ed10-48c1a5d3b29e.png)

一方、遺跡の立地傾斜は前半期と後半期で差がないように見え、縄文前半期は高台の平坦地、後半期では低地よりの平坦地に居住領域を選定したという仮説が成立しそうです。
![12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60438/17b0b48e-4fd4-ee3b-316a-b31eec3312c1.png)
【Ruby のまずいコード】二乗和

# お題

数値配列の要素の 2 乗の和を返すメソッドを定義しよう。

# コード

```rb
def square_sum(values)
  values.inject(0){ |sum, value| sum + value ** 2 }
end
```

# 改善

もっと簡素に書けます。

総和を求めるのに専用のメソッド [Array#sum](https://docs.ruby-lang.org/ja/3.0.0/method/Array/i/sum.html) を使うとよいのですが，

```rb
def square_sum(values)
  values.map{ |value| value ** 2 }.sum
end
```

などとせずとも，`sum` のブロック付き用法で，

```rb
def square_sum(values)
  values.sum{ |value| value ** 2 }
end
```

と書けます。

番号指定ブロックパラメーターの導入された Ruby 2.7 以降なら

```rb
def square_sum(values)
  values.sum{ _1 ** 2 }
end
```

と書けます。

なお，Ruby 3.0 未満では数値の 2 乗を得るのに `** 2` とすると遅いので，

```rb
def square_sum(values)
  values.sum{ _1 * _1 }
end
```

と書くほうがよいでしょう。
Ruby 3.0 でも依然として後者のほうが少し速いので，速度がクリティカルな場合は後者の書き方にしましょう。
参考：[Ruby 3.0 で x ** 2 が速くなった件 - Qiita](https://qiita.com/scivola/items/b14b038eead48da409ed)
VPC作ってEC2立てたけどSSHでConnection refusedになる

# 経緯
仕事でAWSを使うことになり事前に自分でその作業の真似事をやろうと思い、VPC作成してEC2を立て、いざSSHでアクセス！と思った時に、それ以前の問題で引っかかってしまった。
1. デフォルトのVPCまで削除してしまっていたのでコンソールから新しくVPCを作成
2. そのVPCに対してEC2を作成
3. 接続ボタンからSSHのコマンドを確認してキーのパス以外をコピペで実行
# 引っかかった内容
`Connection refused`が表示される。
# 原因
VPCを作成した後にインターネットゲートウェイ作って適用してなかった。VPC作成するだけじゃインターネット上からアクセスできないんですよね。基本すぎて気づいた時ショックだった。（その後ちゃんとルートテーブルは設定した）
# さらに
おかしいなーと思いながらEC2を立てては終了しを繰り返し、SGの設定やらEIPやらを変えてみたり、一度停止してから再度起動と試している中で更新ボタンクリックしないとパブリックIP変わらないのに、それに気づかず同じIPに対して接続しようとしていたためにさらに余計な時間を使っていました。
# すべてを一から用意するときは
ちゃんと必要な設定を確認して手順に則って実施しましょうというお話でした。
【Ruby のまずいコード】除算の落とし穴

# お題

体重と身長だけから算出されるおおざっぱな肥満度の指数として BMI（Body Mass Index）というものがありますね。
体重を $w$ kg，身長を $h$ m とすると，

```math
\textrm{BMI} = \frac{ w }{ h^2 }
```

と表されます。18.5 以上 25 未満が標準だそうです。

では，体重を kg で，身長を cm で与えると BMI 値を返すメソッドを定義してみましょう。上の式では身長の単位は m ですが，cm のほうが扱いやすいので，cm にしましょう。すると 10000 を掛けることになりますね。

# コード

ときどき，こういうコード（に類するもの）を目にします。

```rb
def bmi(weight, height)
  10_000 * weight / height ** 2
end
```

# 問題点

このメソッドは，正確な値を返さないことがあります。
Ruby の除算演算子 `/` は，非除数（割られる数），除数（割る数）がいずれも Integer オブジェクトのとき，普通の商ではなく整商（整数の商）を返す仕様なのです。
つまり，

```rb
p 5 / 4.0 # => 1.25
p 5 / 4   # => 1
```

となります。

したがって，体重 55 kg，身長 172 cm の人は BMI 値が本来は

```
18.591130340724717
```

と標準の範囲に入るはずですが，引数を整数で与えると

```rb
p bmi(55, 172) # => 18
```

となって，「低体重」とされてしまいます。
返り値が正しい値と少ししか違わないので，気づきにくいバグと言えます。

# 改善

`/` のような，数値のクラスによって意味がガラッと変わる演算子は要注意です。

今の場合，[Numeric#fdiv](https://docs.ruby-lang.org/ja/3.0.0/method/Numeric/i/fdiv.html) を用いて

```rb
def bmi(weight, height)
  10_000 * weight.fdiv(height ** 2)
end
```

とするとか，Integer リテラルの `10_000` を Float リテラルの `10_000.0` に代えて

```rb
def bmi(weight, height)
  10_000.0 * weight / height ** 2
end
```

とすれば直ります。
Linux　パーミッション

ファイルにはオーナーとグループが設定される
idコマンドで自分のユーザーを確認できる

## パーミッション記号
r 読み取り(read)
w 書き込み(write)
x 実行
-権限なし

2進数で表す
rwx -> 7
r-x -> 5
r-- -> 4

## 役割
所有者
グループ
その他ユーザー

## 読み方（一番重要！！！！）
-rwxr--r--
左から順に
①ファイルの種類
-は通常のファイル、dはディレクトリ
②③④所有者
⑤⑥⑦グループ
⑧⑨⑩その他のユーザ

## パーミッションの変更
chmodコマンドでできる

## スーパーユーザ
スーパーユーザは管理者権限をもつユーザーのこと
コマンドの前に「sudo」をつける
Sourcetreeで認証情報を変更したい

Sourcetreeで初回のユーザー名・パスワードを間違えると、間違えた状態で保存されてしまう・・
プッシュしても当然エラーで失敗するのですが、再度聞いてくれません。

変更方法を探すのに少し時間が取られたので、対処方法をメモします！

## 変更方法
こちらMacでの変更方法になります！
以下のフォルダを開き、hostusernamemapというファイルを削除します。以上です！
`~/Library/Application Support/Sourcetree`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/558865/756b07f3-004d-0a39-6b36-31ad454a12e4.png)

再度、Sourcetreeでプッシュするとユーザー名・パスワードを聞いてくれます。
※Windowsの方は参考をご覧ください。

## 参考
https://sunny10side.com/fix-sourcetree-username-and-password-mishaps/

## 終わりに
最初は認証情報ぐらい簡単に変更できると思い、Sourcetreeの設定を見ましたが、ちっとも見つかりませんでした^^;
簡単に変更できましたが、手順自体は知らないと迷いますね・・
Nordic NRF52シリーズ 開発環境の構築

---
**はじめに**
NORDIC nRF52 シリーズのファームウェア開発環境構築について記す。

---
**2024年12月12日追記**
現時点の [Embedded Studio] の最新 Version は、
・8.20 [2024-11-22]  Embedded Studio for Arm and RISC-V

nRF5 SDK の最新 version は、
・nrf5_sdk_17.1.0_ddde560 で 2021年から同じ

この組み合わせで、使用すると Build時に Sizeエラーが発生する

対処方法は、各プロジェクトで定義されている [flash_placement.xml] を以下の様に編集する
【編集前】
 < ProgramSection alignment="4" load="Yes" name=".text" size="0x4" />
 < ProgramSection alignment="4" load="Yes" name=".rodata" size="0x4" />

【編集後】
 < ProgramSection alignment="4" load="Yes" name=".text" />
 < ProgramSection alignment="4" load="Yes" name=".rodata" />
 
 **以上**
 
---
**統合開発環境(IDE)のインストール**
私の手元には、NORDIC Semiconductor社製 nRF5284-DK のボードがあり CPUには、
[ARM®の32ビットCortex™-M4 CPU](https://www.nordicsemi.com/Products/nRF52840?lang=ja-JP)が搭載されている。

それ故に統合開発環境として[Embedded Studio](https://www.segger.com/downloads/embedded-studio) を使用しました。
[このページ(https://www.segger.com/downloads/embedded-studio)](https://www.segger.com/downloads/embedded-studio)から
ダウンロードを行い デフォルトでインストールするだけでことで簡単に
Windows10上で 環境構築が出来ました。

**サンプルコードの入手**
こちらは、下記に SDK が置かれておりダウンロードが出来ます。
[https://www.nordicsemi.com/Products/Development-software/nRF5-SDK/Download](https://www.nordicsemi.com/Products/Development-software/nRF5-SDK/Download)
※必要な SDKは、RF5_SDK_xx.x.x_xxxxxxx.zip

この Zip を解凍すると 様々なサンプルソースコードが置かれています。
実際に必要とするサンプルコードが、どこにあるのかを探すのは非常に面倒く
英文ですが、[https://infocenter.nordicsemi.com/index.jsp](https://infocenter.nordicsemi.com/index.jsp)にある
解説からひも解いて下さい。

ただし、慣れてくれば感覚で当たりをつけられる様になります。
※当たれば、( ^)o(^ ) / 外れれば がっかり！
例えば、UARTのサンプルコードは下記に

~~~
C:\nRF5_SDK_17.1.0_ddde560\examples\peripheral\uart
~~~

timer に関するサンプルコードは

~~~
C:\nRF5_SDK_17.1.0_ddde560\examples\peripheral\timer
~~~

という具合です。

ちなみに いつもの Lチカ は、

~~~
C:\nRF5_SDK_17.1.0_ddde560\examples\peripheral\blinky
~~~

nRF5x の特徴である BLE 関係は下記に分散されて置かれています。
BLE 通信の簡単な実証実験が出来ます。

~~~
C:\nRF5_SDK_17.1.0_ddde560\examples\ble_central
C:\nRF5_SDK_17.1.0_ddde560\examples\ble_central_and_peripheral
C:\nRF5_SDK_17.1.0_ddde560\examples\ble_peripheral
~~~


**IDEの起動**
先にインストールをした Embedded Studio の起動は、nRF52840-DK の場合
uart_pca10056.emProject を起動する。(下記参照)

~~~
C:\nRF5_SDK_17.1.0_ddde560\examples\peripheral\uart\pca10056\blank\ses\uart_pca10056.emProject
~~~

![2021-11-21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2056854/4e20c299-9b10-0bd9-fcad-2392f150c3b8.png)

![2021-11-21 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2056854/82b1d18d-51d7-0834-f0ae-9d230c0bfbf0.png)


起動場所は、nRF5シリーズによって異なっており下記の通りです。
nRF52840 DK	PCA10056
nRF52840 Dongle	PCA10059
nRF52833 DK	PCA10100
nRF52 DK	PCA10040
nRF51 DK	PCA10028
nRF51 Dongle	PCA10031
参考：https://infocenter.nordicsemi.com/index.jsp?topic=%2Fug_gsg_ses%2FUG%2Fgsg%2Fchips_and_sds.html

---
☆2021年11月21日(日) 午後03時30分 初版(Ver1.00) 作成
☆2024年12月12日(木) 午前10時00分 Ver1.01 作成 - 追記の項目のみ

---
dockerでのphp開発環境を構築する

自宅でもphpについて学びたいと思い、ローカルで開発環境を構築しようと思いました。
MAMPでもいいと思いましたが、せっかくなのでdockerで作ってみたいと思います。
開発中に色々詰まってしまったので備忘録がてらの記述です。

<h1>手順</h1>
1:dockerによる開発環境構築
2:ファイルディレクトリの準備
3:各ファイルの作成
4:コンテナ起動
5:サイトにアクセス

<h1>1:dockerによる開発環境構築</h1>
Dockerを使用するには「docker for mac」が必要なんですが、導入方法などはすでにQiitaなどで分かりやすく書かれてる方々がたくさんいますので、今回は割愛させていただきます。

今回はDockerで以下のコンテナを使います。
・nginx
・PHP
・MySQL
・PHPMyAdmin

使用するコンテナが複数あるため今回は「docker compose」を使用していきたいと思います。

<h2>2:ファイルディレクトリの準備</h2>
まずはDockerを起動するための場所を作成します。
ファイル構造は次のようにします。

```
php-test
├── docker-compose.yml
├── mysql
│   └── data
├── nginx
│   └── nginx.conf
├── php
│   ├── Dockerfile
│   └── php.ini
└── www
   └── html
       └── index.php
```

<h1>3:各ファイルの作成</h1>
下記のファイルを編集して中身を作成していきます。

```php-test/php/Dockerfile
FROM php:7.2-fpm
COPY php.ini /usr/local/etc/php/
RUN docker-php-ext-install pdo_mysql
```

```php-test/nginx/nginx.conf
server {
  listen 80;
  server_name _;

  root  /var/www/html;
  index index.php index.html;

  access_log /var/log/nginx/access.log;
  error_log  /var/log/nginx/error.log;

  location / {
      try_files $uri $uri/ /index.php$is_args$args;
  }

  location ~ \.php$ {
      fastcgi_pass php:9000;
      fastcgi_index index.php;    
      fastcgi_param SCRIPT_FILENAME  $document_root$fastcgi_script_name;
      include       fastcgi_params;
  }
}
```

```php-test/docker-compose.yml
version: '3'
services:
  nginx:
    image: nginx:latest
    ports:
      - 8080:80
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
      - ./www/html:/var/www/html
    depends_on:
      - php

  php:
    build: ./php
    volumes:
      - ./www/html:/var/www/html
    depends_on:
      - db

  db:
    image: mysql:5.7
    ports:
      - 13306:3306
    volumes:
      - ./mysql/data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: secret

  phpmyadmin:
    image: phpmyadmin/phpmyadmin:latest
    ports:
      - 8888:80
    depends_on:
      - db
```

```php-test/www/html/index.php
<?php
phpinfo();
?>
```

<h1>4:コンテナ起動</h1>
ここまでの作業が終わりましたら、ターミナルから```docker-compose.yml```ファいうがあるディレクトリ移動して```docker-compose up -d```コマンドでコンテナ起動できます。

コンテナを停止したい場合は```docker-compose stop```コマンドで停止できます。

<h1>5:サイトにアクセス</h1>
最後にサイトにアクセスしてみてちゃんと表示されてたら起動完了です。
Webサイト：http://localhost:8080/

お疲れ様でした。
【Python】Pandas で株価データを取得する（その３）

## はじめに

[前回](https://qiita.com/tnozoo/items/e69bf998ddc3ab836d2f)書いた株価データを取得するコードは問題があった

- 株価データ取得期間が固定
- 取得したい銘柄のコードをPythonコードの中に直に書いている

今回は，[前回](https://qiita.com/tnozoo/items/e69bf998ddc3ab836d2f)のコードを次のように書きかえてみる

- １年前から現在までの期間で株価データを取得するコード

## dateutil.relativedelta で，N日前の日付を求める

N日前の日付を求めるには，dateutil.relativedelta を使うのが便利そうだ
dateutil.relativedelta の動作を確認してみる

本日2021/11/21の１年前（12か月前）を求めるのは次のようだ

```python
>>> st = datetime.datetime.now() - relativedelta(months = 12);
>>> st.year, st.month, st.day
(2020, 11, 21)
```

現在日時を返す getToday 関数と，
指定の日時からのNか月前を返す getMonthAgo 関数を用意する

```python
def getToday():
    return datetime.datetime.now();

def getMonthAgo(day, month):
    return (day - relativedelta(months = month));
```

main 関数内で，getToday 関数，getMonthAgo 関数を使い，
データ取得の期間を設定している
print 関数で，銘柄のコードとデータ取得期間の開始と終了を出力している


```python
def main(codeList, companyList):
    ed = getToday();
    st = getMonthAgo(ed, 12);
    for code, company in zip(codeList, companyList):
        df = pdr.DataReader(code + '.T', 'yahoo', st, ed);
        print('%s [%s]-[%s]' % (company, df.head(1).index[-1], df.tail(1).index[-1]));
```

まとめると，次のようなコードになる


```python:step03.py
import pandas_datareader.data as pdr
import datetime;
from dateutil.relativedelta import relativedelta;

def getToday():
    return datetime.datetime.now();

def getMonthAgo(day, month):
    return (day - relativedelta(months = month));

def main(codeList, companyList):
    ed = getToday();
    st = getMonthAgo(ed, 12);
    for code, company in zip(codeList, companyList):
        df = pdr.DataReader(code + '.T', 'yahoo', st, ed);
        print('%s [%s]-[%s]' % (company, df.head(1).index[-1], df.tail(1).index[-1]));

if __name__ == '__main__':
    codeList = ['7203', '7201', '7267'];
    companyList = ['トヨタ', '日産', 'ホンダ'];
    main(codeList, companyList);
```

実行した結果は次のようだ
2020年11月20日から2020年11月23日はマーケットはお休みなので，
2020年11月24日からのデータが取得できたことがわかる

```
❯ python step03.py
トヨタ [2020-11-24 00:00:00]-[2021-11-19 00:00:00]
日産 [2020-11-24 00:00:00]-[2021-11-19 00:00:00]
ホンダ [2020-11-24 00:00:00]-[2021-11-19 00:00:00]
```

## おわりに

１年前から現在までの期間で株価データを取得することができた

次回は，次のようなコードを書いてみよう

- 取得する銘柄コードをExcelファイルで用意し，Excelファイルを読み込み株価データが取得できるコード
一桁の数字の前に0をつける方法(Laravel)

##一桁の数字の前に0をつける方法
1~20の数字があったとする。その中で1~9までの数字には前に0をつけたい。
↓みたいな感じ
01,02,03,04,....

自分で考えた実装はこちら

```php
$numberList = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];

foreach ($numberList as $number) {

     //10以下の数字には0を連結させる
     if ($number < 10) {
         $hoge = '0' . $number;
     } else {
         $hoge= $number;
     }
 };
//01,02,03,04...09
```

これでも実装はできるが下記の実装の方がスマートなんで紹介しとく。

```php
$numberList = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];

foreach ($numberList as $number) {
    //文字の長さが一桁以上ならtrue
    if (strlen($number) > 1) {
        //$numberを文字列に変換
        $hoge =  strval($number);
    }else{
        //$numberを文字列に変換して0を連結
        $hoge = '0' . strval($number)
    }
 };

//01,02,03,04...09になる
```

**strlen()**は文字の長さを取得する。
**strval()**は変数を文字列に変換する。

ついでに↓は変数の中身の型を調べることができる。

```php
gettype($hoge);
```
Dynamics365/PowerAppsモデル駆動アプリの汎用的なラベル表示ツール

固定の警告メッセージ、補足情報など長い文字列を表示する場合が偶にあるかと思います。
キャンパスアプリやPCFも汎用的なメッセージ表示のコンポーネント作成できますが、表示用の項目の作成が必要のため、少し不便があるかと思います。
過去作成した汎用的なメッセージ表示のwebresourceを共有させていただきます。
以下は簡易的な説明となります。
①webresource配置
![MicrosoftTeams-image (10).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2307534/189d4e2c-db17-004f-9571-d19261105e7c.png)
②表示メッセージを入力、改行あり可能
![MicrosoftTeams-image (11).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2307534/226edac1-47a8-a1f6-face-e10d5ce20c1e.png)
③境界線をOFFにする
![MicrosoftTeams-image (12).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2307534/46264977-098f-00dd-c917-e063ca692a97.png)
④画面表示イメージ
![MicrosoftTeams-image (13).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2307534/0ffe5f1d-dd16-6ceb-b731-a38d940cef14.png)
表示色やフォントサイズの引数も追加構成可能です。
動的にメッセージ表示したい場合、JSで制御可能

サンプル資材はこちらになります。
https://github.com/skyfuturejp/Dynamics365Tool/blob/main/commonmessagelabel/new_commonmessagelabel.html

MSの参考資料もあります、ご参考までに
https://docs.microsoft.com/ja-jp/dynamics365/customerengagement/on-premises/developer/sample-pass-multiple-values-web-resource-through-data-parameter?view=op-9-1
HHKBおすすめキー配列設定（エンジニアver.）

# はじめに

以下の条件に当てはまる人の参考になればと思います

* 普段からコーディングをしている
* HHKB（Happy Hacking Keyboard）を使用している
* 英語配列である

# 結論

個人的に最適なキー配列です
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1719666/1418837a-2e32-3f30-27e1-8dd8b6ac4534.png)

Fnキーを押したときものだけ変更しています

方向キーを中央に寄せるのは特におすすめです！
それ以外は自分がよく使う言語の特性に合わせて、変更していけばいいと思います

# キー配列の変更方法

以下のサイトからツールをダウンロードして、変更できます（有線接続が必要）

[HHKBキー配列切替ツール](https://www.pfu.fujitsu.com/hhkeyboard/tooldownload.html)

# さいごに

~~投稿者のHHKB歴はまだ10ヵ月で、「これが最適！」と自信を持って言えません
なので、より良い配列が見つかれば更新していこうと思います~~

HHKB Studioに乗り換えたので、最終的なキー配列に更新しました
AtCoder Beginner Contest 228

##[A - On and Off](https://atcoder.jp/contests/abc228/tasks/abc228_a)

O(1)
S<Tの時
T<Sの時
のバターン分けをして行きます。

```C++:C++
#include <bits/stdc++.h>
 
#define rep(i,n) for(int i=0; i<(n); ++i)
#define fixed_setprecision(n) fixed << setprecision((n))
#define execution_time(ti) printf("Execution Time: %.4lf sec\n", 1.0 * (clock() - ti) / CLOCKS_PER_SEC);
#define pai 3.1415926535897932384
#define NUM_MAX 2e18
#define NUM_MIN -1e9
 
using namespace std;
using ll = long long;
using P = pair<int,int>;
template<class T> inline bool chmax(T& a, T b){ if(a<b){ a=b; return 1; } return 0; }
template<class T> inline bool chmin(T& a, T b){ if(a>b){ a=b; return 1; } return 0; }
 
int main() {
    int S, T, X;
    cin >> S >> T >> X;
 
    if(S<T){
        if(S <= X && X < T) cout << "Yes" << endl;
        else cout << "No" << endl;
    }else{
        if(S <= X || X < T) cout << "Yes" << endl;
        else cout << "No" << endl;        
    }
 
    return 0;
}
```

##[B - Takahashi's Secret](https://atcoder.jp/contests/abc228/tasks/abc228_b)

O(N)

```C++:C++
#include <bits/stdc++.h>
 
#define rep(i,n) for(int i=0; i<(n); ++i)
#define fixed_setprecision(n) fixed << setprecision((n))
#define execution_time(ti) printf("Execution Time: %.4lf sec\n", 1.0 * (clock() - ti) / CLOCKS_PER_SEC);
#define pai 3.1415926535897932384
#define NUM_MAX 2e18
#define NUM_MIN -1e9
 
using namespace std;
using ll = long long;
using P = pair<int,int>;
template<class T> inline bool chmax(T& a, T b){ if(a<b){ a=b; return 1; } return 0; }
template<class T> inline bool chmin(T& a, T b){ if(a>b){ a=b; return 1; } return 0; }
 
int main() {
    int N, X;
    cin >> N >> X;
    X--;
 
    vector<int> A(N);
    vector<int> T(N);
    rep(i, N){
        cin >> A[i];
        A[i] = A[i] - 1;
        T[i] = 0;
    }
 
    int res=0;
    while(T[X] == 0){
        T[X] = 1;
        X = A[X];
        res++;
    }
    cout << res << endl;
 
    return 0;
}
```

##[C - Final Day](https://atcoder.jp/contests/abc228/tasks/abc228_c)

O(NlogN)
多分、正攻法の解き方ではない気がする。

```C++:C++
#include <bits/stdc++.h>
 
#define rep(i,n) for(int i=0; i<(n); ++i)
#define fixed_setprecision(n) fixed << setprecision((n))
#define execution_time(ti) printf("Execution Time: %.4lf sec\n", 1.0 * (clock() - ti) / CLOCKS_PER_SEC);
#define pai 3.1415926535897932384
#define NUM_MAX 2e18
#define NUM_MIN -1e9
 
using namespace std;
using ll = long long;
using P = pair<int,int>;
template<class T> inline bool chmax(T& a, T b){ if(a<b){ a=b; return 1; } return 0; }
template<class T> inline bool chmin(T& a, T b){ if(a>b){ a=b; return 1; } return 0; }
 
int main() {
    int N, K;
    cin >> N >> K;
 
    vector<int> P(N);
    rep(i, N){
        rep(j, 3){
            int p;
            cin >> p;
            P[i] += p;
        }
    }
 
    vector<int> T = P;
    sort(T.begin(), T.end());
 
    rep(i, N){
        auto iter = upper_bound(T.begin(), T.end(), P[i]+300);
        int res = T.size() - (iter - T.begin()) + 1;
        if(res <= K) cout << "Yes" << endl;
        else cout << "No" << endl;
    }
 
    return 0;
}
```
Node.jsのバージョン管理アプリ「NVM for Windows」は管理者権限で実行する

##はじめに
WebアプリやWebサイトを構築するような複数の案件に関わっていると、Node.jsをプロジェクトごとに切り替える必要性が出てくるため、「NVM for Windows」を導入しています。
使い初めたころ、基本にも関わらず私が躓いてしまった「管理者権限での実行」についての説明をします。

##動作環境
Windows 10

##まずは「NVM for Windows」をインストール
[https://github.com/coreybutler/nvm-windows/releases/](https://github.com/coreybutler/nvm-windows/releases/)からNVM for Windowsのインストーラをダウンロード。
![node-03.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2217806/ba55ef14-74e2-27a3-690d-9fd02d0deddd.jpeg)
nvm-setup.zipを選択します。
![node-04.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2217806/50d4966a-3ecc-94c9-cfff-262180ef8c06.jpeg)
nvm-setup.exeを立ち上げたらインストーラに沿って進めます。
![node-05.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2217806/a9289416-6a2f-3628-c8d6-33dbfc69682b.jpeg)
既にNode.jsがインストールされている場合、ダイアログでそのバージョンをNVMでコントロールするかを聞かれるので「はい」を選択してください。
![node-05_01.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2217806/dd5a4931-ec99-722b-5d17-aa2026f209b2.jpeg)
インストールが完了したら、いよいよバージョン切り替えをしていきます。


##NVMのコマンド実行は管理者権限で！
この時点でコマンドラインツール（cmdやPowerShell）を通常の方法で立ち上げていたら一旦終了させてください。
NVMのコマンドによるバージョン管理は**管理者権限**で行う必要があるためです。

Windowsのアプリ検索でcmdを検索したら、アイコンを右クリックし「管理者として実行」を選択してください。
![node-06.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2217806/67479968-e3a1-7587-d9b0-3ed14b366c36.jpeg)
それでは現在インストールされているNode.jsのバージョンを確認します。

```node:Node.jsのバージョンリスト確認
nvm ls
```
```node:結果
* 16.13.0 (Currently using 64-bit executable)
```

今は「v16.13.0」の一種類しかインストールしていないので、上記のように表示されます。
他のバージョンを指定して追加インストールしたい場合

```node:Node.jsのバージョン指定インストール
nvm install x.xx.x（バージョン名）
```

下図のように「nvm install 8.15.0」と指定すれば、v8.15.0のNode.jsとそれに対応したバージョンのnpmがインストールされます。
![node-07.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2217806/3253465e-07fc-e6ce-fab7-08a0bf1c4a57.jpeg)
この時点ではまだ新しく追加したバージョンは使用できません。
もう一度、バージョンリストを確認するコマンド「nvm ls」を実行。
![node-08.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2217806/cdd86f0e-462e-d6fd-972a-2e967f790723.jpeg)
先ほど一種類しかなかったバージョンリストが増えていることが確認できます。
それでは、使用したいNode.jsのバージョンを指定します。

```node:node.jsのバージョン切り替え
nvm use v8.15.0
```

この「nvm use」コマンドを管理者権限の状態で実行しないと、Node.jsやnpmのバージョン切り替えがうまくいかずにエラーを食らい続けることになってしまいます。

Node.jsのバージョンを確認してみましょう。

```node:node.jsのバージョン確認
node -v
```

```node:確認結果
v8.15.0
```
「v8.15.0」と返されたら成功です！
ついでにnpmのバージョンも確認しましょう。

```node:npmのバージョン確認
npm -v
```
```node:確認結果
6.4.1
```

npmも無事に切り替わっています。
これで完了です。
SQL 関数と演算子について

#記事内容

- **算術演算子の種類**
- **nullを含む演算**
- **絶対値の取得**
- **四捨五入**
- **文字列の演算**
- **日付と時刻の演算**

#関数と演算子
**算術演算子の種類**

| 算術演算子 | 意味 |算術演算子 | 意味 |
|:-:|:-:|:-:|:-:|
| +  |  足し算 | *  |  掛け算 |
| -  |  引き算 |/  |  割り算 |
| %  |  余り |

```sql
-- 足し算
select 10 + 20;
-- 出力結果
30 

-- 割り算の余り
select 10 % 3;
--　出力結果
1
```

##nullを含む演算
- `null`とは**データが存在しないことを表す値**

:::note alert
nullを含んだ演算結果はすべて null になるので注意
:::

```sql
-- nullを足し算した場合
select 10 + null;
-- 出力結果
null
```

##abs関数
* **絶対値の取得ができる**
  * **数値の符号(+, -)を考えない、０からの距離の大きさを表す数値**
     * `10`の絶対値は`10`
     * `-10`の絶対値は`10`
     * `0`の絶対値は`0`

```sql
-- -10の絶対値
select abs(-10);
-- 出力結果
10
```

##round関数
**四捨五入ができる**

```sql
-- 構文
round(対象の数値, 丸めの桁数)

-- 小数第1位で四捨五入
select round(10.55, 0);
-- 出力結果
11

-- 少数第2位で四捨五入
select round(10.55, 1);
-- 出力結果
10.6

-- avg等の関数と組み合わせも可能
round(avg(price), 0);
```

##concat関数
**文字列の連結**

```sql
-- 構文
concat(文字列1,文字列2,文字列3 ...);

-- 氏名の名前と名字を繋げて出力する場合 (' ' = 半角スペース)
select concat('foo', ' ', 'bar', 'さん');
-- 出力結果
foo barさん
```

##日付と時刻の演算

| コマンド | 意味 |
|:-:|:-:|
| current_date  |   現在の日時|
|  current_timestap |  現在の時刻 |
|  d + interval n day| n日後の日付  |
|  d -  interval n day | n日前の日付  |
|  + interval 'x hour'| x時間後の時刻  |
|  - interval 'x hour' | x時間前の時刻  |
|  ectract | 日付や時刻の特定部分(年や月)までを取り出す |

```sql
-- orders = 注文履歴テーブル
-- order_time = 注文日
-- year_month = 年月
-- year = 年

-- 3日後の日付を取得
select current_date() + interval 3 day;

-- 6時間後の時刻を取得
select current_timestamp() + interval 6 hour;

-- 注文日が2021年1月のレコードのみ取得
select * from orders where extract(year_month from order_time) = 202101;

-- 注文日が2021年のレコードのみ取得
select * from orders where extract(year from order_time) = 2021;
``` 

##参考記事

https://www.udemy.com/course/standard-sql-for-beginners
【Next.js】動的ルーティングについて

#はじめに
こんにちは！
今回は【Nuxt.js】動的ルーティングについてアウトプットしていきます！

パラメータを使って動的なルーティングを定義するにはコンポーネントのファイル名にアンダースコアのプレフィックスをつけます。

##パラメータとは
URLパラメータとは、サーバー側で動くプログラムに情報を送るために、URLの末尾に付け加える変数（文字列）のことを言います。

##動的なルーティングとは
動的なルーティング（ダイナミックルーティング）とは、ルータなどが経路情報を互いに交換しあい、自動的に生成・更新し続ける経路表（ルーティングテーブル）に基づいて経路選択を行なうこと。



#解説
```:URLの例

localhost:3000/users/1
localhost:3000/users/2
localhost:3000/users/3

```
```:ファイル名の例

_id.vue
⏫アンダースコアのプレフィックスを付ける

```

URLパラメーターとして渡された値（1,2,3）といった`id`を取り出すには、作成したファイルの中のテンプレート部分に、

```:_id.vue
    <p>id: {{ this.$route.params.id }}</p>
```
といったような`this.$route.params.id`というように記述します。

#まとめ
・動的なルーティングを行うには、ファイル名にアンダースコアのプレフィックスしたものを作成し、テンプレートに`this.$route.params.id`たるものを記述しなければならない。
・またパラメータ部分は数値でなくても構わない。
予期せぬエラー防止のためにパラメータ部分を指定する事も可。使用されるのは`validate`メソッド。

```:id_vue
<script>
validate({ params }){

}
</script>
```
この{}内に指定内容を記述してあげる。

#最後に
今回は動的ルーティングについてアウトプットしました。
今回の記事を読んで質問、間違い等あればコメント等頂けると幸いです。

今後ともQiitaにてアウトプットしていきます！

最後までご愛読ありがとうございました！
rubyでbin2hexとhex2bin

phpの`hex2bin`と`bin2hex`に該当する処理をrubyでぱっと実現。

```ruby
def hex2bin(value)
  [value].pack "H*"
end
```

```ruby
def bin2hex(value)
  value.unpack "H*"
end
```

## 参考

- https://stackoverflow.com/questions/5244414/shortest-hex2bin-in-ruby
OS~Linux~

OS
Linux
→
・オープンソースであり、無料
・自由に中身を変更できる
・メインとなる操作はCUIで行われる。
・様々なディストリビューション（配布形態）が存在する。
ディストリビューション：
〇　Debian(デビアン)系
→
・Debian、Ubuntuなど。
・高性能かつ利用が難しくない。
・Raspberry Pi(ラズベリーパイ)などの超小型機器向けも存在する。
〇　Slackware(スラックウェア)系
→
・Slackware、OpenSUSEなど。
・管理が複雑だが、安定性やセキュリティに定評がある。
〇　Red Hat(レッドハット)系
→
・有償のものもある。
・RHEL、Fedora(フェドラ)、CentOS(セントス)など。
【命名】"ユーザー：user"という名前を安易に使わない

# 「ユーザー：user」にはいろんな種類がある

　よくテーブル名やモデル名に「ユーザー」とつけているのを見かけることがあります。
「ユーザー」はあまりにも曖昧で幅広い言葉なので、命名する際にはできるだけ避けたほうがいいと思います。

# ユーザーの立場を表す名前をつける

　ユーザーと一言で言っても、社員、顧客、管理者、投稿者などいろいろな立場があります。
単なる「ユーザー」とつけるのではなく、立場を表す名前をつけている方がシステムが大きくなったとき、
命名の曖昧さからくる混乱を回避することができます。

社員：crew、顧客：client、管理者：administrator、投稿者：contributorなどをつけておくほうがいいと思います。
WindowsにNode.jsをインストールする

##はじめに
Windows環境にNode.jsをインストールする手順です。
仕事でVueCLIを使用することになり、新しいデバイスにNode.jsやnpmを入れる必要があったため備忘録として記録。

関連：[Node.jsのバージョン管理アプリ「NVM for Windows」は管理者権限で実行する](https://qiita.com/iraimirai/items/e6b3133857f0af3a3f97)

##動作環境
Windows 10

##Node.jsをインストールする

まずは、Node.js 公式サイト（[https://nodejs.org/ja/](https://nodejs.org/ja/)）からインストーラをダウンロード。
左側の推奨版を選択します。
![node-01.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2217806/97cee90a-7c2b-defb-5750-fbedf5af9768.jpeg)
インストーラに沿って進めていきます。
途中で表示される「Automattically install the necessary tools. ...うんたらかんたら」のチェックボックスは、VueCLIを使用したいだけの私には必要なさそうだったので選択しませんでした。
![node-02.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2217806/77a97e33-cd25-1ef9-2c16-f1545797e4e5.jpeg)

インストールが完了したらcmd（コマンドプロント）もしくはPowerShellを立ち上げて、実際に確認。

```node:node.jsのバージョン確認
node -v
```

```node:確認結果
v16.13.0
```

Node.jsのバージョンに合わせたnpmも一緒にインストールしてくれています。
こちらもバージョン確認。

```node:npmのバージョン確認
npm -v
```

```node:確認結果
8.1.0
```
これで導入完了です。

よろしければ、関連記事もどうぞ
→[Node.jsのバージョン管理アプリ「NVM for Windows」は管理者権限で実行する](https://qiita.com/iraimirai/items/e6b3133857f0af3a3f97)
Splunk: 重複削除コマンドdedupが残すイベントの基準について

調べる機会があったのでメモ。

###### 実施環境： Splunk Free 8.2.2

###### 事象

Splunk の重複削除コマンドといえば、dedup コマンドをよく使用します。
では、 dedup コマンドは重複したイベントについて、どのイベントを残し、どのイベントを削除するのでしょうか。

以下の例を見ると、 dedup コマンドは「最新のログを残す」ように見えます。

```Splunk:Splunk
index="_internal" sourcetype="splunkd" source="*/splunkd.log" log_level="INFO"
event_message = "*tracker.log*"
| table _time, event_message
```

![スクリーンショット 2021-11-14 21.30.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465018/8fff378e-c055-f8ec-408c-a2563e9004f1.png "重複イベント")

```Splunk:Splunk
index="_internal" sourcetype="splunkd" source="*/splunkd.log" log_level="INFO"
event_message = "*tracker.log*"
| dedup event_message
| table _time, event_message
```

![スクリーンショット 2021-11-14 21.30.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465018/47569d7d-be0c-577d-b4f9-c5b30e014e43.png "重複削除後")

ただ、ここに sort コマンドを入れると、少々事情が変わってきます。

```Splunk:Splunk
index="_internal" sourcetype="splunkd" source="*/splunkd.log" log_level="INFO"
event_message = "*tracker.log*"
| sort _time
| table _time, event_message
```

![スクリーンショット 2021-11-14 21.32.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465018/fc6e1931-5e3a-17e1-559e-a7b3e78c5ad4.png "重複イベント-並べ替えあり")

```Splunk:Splunk
index="_internal" sourcetype="splunkd" source="*/splunkd.log" log_level="INFO"
event_message = "*tracker.log*"
| sort _time
| dedup event_message
| table _time, event_message
```

![スクリーンショット 2021-11-14 21.33.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465018/87ee81d3-40d3-56ee-ffbf-40fc3042db7f.png "重複削除後-並べ替えあり")

なんと、動きが「最古のログを残す」に変わってしまいました。

###### 原因

なぜこのようなことが起こるのか。
 dedup コマンドが残すイベントは「最新のログ」ではなく、「一番先頭のログ」だからです。
例えば、以下のような並べ替えをすると、中途半端な時間のログを dedup の結果として抜き出すことも可能です。

```Splunk:Splunk
index="_internal" sourcetype="splunkd" source="*/splunkd.log" log_level="INFO"
event_message = "*tracker.log*"
| streamstats count AS CNT
| eval SORT = ( CNT + 5 ) % 10
| sort SORT
| table SORT, _time, event_message
```

![スクリーンショット 2021-11-14 21.36.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465018/dc249823-e193-f337-2e82-f00eed6ec743.png "重複イベント-並べ替えあり2")

```Splunk:Splunk
index="_internal" sourcetype="splunkd" source="*/splunkd.log" log_level="INFO"
event_message = "*tracker.log*"
| streamstats count AS CNT
| eval SORT = ( CNT + 5 ) % 10
| sort SORT
| dedup event_message
| table SORT, _time, event_message
```

![スクリーンショット 2021-11-14 21.37.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1465018/5d843077-84d6-1aae-770b-6179fef50e91.png "重複削除後-並べ替えあり2")

特に、ログのリカバリや転送で同じ日時のイベントが複数存在しうる場合等は、インデックスへの格納時間等何らかの値で並び替えを行わないと思わぬログが残されてしまう可能性があるため、注意が必要です。
GAS を使って定期的にgoogle アカウントのパスワードを変更する

# はじめに

業務で社員が共有しているGoogle アカウントのパスワードを、セキュリティの面で定期的に変える必要があったのでその時のことを残します。


# 手順

1. 特権管理者アカウントでスクリプトを開く

2. サービス "Admin SDK API" の追加

3. Gas のコード変更

```js
function doGet(e) {
  var address = e.parameter.address
  var newPassword = e.parameter.password
  var user = AdminDirectory.Users.get(address);
  var userKey = user.id
  let data = {
    "password": newPassword, // 仮パスワード
    "changePasswordAtNextLogin": false, // ログイン後にパスワードの変更を強制しない
  }
  AdminDirectory.Users.update(JSON.stringify(data),userKey);
}
```


# おわりに

特権管理者じゃないと、

```
Not Authorized to access this [resource/api]
```

怒られるので注意

## 参考記事

https://dev.classmethod.jp/articles/g-suite-groups-insert/

https://developers.google.com/apps-script/advanced/admin-sdk-directory
SQLインジェクションを防ぐ 〜8

#####SQLコードインジェクション防止の８のベストプラクティス 
https://snyk.io/blog/sql-injection-cheat-sheet/

######第8章です。

[序章](https://qiita.com/bricolageart/items/f87dd0d98f1e1b5c4666)
1. [クライアントサイドの入力バリデーションに依存しない](https://qiita.com/bricolageart/items/b442165360ef24d093da)
2. [制限された権限を持つデータベースユーザーを使用する](https://qiita.com/bricolageart/items/a857fa1c058a4527f155)
3. [プリペアドステートメントとクエリパラメータ化を使用する](https://qiita.com/bricolageart/items/ffe99886bdcdbf32658e)
4. [コードをスキャンしてSQLインジェクションの脆弱性を探す](https://qiita.com/bricolageart/items/37b13aaa59a28c148ac5)
5. [ORMレイヤーを使用する](https://qiita.com/bricolageart/items/1532b73057a260aaa755)
6. [ブロックリストに依存しない](https://qiita.com/bricolageart/items/6d4a78da2870dce6edf7)
7. [入力バリデーションを行う](https://qiita.com/bricolageart/items/36689c797a678e406b6c)
8. [ストアドプロシージャに注意する](https://qiita.com/bricolageart/items/2abfb7ef48a6bfd990bf)


[チートシート（英語）はこちら](https://snyk.io/wp-content/uploads/8-best-practices-to-prevent-SQL-injection-attacks.pdf)


### 8.ストアドプロシージャに注意
多くのデベロッパが、SQLインジェクションを防ぐには、ストアドプロシージャを使用するのが良いと思っています。しかし、必ずしもそうとは限りません。アプリケーションで作成したSQLクエリと同様に、ストアドプロシージャにも悪意のあるインジェクションが可能です。

アプリケーションのSQLクエリと同様に、ストアドプロシージャのクエリもパラメータを連結するのではなく、パラメータ化する必要があります。ストアドプロシージャへのSQLインジェクションは、非常に簡単に防ぐことができます。


ですから、MySQLではこのようなことをしないでください。
 
 
`DELIMITER //
CREATE PROCEDURE `FindUsers`(
    IN Username VARCHAR(50)
)
BEGIN`

`SET @Statement = CONCAT('SELECT * FROM User WHERE username = ', Username, ' );`

`PREPARE stm FROM @Statement;
    EXECUTE stm;`

`END //
DELIMITER ;`

ストアドプロシージャでは、パラメータ化されたクエリを使用してください。

`DELIMITER //
CREATE PROCEDURE `First`(
    IN Username VARCHAR(50)
)
BEGIN`

`PREPARE stm FROM 'SELECT * FROM User WHERE username = ?';
    EXECUTE stm USING Username;`


`END //
DELIMITER ;`




上記のように、、アプリケーションのコードと同じルールがストアドプロシージャに適用されます。ストアドプロシージャの実装方法は、データベースによって異なります。利用しているデータベースのストアドプロシージャの実装方法を確認し、そこでもインジェクションに注意してください。すべてのロジックをアプリケーション内で行うことが望ましいとは思いますが、開発する言語でプリペアドステートメントが利用できない場合、ストアドプロシージャは合理的な解決策となります。



8つめは以上です。
最後までお読みいただきありがとうございました！

######最終章は以上です。


SQLインジェクションに関する英語全原文は[こちら](https://snyk.io/blog/sql-injection-cheat-sheet/)です。

---
######Contents provided by:
Jesse Casman, Fumiko Doi, Content Strategists for Snyk, Japan, and Randell Degges, Community Manager for Snyk Global
SQLインジェクションを防ぐ 〜7

#####SQLコードインジェクション防止の８のベストプラクティス 
https://snyk.io/blog/sql-injection-cheat-sheet/

######第7章です。

[序章](https://qiita.com/bricolageart/items/f87dd0d98f1e1b5c4666)
1. [クライアントサイドの入力バリデーションに依存しない](https://qiita.com/bricolageart/items/b442165360ef24d093da)
2. [制限された権限を持つデータベースユーザーを使用する](https://qiita.com/bricolageart/items/a857fa1c058a4527f155)
3. [プリペアドステートメントとクエリパラメータ化を使用する](https://qiita.com/bricolageart/items/ffe99886bdcdbf32658e)
4. [コードをスキャンしてSQLインジェクションの脆弱性を探す](https://qiita.com/bricolageart/items/37b13aaa59a28c148ac5)
5. [ORMレイヤーを使用する](https://qiita.com/bricolageart/items/1532b73057a260aaa755)
6. [ブロックリストに依存しない](https://qiita.com/bricolageart/items/6d4a78da2870dce6edf7)
7. [入力バリデーションを行う](https://qiita.com/bricolageart/items/36689c797a678e406b6c)
8. [ストアドプロシージャに注意する](https://qiita.com/bricolageart/items/2abfb7ef48a6bfd990bf)


[チートシート（英語）はこちら](https://snyk.io/wp-content/uploads/8-best-practices-to-prevent-SQL-injection-attacks.pdf)


### 7.入力バリデーションを実行する
 
入力バリデーションを必ず行ってください。クエリのパラメータ化によるプリペアドステートメントは、SQLインジェクションに対する最善の防御策ですが、常に複数の防御層を設けるようにしましょう。データベースユーザーの権限を制限するのと同様に、入力バリデーションは、アプリケーションのリスクを全般的に下げるための優れた方法です。

また、プリペアドステートメントが利用できない状況もあります。言語によってはこの仕組みをサポートしていなかったり、古いデータベースシステムではユーザーの入力をパラメータとして与えることができなかったりします。このような場合、入力バリデーションが代替手段となるでしょう。

前述のように、入力バリデーションはブロックリストではなく許可リストに依存するようにしてください。許可されるすべてのパターンを正規表現などで記述したルールを作成するか、メンテナンスの行き届いたライブラリを使用してください。これをプリペアドステートメントやクエリのパラメータ化と組み合わせることで、強固な防御が可能になります。




7つめは以上です。
最後までお読みいただきありがとうございました！

######次回は最終章　SQLインジェクションを防ぐ 〜8に続きます。


SQLインジェクションに関する英語全原文は[こちら](https://snyk.io/blog/sql-injection-cheat-sheet/)です。

---
######Contents provided by:
Jesse Casman, Fumiko Doi, Content Strategists for Snyk, Japan, and Randell Degges, Community Manager for Snyk Global
SQLインジェクションを防ぐ 〜6

#####SQLコードインジェクション防止の８のベストプラクティス 
https://snyk.io/blog/sql-injection-cheat-sheet/

######第6章です。

[序章](https://qiita.com/bricolageart/items/f87dd0d98f1e1b5c4666)
1. [クライアントサイドの入力バリデーションに依存しない](https://qiita.com/bricolageart/items/b442165360ef24d093da)
2. [制限された権限を持つデータベースユーザーを使用する](https://qiita.com/bricolageart/items/a857fa1c058a4527f155)
3. [プリペアドステートメントとクエリパラメータ化を使用する](https://qiita.com/bricolageart/items/ffe99886bdcdbf32658e)
4. [コードをスキャンしてSQLインジェクションの脆弱性を探す](https://qiita.com/bricolageart/items/37b13aaa59a28c148ac5)
5. [ORMレイヤーを使用する](https://qiita.com/bricolageart/items/1532b73057a260aaa755)
6. [ブロックリストに依存しない](https://qiita.com/bricolageart/items/6d4a78da2870dce6edf7)
7. [入力バリデーションを行う](https://qiita.com/bricolageart/items/36689c797a678e406b6c)
8. [ストアドプロシージャに注意する](https://qiita.com/bricolageart/items/2abfb7ef48a6bfd990bf)

[チートシート（英語）はこちら](https://snyk.io/wp-content/uploads/8-best-practices-to-prevent-SQL-injection-attacks.pdf)


### 6.ブロックリストに依存しない
これはすでに多くの人に馴染みがあると思いますが、再度、次のことを強調します。パラメータにブロックリストを実装しないでください。ブロックリストアプローチは、脆弱な入力を定義するルールのコレクションを設定します。入力がこれらのルールに合致する場合、リクエストはブロックされます。しかし、そのルールが弱すぎると、悪意のあるエントリが有効になりえます。強すぎると、有効なエントリーをブロックしてしまいます。
 
たとえば、`OR`という単語を含むすべてのリクエストをブロックします。これは妥当なルールかもしれませんが、実際には「Or」はイスラエル人のファーストネームとして非常に一般的に使われています。つまり、私の同僚の名前を入力すると、大勢の人がブロックされてしまうのです。同じことが「シングルクォート」'にも当てはまります。無数の名前がその文字を含んでいます。O'NeillやO'Donnellなどもあります。またDont'aといったファーストネームもあるのです。



6つめは以上です。
最後までお読みいただきありがとうございました！

######次回はSQLインジェクションを防ぐ 〜7に続きます。


SQLインジェクションに関する英語全原文は[こちら](https://snyk.io/blog/sql-injection-cheat-sheet/)です。

---
######Contents provided by:
Jesse Casman, Fumiko Doi, Content Strategists for Snyk, Japan, and Randell Degges, Community Manager for Snyk Global
ウィンドウ，ボタンとイベント

　プロデルの魅力の一つは，「ウィンドウの設計」が付属しており画面を簡単に作れることである。
　いま，data.go.jpからデータセットを得ようとしていて（[過去の記事1](https://qiita.com/nipo/private/4a663b1ec2a12e21f912)・[過去の記事2](https://qiita.com/nipo/private/47fcd9a5c35e3a26ad12)），検索結果を見やすくするためにウィンドウを作る。

## ウィンドウの設計

　ウィンドウの設計は，挿入→ウィンドウで起動する。この時，自動的にウィンドウのコードがソースに追加される。ウィンドウの設計を一度閉じても，右側のツリーから画面をダブルクリックすることで再度開かれる。

　リストボックスとボタンを2つ設置した。

```プロデル
初期化する手順
ーー自動生成された手順です。ここにプログラムを書き加えても消える場合があります
	この実質大きさを｛675,390｝に変える
	この内容を「検索画面」に変える
	初期化開始する
	ボタン2というボタンを作る
		その大きさを｛100，45｝に変える
		その内容を「＞」に変える
		その移動順を2に変える
	ボタン1というボタンを作る
		その大きさを｛100，45｝に変える
		その内容を「＜」に変える
		その移動順を1に変える
		その有効は，×。
	リストボックス1というリストボックスを作る
		その項目高さを27に変える
		その移動順を3に変える
		そのフォントを「游ゴシック,15.75,太字」に変える
	初期化終了する
終わり
```

今回は大きさが変えられるウィンドウであり，位置と，リストボックスの大きさを定める意味がないので消した。

## イベント

　オブジェクト毎に利用可能なイベントが定められており，これはドキュメントに明記されている。今回は，ウィンドウの大きさが変わった時にリストボックスの大きさとボタンの位置を変えたい。また，ボタンが押された時に，検索結果を次々に表示したい。

### ウィンドウの大きさが変わったときにGUI部品の位置と大きさを変える

ウィンドウに関するイベントは，主語なくウィンドウ種類の定義内にいきなり書く。ウィンドウの大きさは「自分の大きさ」や「自分の実質大きさ」で取得する。基本的には実質大きさを使うことが多いはず。

```プロデル
検索画面とは
	ウィンドウを継承する
	’（略）
	大きさが変化した時の手順
		リストボックス1の大きさを｛自分の実質大きさの幅，自分の実質大きさの高さ-45｝に変える。
		ボタン1の位置を｛自分の実質大きさの幅-200，自分の実質大きさの高さ-45｝に変える。
		ボタン2の位置を｛自分の実質大きさの幅-100，自分の実質大きさの高さ-45｝に変える。
	終わり
終わり
```

　これで，ウィンドウの大きさが変わった際に他のGUI部品の位置や大きさも変わるようになったが，このプログラムは動かない。なぜなら，ウィンドウが初期化される際にもウィンドウの大きさが変わるためこの手順が実行されてしまうからである。その時，まだボタン1などは未定義であり，エラーが出る。

　そこで，開いた時イベントを使って，GUI部品が出来上がっていた場合にのみ位置と大きさを変更することにした。

```プロデル
開いた時の手順
	表示済は，○
	大きさが変化した
終わり
大きさが変化した時の手順
	もし表示済なら
		リストボックス1の大きさを｛自分の実質大きさの幅，自分の実質大きさの高さ-45｝に変える。
		ボタン1の位置を｛自分の実質大きさの幅-200，自分の実質大きさの高さ-45｝に変える。
		ボタン2の位置を｛自分の実質大きさの幅-100，自分の実質大きさの高さ-45｝に変える。
	もし終わり
終わり
```

　イベント手順を呼び出す際は，「時の手順」を除いて呼べば良い（ドキュメントに書かれているままの名称で呼び出せる）。

### ボタンのクリック

　ウィンドウ以外のGUI部品のイベントは，その部品の名称を主語にして記述する。

```プロデル
ボタン1がクリックされた時の手順

終わり
```

## できあがり

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150160/55892d48-8e76-9e48-4594-bc4ff31a5d23.png)
SQLインジェクションを防ぐ 〜5

#####SQLコードインジェクション防止の８のベストプラクティス 
https://snyk.io/blog/sql-injection-cheat-sheet/

######第５章です。

[序章](https://qiita.com/bricolageart/items/f87dd0d98f1e1b5c4666)
1. [クライアントサイドの入力バリデーションに依存しない](https://qiita.com/bricolageart/items/b442165360ef24d093da)
2. [制限された権限を持つデータベースユーザーを使用する](https://qiita.com/bricolageart/items/a857fa1c058a4527f155)
3. [プリペアドステートメントとクエリパラメータ化を使用する](https://qiita.com/bricolageart/items/ffe99886bdcdbf32658e)
4. [コードをスキャンしてSQLインジェクションの脆弱性を探す](https://qiita.com/bricolageart/items/37b13aaa59a28c148ac5)
5. [ORMレイヤーを使用する](https://qiita.com/bricolageart/items/1532b73057a260aaa755)
6. [ブロックリストに依存しない](https://qiita.com/bricolageart/items/6d4a78da2870dce6edf7)
7. [入力バリデーションを行う](https://qiita.com/bricolageart/items/36689c797a678e406b6c)
8. [ストアドプロシージャに注意する](https://qiita.com/bricolageart/items/2abfb7ef48a6bfd990bf)


[チートシート（英語）はこちら](https://snyk.io/wp-content/uploads/8-best-practices-to-prevent-SQL-injection-attacks.pdf)


### 5.ORMレイヤーを使用する
オブジェクトリレーショナルマッピング（ORM）レイヤーの使用の検討も推奨しています。ORMレイヤーは、データベースからのデータをオブジェクトに、またはその逆に変換します。ORMライブラリを使用すると、明示的なSQLクエリを減らすことができ、SQLインジェクションに対する脆弱性を大幅に低減できます。
既存のORMライブラリのいくつかの優れた例としてJavaのHibernate およびEntityFramework for C＃があります。これらのORMライブラリの使用により、オブジェクトとデータベーステーブル間のマッピングを行うための雛型となるコードを自動生成することが可能です。
これならば、SQLクエリに自分で関与する必要さえありません。
 
とはいえ、カスタムクエリを作成する必要がある場合は、ここで問題が発生します。JavaのHibermateには、独自のHibernateクエリ言語（HQL）があります。HQLクエリをコンパイルするときは、インジェクションに再度注意し、プリペアドステートメントと同様に機能する`createQuery()`関数を使用する必要があります。

JavaScriptの場合、sequelizeのようなよく知られたORMライブラリも利用できます。sequelizeを使用すると、値をデータベース内の特定のタイプにマップする方法を定義できます。しかし、最終的に、ORMライブラリはロジックをSQLステートメントに変換し直す必要があります。したがって、これらのライブラリがパラメータの適切なエスケープを実装していると考えられます。
ORMライブラリにSQLインジェクションの問題が含まれていないことを確認するには、既知の脆弱性がないかをスキャンする必要があります。間違った古いバージョンの[sequelize](https://snyk.io/vuln/search?type=npm&q=sequelize)または[hibernate](https://snyk.io/vuln/search?q=org.hibernate&type=maven)の使用によっても、問題が発生することがあります。[Snyk Open Source](https://snyk.io/product/open-source-security-management/)を使ってプロジェクトをチェックすることにより、ライブラリ内の隠されたSQLインジェクションや他の多くの問題を防止することができます。 



5つめは以上です。
最後までお読みいただきありがとうございました！

######次回はSQLインジェクションを防ぐ 〜6に続きます。


SQLインジェクションに関する英語全原文は[こちら](https://snyk.io/blog/sql-injection-cheat-sheet/)です。

---
######Contents provided by:
Jesse Casman, Fumiko Doi, Content Strategists for Snyk, Japan, and Randell Degges, Community Manager for Snyk Global
SQL 記述・実行順序 or Group by・Having・Order by句の記述方法

#記事内容

- **select文の記述順序と実行順序**
- **テーブルデータのグループ化**
- **集約結果の絞り込み**
- **取得データの並び替え**

#select文の記述順序と実行順序
**記述する順序は下記表の通りに決められている**

:::note alert
select文の記述順序と実行順序は異なるので注意
:::

| 記述順 | 文・句 | 意味 |実行順 | 文・句 | 意味 |
|:-:|:-|:-|:-:|:-|:-|
|1| select  | 取得行（カラム）の指定  |1| from  |  対象テーブルの指定 |
|2| from  |  対象テーブルの指定 |2| inner join  | 結合処理  |
|3| inner join  | 結合処理  |3| where  | 絞り込みの指定  |
|4| where  | 絞り込みの指定  |4| group by  | グループ化の条件を指定  |
|5| group by  | グループ化の条件を指定  |5| having  | グループ化した後の絞り込み条件の指定  |
|6| having  | グループ化した後の絞り込み条件の指定  |6| select  | 取得行（カラム）の指定  |
|7| order by  | 並び替え条件の指定  |7| order by  | 並び替え条件の指定  |
|8| limit  | 取得する行数の制限  |8| limit  | 取得する行数の制限  |

##group by
**テーブルデータのグループ化**

- `group by`で指定する列名によって**グループ化**される
- `列名`は**複数指定可**
- `count関数`で**グループ別に集計**

```sql
-- prefecture_id = 都道府県ID

-- 都道府県別のユーザー数を取得
select
  prefecture_id,
  count(*)
from
  users
group by
  prefecture_id;
```

##having
**集約結果をさらに絞り込むことができる句**

- **テーブルデータを集約した結果に対して、条件式を適用する場合に利用**
- `having`は`group by`の後に記述

```sql
-- access_logs = アクセスログテーブル
-- request_month = アクセスした年月日
-- user_id =　アクセスしたユーザーID

-- 2017年のアクセスログから月間ユニークユーザー数が600人以上の月の一覧を取得

select
  request_month,
  count(distinct user_id)
from
  access_logs
where
  request_month >= '2017-01-01'
  and request_month < '2018-01-01'
group by
  request_month
having
  count(distinct user_id) >= 600;
```

#order by
**取得データの並び替え**

* **並び替えの指定**
   * `asc`  ・・・ **昇順 (ascending)**
   * `desc` ・・・ **降順 (descending)**

:::note
取得するデータの並び順が重要な場合、order by句を使用して明示的に指定
:::

```sql
-- 構文
order by 列や式 並び替え, ...;

-- 価格が低い順に取得
select * from products order by price asc;
-- asc はデフォルト設定のため、省略可
select * from products order by price;
```

###並び替え条件の指定

```sql
-- 商品価格が高い順に並び替えを行い、商品価格が同じ場合は登録順に並び替えて取得
select * from products order by price desc, id asc; 
```

##参考教材

https://www.udemy.com/course/standard-sql-for-beginners