# 数式

- Human documentation: [https://docs.univer.ai/ja-JP/guides/sheets/features/core/formula](https://docs.univer.ai/ja-JP/guides/sheets/features/core/formula)

- Agent Markdown: [https://docs.univer.ai/ja-JP/guides/sheets/features/core/formula.md](https://docs.univer.ai/ja-JP/guides/sheets/features/core/formula.md)

- Requested language: `ja-JP`

- Content language: `ja-JP`

- Documentation version: `1.1.0`

- Source: [sheets/features/core/formula.ja-JP.mdx](https://github.com/dream-num/documentation/blob/dev/content/guides/sheets/features/core/formula.ja-JP.mdx)

---

数式は Univer が提供する中核能力の一つで、セル内で数式を用いて値を計算できます。サポートされる関数は Excel と互換であり、数学・論理・テキスト・日付関数など多岐にわたります。

ブック内の数式が多い場合、数式計算がメインスレッドを占有してページの応答性が低下することがあります。ページのレンダリングや操作への影響を抑えるため、数式計算は Worker プロセスで実行することをおすすめします。設定方法については [Web Workers](https://docs.univer.ai/ja-JP/guides/sheets/getting-started/worker.md) を参照してください。

## 設定

以下は数式関連プラグインの設定項目へのリンクです:

| プラグイン                      | 設定項目                                                                                  |
| -------------------------- | ------------------------------------------------------------------------------------- |
| `@univerjs/engine-formula` | [IUniverEngineFormulaConfig](https://docs.univer.ai/ja-JP/reference/packages/plugins/univerjs/engine-formula.md)     |
| `@univerjs/sheets-formula` | [IUniverSheetsFormulaBaseConfig](https://docs.univer.ai/ja-JP/reference/packages/plugins/univerjs/sheets-formula.md) |

`initialFormulaComputing` はブック読み込み時の計算方法を制御します。保存済みの計算結果が古い場合は `CalculationMode.FORCED` で再計算します。プリセットモードでは同じオプションを `formula` 内に指定します。

利用中のモードを選び、既存の初期化設定に以下のオプションを組み込んでください。他のプラグイン、言語、テーマ、コンテナーの設定は保持し、同じプラグインを二重に登録しないでください。

#### プラグインモード

```typescript
import { CalculationMode, UniverSheetsFormulaPlugin } from '@univerjs/sheets-formula'

univer.registerPlugin(UniverSheetsFormulaPlugin, {
  initialFormulaComputing: CalculationMode.FORCED,
})
```

#### プリセットモード

```typescript
import { CalculationMode, UniverSheetsCorePreset } from '@univerjs/preset-sheets-core'
import { createUniver } from '@univerjs/presets'

const { univerAPI } = createUniver({
  presets: [
    UniverSheetsCorePreset({
      formula: {
        initialFormulaComputing: CalculationMode.FORCED,
      },
    }),
  ],
})
```

* `CalculationMode.FORCED`: すべての数式を再計算します。
* `CalculationMode.WHEN_EMPTY`: 既定値：保存済みの `v` 値がない数式セルだけを計算します。
* `CalculationMode.NO_CALCULATION`: 初期化時には計算しません。

## ブック間参照

ブック間数式は、たとえば `Report` から `Sales.xlsx` の売上を集計します。同じブック内の別シートへの参照に加え、参照元ブックも指定します。

### 参照の構文

| 用途         | 例                                     | 対象                        |
| ---------- | ------------------------------------- | ------------------------- |
| 同じブックの別シート | `=SUM(Data!A1:A10)`                   | 現在のブックの Data シート          |
| 別ブックのセル範囲  | `=SUM('[Sales.xlsx]Data'!A1:A10)`     | Sales.xlsx の Data シート     |
| 別ブックのテーブル  | `=SUM(Sales.xlsx!SalesTable[Amount])` | 既存の SalesTable の Amount 列 |

`Sales.xlsx` はブック名であり、ファイルパスや URL ではありません。数式を書いてもファイルの読み込み、Excel のダウンロード、別のブラウザーページへの接続は行われません。構造化参照には対応する[テーブル](https://docs.univer.ai/ja-JP/guides/sheets/features/table.md)を先に作成してください。

### 同じインスタンスに読み込んだブックを参照する

基本の数式エンジンでは、両方のブックを同じ Univer インスタンスと計算環境に読み込みます。以下は Sheets 初期化後に実行します。プラグインモードでは本ページの Facade 拡張も読み込んでください。

```typescript
const source = univerAPI.createWorkbook({
  id: 'sales-workbook',
  name: 'Sales.xlsx',
  sheetOrder: ['sales-data'],
  sheets: {
    'sales-data': {
      id: 'sales-data',
      name: 'Data',
      rowCount: 100,
      columnCount: 20,
      cellData: {
        0: { 0: { v: 100 } },
        1: { 0: { v: 200 } },
      },
    },
  },
}, { makeCurrent: false })

const report = univerAPI.createWorkbook({ id: 'report-workbook', name: 'Report' })
const resultCell = report.getActiveSheet().getRange('A1')
resultCell.setFormula("=SUM('[Sales.xlsx]Data'!A1:A2)")

const formula = univerAPI.getFormula()
formula.executeCalculation()
await formula.onCalculationResultApplied()
console.log(resultCell.getValue()) // 300
```

`makeCurrent: false` は現在のブックを切り替えずに参照元を読み込みます。`source` と `report` を保持すれば、アクティブなブックが変わっても書き込み先を取り違えません。

名前は大文字・小文字を区別せず、完全一致を優先します。一致しない場合だけ `.xlsx`、`.xlsm` などの拡張子を無視します。候補がない場合や複数ある場合は `#REF!` となるため、一意の名前を使用してください。

### 安定した参照元 ID にバインドする

数式内の名前を固定の文書 ID に関連付けるには、Pro 拡張の `@univerjs-pro/engine-formula` を使用します。[クライアントライセンス](https://docs.univer.ai/ja-JP/server/license.md)を設定し、初期化時に `UniverFormulaEnginePlugin` を `UniverProFormulaEnginePlugin` に置き換え、Pro Facade を読み込みます。両方のエンジンを登録しないでください。Facade の import だけではプラグインは登録されません。

Pro エンジンを設定済みの環境で、上のブックを使用します。

```typescript
import '@univerjs-pro/engine-formula/facade'

const reference = univerAPI.getFormula().buildReference({
  hostUnitId: report.getId(),
  unit: {
    unitId: source.getId(),
    formulaQualifier: 'Sales Source',
  },
  target: {
    kind: univerAPI.Enum.FormulaReferenceType.SHEET_RANGE,
    sheetName: 'Data',
    range: { startRow: 0, endRow: 1, startColumn: 0, endColumn: 0 },
  },
})

resultCell.setFormula(`=SUM(${reference})`)
```

`hostUnitId` は数式を保存するブック、`unit.unitId` は参照元の安定した ID、`formulaQualifier` は数式内の公開名です。座標は 0 始まりで終端を含みます。

`buildReference()` は `=` のない参照文字列を返し、バインドを保存します。参照元の読み込みや計算は行いません。データは計算環境に読み込んだブックか、設定済みのデータプロバイダーから提供する必要があります。手書きやアプリ生成の数式は、先にバインドしてください。

```typescript
const formula = univerAPI.getFormula()
const bound = formula.upsertExternalReference({
  unitId: report.getId(),
  qualifier: 'Sales Source',
  sourceUnitId: source.getId(),
  sourceUnitType: univerAPI.Enum.UniverInstanceType.UNIVER_SHEET,
})
if (!bound) throw new Error('Could not bind Sales Source')

resultCell.setFormula("=SUM('[Sales Source]Data'!A1:A2)")
```

同じバインドの繰り返しは冪等です。`qualifier` に括弧や引用符を含めないでください。`1`、`2` などの正整数は Excel 外部リンクのスロット番号であり、文書 ID には使用できません。外部リンクをインポートする場合はリンクリソースも保持し、数式だけをコピーしないでください。

### 更新・保存・アンロード

Facade で参照元を更新した後、明示的に計算し、結果の反映を待って読み取ります。`setFormula()` や `setValue()` の直後に非同期計算が完了したとは限りません。

```typescript
const sourceSheet = source.getSheetBySheetId('sales-data')
if (!sourceSheet) throw new Error('Source sheet not found')
sourceSheet.getRange('A1').setValue(150)
formula.executeCalculation()
await formula.onCalculationResultApplied()
console.log(resultCell.getValue()) // 350
```

`report.save()` でバインドリソースを含む完全なスナップショットを保存します。数式だけを保存すると名前と ID の対応が失われます。参照元データは別途保存・読み込みが必要です。

参照元をアンロードすると、通常の読み込み済みブック参照はデータを取得できなくなります。Pro 参照で計算を継続できるかはデータプロバイダーとキャッシュ次第です。バインドはデータを含まず、オフライン計算を保証しません。以前の表示値を最新値とみなさないでください。

不要になった Pro バインドは削除できます。

```typescript
univerAPI.getFormula().removeExternalReference({
  unitId: report.getId(),
  qualifier: 'Sales Source',
})
```

削除しても数式は削除・書き換えされません。次の計算では名前を再解決し、同名の読み込み済みブックがあれば参照が解決する場合があります。なければ `#REF!` になります。参照を停止するには数式も変更・消去してください。詳細は [FFormula 参照 API](https://docs.univer.ai/ja-JP/reference/facade/formula.md) を参照してください。

## 3D 参照

3D 参照は同一ブック内の連続したシートを対象にします。ブック間参照とは別の機能です。

```text
=SUM(Jan:Mar!B2:C3)
=SUM('Jan':'Mar'!B2)
```

Jan と Mar、およびその間の全シートをブック内の順序で含みます。名前順ではありません。シートを並べ替える場合は集計範囲を確認してください。

## サポートされる数式関数



## Facade API

完全な Facade API については、[FFormula](https://docs.univer.ai/ja-JP/reference/facade/formula.md) を参照してください。

### インポート

> [!INFO: プラグインモード注意]
> Facade パッケージの手動 import が必要なのはプラグインモードのみです。プリセットモードには対応する Facade
> パッケージが内蔵されているため、追加の import は不要です。

```typescript
import '@univerjs/engine-formula/facade'
import '@univerjs/sheets-formula/facade'
import '@univerjs/sheets-formula-ui/facade'
```

### 範囲セレクターダイアログを開く

`@univerjs/sheets-formula-ui/facade` は `univerAPI.showRangeSelectorDialog` を追加します。カスタム UI でユーザーに 1 つ以上のセル範囲を選択させたい場合、数式関連パネルと同じ範囲セレクターを利用できます。

```typescript
const workbook = univerAPI.getActiveWorkbook()
const worksheet = workbook?.getActiveSheet()

if (workbook && worksheet) {
  await univerAPI.showRangeSelectorDialog({
    unitId: workbook.getId(),
    subUnitId: worksheet.getSheetId(),
    initialValue: [
      {
        unitId: workbook.getId(),
        sheetName: worksheet.getSheetName(),
        range: worksheet.getRange('A1:B2').getRange(),
      },
    ],
    maxRangeCount: 2,
    supportAcrossSheet: true,
    callback: (ranges, isCancel) => {
      console.log(ranges, isCancel)
    },
  })
}
```

### 数式計算の実行

```typescript
const formulaEngine = univerAPI.getFormula()
formulaEngine.executeCalculation()
```

### 数式計算の停止

```typescript
const formulaEngine = univerAPI.getFormula()
formulaEngine.stopCalculation()
```

### 数式計算の開始

```typescript
// 数式計算の開始イベントをリッスン
const formulaEngine = univerAPI.getFormula()
formulaEngine.calculationStart((forceCalculate) => {
  console.log(forceCalculate)
})
```

### 数式計算の進行

```typescript
// 数式計算の進行イベントをリッスン
const formulaEngine = univerAPI.getFormula()
formulaEngine.calculationProcessing((stageInfo) => {
  console.log(stageInfo)
})
```

### 数式計算の終了

```typescript
// 数式計算の終了イベントをリッスン
const formulaEngine = univerAPI.getFormula()
formulaEngine.calculationEnd((functionsExecutedState) => {
  console.log(functionsExecutedState)
})
```

### 数式計算結果の適用

```typescript
// 数式計算結果の適用イベントをリッスン
const formulaEngine = univerAPI.getFormula()
formulaEngine.calculationResultApplied((result) => {
  console.log(result)
})

// 数式計算結果の適用完了を待機
await formulaEngine.onCalculationResultApplied()
```

### 初期化データ時の数式計算モードの設定

```typescript
const formulaEngine = univerAPI.getFormula()
formulaEngine.setInitialFormulaComputing(CalculationMode.FORCED)
```

この API が `Starting` ライフサイクル以降に呼ばれた場合、次に Univer Sheet が生成されるタイミングで反映されます。

現在の初期化処理中に反映させたい場合は、`Ready` より前、例えば `Starting` ステージで呼び出してください:

```typescript
univerAPI.addEvent(univerAPI.Event.LifeCycleChanged, ({ stage }) => {
  if (stage === LifecycleStages.Starting) {
    const formulaEngine = univerAPI.getFormula()
    formulaEngine.setInitialFormulaComputing(CalculationMode.FORCED)
  }
})
```

### 反復計算

反復計算はブックごとに設定し、スナップショットの `calculationSettings` に保存されます。既定では無効です。循環参照の数式はキャッシュ値を保持し、値がなければゼロを使用します。意図的に循環参照を含むモデルで有効にしてください。

| オプション                  | 既定値     | 動作                                                   |
| ---------------------- | ------- | ---------------------------------------------------- |
| `iterativeCalculation` | `false` | 循環参照の反復計算を有効にします。                                    |
| `maxIterations`        | `100`   | 1～32767 の整数。                                         |
| `maxChange`            | `0.001` | 有限の正数で指定する収束しきい値。循環グループ内のすべての数値結果の変化がこの値以下になると停止します。 |

ブックの Facade から 3 つの設定をまとめて更新します。元に戻す、やり直し、共同編集に対応し、ブックが存在しないか編集できない場合は `false` を返します。無効な設定は既定値に正規化されます。

```ts
const workbook = univerAPI.getActiveWorkbook()
if (!workbook) throw new Error('No active workbook')

const applied = workbook.setCalculationSettings({
  iterativeCalculation: true,
  maxIterations: 100,
  maxChange: 0.001,
})
if (!applied) throw new Error('Could not update calculation settings')

console.log(workbook.getCalculationSettings())
```

`createWorkbook()` の `calculationSettings` で初期設定することもできます。設定はブックごとに独立しています。数式リボンの「計算オプション」でも同じ設定を変更できます。

計算結果の適用後に循環参照の診断を取得できます。`getCircularReferences()` は計算を開始しません。結果にはシート ID、0 始まりの行列座標、グループ ID、反復回数、取得可能な場合は収束情報が含まれます。

```ts
const formula = univerAPI.getFormula()
formula.executeCalculation()
await formula.onCalculationResultApplied()
console.table(workbook.getCircularReferences())
```

### 数式文字列の計算結果の取得

数式文字列の一括非同期実行と計算結果の取得:

```typescript
const formulaEngine = univerAPI.getFormula()
const formulas = {
  Book1: {
    Sheet1: {
      2: {
        3: [
          // 完全な数式:
          '=SUM(A1:A10) + SQRT(D7)',

          // 分解されたサブ数式（それぞれ独立して評価可能）:
          'SUM(A1:A10)', // サブ数式1
          'SQRT(D7)', // サブ数式2
          'A1:A10', // 範囲参照
          'D7', // 単一セル参照
        ],
      },
      4: {
        5: ['=A2 + B2 + SQRT(C5)', 'A2', 'B2', 'SQRT(C5)'],
      },
    },
  },
}
const result = await formulaEngine.executeFormulas(formulas)
console.log(result)
```

## カスタム数式

Univer はカスタム数式をサポートしています。実装方法は [Custom Formulas](/blog/custom-formula) を参照してください。

## よくある質問

### `HYPERLINK` 関数セルでハイパーリンク操作ができない

Univer Sheets 用のハイパーリンクプラグインをインポートする必要があります。詳細は [ハイパーリンク](https://docs.univer.ai/ja-JP/guides/sheets/features/hyper-link.md) モジュールを参照してください。
