SOQLでSELECT * FROM SOME-TABLEっぽいことする

# はじめに
この記事は 2020 年の RevComm アドベントカレンダー 17 日目の記事です。 16 日目は @shuheikatoinfo さんの「[音声合成・激動の10年を振り返る](https://qiita.com/shuheikatoinfo/items/f37898d1e46cd1e218a8)」でした。

こんにちは、 RevComm でサーバサイドエンジニアをしている @enotesupa です。

今回は、Python から Salesforce の REST APIを実行するためのパッケージの一つである、 [simple-salesforce](https://github.com/simple-salesforce/simple-salesforce) を利用して、SOQL では実行できない、オブジェクトの全てのカラムの値を取得する方法を紹介します。

標準的な SQL には、 `SELECT *` のように、ワイルドカードを使ってテーブル（オブジェクト）の全てのカラム（項目）の値を取得する事ができますが、 Salesforce の SOQL では使うことができません。しかし、実際にクエリを実行する際に、オブジェクトの項目名がわからないため、全ての項目について取得したい場合もあるでしょう。

ただし今回紹介する方法は、一部の標準オブジェクトなどでは、項目数が多すぎて `Malformed Request` のエラーレスポンスが返される場合があるのでご注意ください。

# SOQLって何？
Salesforceには、Trailheadという、eラーニング形式で学習できる多数のコンテンツが用意されています。その内容の一つに [SQL から SOQL への移行](https://trailhead.salesforce.com/ja/content/learn/modules/database_basics_dotnet/sql_to_soql)という単元があり、 SQL と SOQL の違いについて紹介されています。

> すぐに気づく大きな違いは、SOQL には SELECT * などというものは存在しないことです。SOQL は Salesforce データを返し、そのデータはマルチテナント環境に存在しますが、マルチテナント環境では全員が「データベースを共有」しているようなものであるため、* などのワイルドカード文字を使用すると問題が発生します。率直に言うと、特にテーブルの項目名がわからない場合などに、新しい SQL クエリを開始して、SELECT * FROM SOME-TABLE と入力してしまいがちですが、このアクションは、共有環境内の他のテナントに多大な影響を及ぼす可能性があります。それは日曜日の朝 7 時に庭の芝生を刈るようなもので、まったく思いやりのない迷惑な行為です。

とあり、サクッと SQL で馴染んだ文を書いても実行エラーが発生し、一切使えないことが分かります。

アプリケーション側で使わないカラムまで取ってくることは SQL でもアンチパターンであるとも言われたりするので、今回は**テーブルの項目名がわからない場合の調査として 1 回だけ使用する**ようなユースケースで実現できるものを作ってみます。

# 前提
- Python 実行環境(僕は Jupyter Notebook を使用します)
- API 連携ができる Enterprise Edition 以上の Salesforce アカウント(僕は Enterprise のサンドボックス環境アカウントを使用します)

# ライブラリのインストール(Jupyter Notebook)
Jupyter では `!` 始まりでコマンドを実行できます。

```shell
!pip install simple-salesforce
```

# インポート
表示名であるあるですが `-` であるものが `_` になっているので typo に注意。また、結果表示のために pandas も活用します。

```python
import simple_salesforce
import pandas as pd
```

# 認証
接続アプリケーションを作成して OAuth を使った認証方式も使えますが、今回はユーザ名とパスワードを用いた認証方式を用いました。以下のように、`simple_salesforce.Salesforce` オブジェクトを作成します。

```python
username = "hogehoge@fugafuga.com"
password = "hogehogepass"
org_id = ""
proxies = None
security_token = None
domain = "test"

sf = simple_salesforce.Salesforce(
    username=username, password=password, domain=domain,
    organizationId=org_id, proxies=proxies, security_token=security_token)
```

これでクエリを叩けるようになります。

# クエリを作成・実行する
今回の記事のためにカスタムオブジェクト・カスタム項目を新たに作成し、その中で更新日時が最新の 10 件を取得します。

```python
# カスタムオブジェクトの項目の一覧を取得する
fields = [x['name'] for x in sf.RevCommAdventCalendar__c.describe()['fields']]
print(fields)

# クエリを作成する
query = "SELECT {} FROM RevCommAdventCalendar__c order by LastModifiedDate desc limit 10".format(", ".join(fields))
print(query)

# クエリを実行する
records = sf.query(query)
print(records)
```
## 出力結果
![スクリーンショット 2020-12-17 11.41.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/890566/2d3e11f3-184c-6ee6-545b-a2d1e39724fd.png)

# 実行結果
`pd.DataFrame` を利用して結果をテーブル表示させます。

```
pd.DataFrame(records['records'])
```

![スクリーンショット 2020-12-17 10.44.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/890566/bf1f19ca-6280-a3aa-06c0-8a7ca7015deb.png)

# おわりに
制約はありますが、カスタムオブジェクトにおいて SOQL でも禁じ手の `SELECT * FROM SOME-TABLE` っぽいことができるという検証結果でした。

実行結果の通り、一度に多くの項目を取得するため、実際にアプリケーションに組み込んで使う際には、必要なカラムに絞って実行しましょう。

明日は @zomaphone さんの投稿です。お楽しみに！

# 追記 (2021 年 4 月 22 日)
Salesforce Spring’21 に含まれるアップデートに `SELECT FIELDS(ALL) FROM SOME-TABLE LIMIT 200` とすることで本記事における内容が公式でできるようになりました。 simple-salesforce においては `simple_salesforce.Salesforce` オブジェクトのインスタンス引数に `version="51.0"` を渡すことで実行可能になっています。
https://developer.salesforce.com/blogs/2021/01/new-soql-fields-function-is-ga.html