[新機能]Snowflakeでテーブルに値を格納するのではなくクエリ実行時に式から値を計算する列を定義できるVirtual Columnsを試してみた

[新機能]Snowflakeでテーブルに値を格納するのではなくクエリ実行時に式から値を計算する列を定義できるVirtual Columnsを試してみた

2026.06.06

さがらです。

Snowflakeの新機能として、Virtual Columns(仮想列) がGAになっています。テーブルに値を格納するのではなく、クエリ実行時に式から値を計算する列を定義できる機能です。これまでViewで実現していた派生列をテーブル本体に定義できるようになり、テーブルとViewのペアをテーブル1つで代替できます。

https://docs.snowflake.com/en/release-notes/2026/10_19

https://docs.snowflake.com/en/sql-reference/virtual-columns

活用方法を複数のパターンに分けて試してみましたので、手順と確認結果をまとめます。

機能概要

Virtual Columnsは、テーブル定義時に AS (<expr>) 句を指定することで定義できる仮想列です。値はテーブルに保存されず、クエリ実行時にSnowflakeが式から計算します。

CREATE TABLEまたはALTER TABLEの構文で定義します。

-- CREATE TABLE時に定義
CREATE OR REPLACE TABLE <table> (
  <col_name> <col_type> AS ( <expr> )
);

-- ALTER TABLEで後から追加
ALTER TABLE <table> ADD COLUMN <col_name> <col_type> AS ( <expr> );

ViewのSELECT句で実現していた派生列と似た目的を果たしますが、テーブル自体に定義されるため、別途Viewを作成・管理する必要がありません。

制限事項

2026年6月6日時点での制限事項・注意点をまとめます。

仮想列の式で使えないもの:

  • 非決定的関数: RANDOM()CURRENT_TIMESTAMPCURRENT_DATEUUID_STRING など、実行のたびに異なる値を返す関数
  • 集計関数: SUMAVGCOUNT など。仮想列は行単位で評価されるため集計不可
  • ウィンドウ関数: OVER 句を使う関数
  • サブクエリ: ネストした SELECT
  • ユーザー定義関数(UDF): SQL、JavaScript、外部UDFを問わず不可
  • バインド変数: ?:1:value などのバインドパラメータ
  • セッション変数: SET で設定した変数
  • 位置指定カラム参照: $1$2 などの参照(外部テーブルの仮想列では $1 擬似列は許可)
  • DEFAULT値を持つ列の参照: DEFAULT値が設定された列は仮想列の式から参照できない。また、仮想列自体にDEFAULT値は設定できない

その他の制限:

  • NOT NULL および CHECK 制約は設定できない
  • 仮想列はテーブルのクラスタリングキーには設定できない
  • 仮想列が参照している列(ベース列・仮想列どちらも)は DROP COLUMN できない
  • 仮想列から他の仮想列を参照する場合は、参照される仮想列が先に定義されている必要がある(前方参照は不可)
  • データ型の互換性ルールがあり、宣言した型が式の推論型と互換性がある必要がある(数値のスケール、文字列の長さ、タイムスタンプの精度など)

既知の問題(Known Issue):

  • COALESCE など NULL を非 NULL に変換する式を仮想列に定義し、外部結合(LEFT JOIN)で使用すると、結合が一致しない保存側の行でも非 NULL 値が返る場合がある。ワークアラウンドは「仮想列の式に COALESCE を入れず、SELECT 句側で適用する」です

事前準備

データベース・スキーマ・ウェアハウスの作成

今回の検証用に、データベース・スキーマ・ウェアハウスを作成します。既存のものを使う場合は適宜置き換えてください。

USE ROLE SYSADMIN;

-- ウェアハウス作成
CREATE WAREHOUSE IF NOT EXISTS VIRTUAL_COL_WH
  WAREHOUSE_SIZE = 'XSMALL'
  AUTO_SUSPEND   = 60
  AUTO_RESUME    = TRUE;

-- データベース・スキーマ作成
CREATE DATABASE IF NOT EXISTS VIRTUAL_COL_DB;
CREATE SCHEMA IF NOT EXISTS VIRTUAL_COL_DB.PUBLIC;

-- 作業コンテキストを設定
USE WAREHOUSE VIRTUAL_COL_WH;
USE DATABASE VIRTUAL_COL_DB;
USE SCHEMA PUBLIC;

サンプルテーブルの作成・データ投入

ECサイトの注文データを想定したサンプルテーブル orders_raw を作成し、検証データを投入します。

CREATE OR REPLACE TABLE orders_raw (
  order_id      INT,
  customer_name STRING,
  email         STRING,
  order_date    DATE,
  product_name  STRING,
  quantity      INT,
  unit_price    NUMBER(10, 2)
);
検証用データのINSERT文(クリックで展開)
INSERT INTO orders_raw
  (order_id, customer_name, email, order_date, product_name, quantity, unit_price)
VALUES
  (1,  '山田太郎',   'taro.yamada@example.com',        '2026-01-05', 'ワイヤレスマウス',           2,  2500.00),
  (2,  '鈴木花子',   'hanako.suzuki@dev.co.jp',         '2026-01-07', 'メカニカルキーボード',       1, 12800.00),
  (3,  '佐藤次郎',   'jiro.sato@mail.example.org',      '2026-01-10', 'USBハブ',                   3,  1980.00),
  (4,  '田中美咲',   'misaki.tanaka@example.com',       '2026-01-12', 'ノートPC用スタンド',         1,  4500.00),
  (5,  '中村健一',   'kenichi.nakamura@company.jp',     '2026-01-15', 'HDMI変換アダプタ',           5,   890.00),
  (6,  '渡辺優子',   'yuko.watanabe@example.com',       '2026-01-18', 'ウェブカメラ',               1,  8900.00),
  (7,  '伊藤誠',     'makoto.ito@dev.co.jp',            '2026-01-20', 'ワイヤレスマウス',           1,  2500.00),
  (8,  '加藤由美',   'yumi.kato@mail.example.org',      '2026-01-22', 'SSDポータブル',             2, 15800.00),
  (9,  '吉田孝',     'takashi.yoshida@example.com',     '2026-01-25', 'キーボード用リストレスト',   1,  2200.00),
  (10, '山本裕美子', 'yumiko.yamamoto@company.jp',      '2026-01-28', 'LEDデスクライト',           1,  5500.00),
  (11, '松本大輔',   'daisuke.matsumoto@example.com',   '2026-02-02', 'メカニカルキーボード',       2, 12800.00),
  (12, '井上さくら', 'sakura.inoue@dev.co.jp',          '2026-02-05', 'ノートPC用スタンド',         2,  4500.00),
  (13, '木村朝子',   'asako.kimura@mail.example.org',   '2026-02-08', 'USBハブ',                   1,  1980.00),
  (14, '林拓也',     'takuya.hayashi@example.com',      '2026-02-10', 'HDMI変換アダプタ',           3,   890.00),
  (15, '斎藤倫子',   'michiko.saito@company.jp',        '2026-02-12', 'SSDポータブル',             1, 15800.00),
  (16, '清水宏',     'hiroshi.shimizu@example.com',     '2026-02-15', 'ウェブカメラ',               2,  8900.00),
  (17, '山口美奈',   'mina.yamaguchi@dev.co.jp',        '2026-02-18', 'LEDデスクライト',           3,  5500.00),
  (18, '池田勝',     'masaru.ikeda@mail.example.org',   '2026-02-20', 'ワイヤレスマウス',           4,  2500.00),
  (19, '橋本あかり', 'akari.hashimoto@example.com',     '2026-02-22', 'キーボード用リストレスト',   2,  2200.00),
  (20, '石川裕太',   'yuta.ishikawa@company.jp',        '2026-02-25', 'メカニカルキーボード',       1, 12800.00),
  (21, '前田咲良',   'sakura.maeda@example.com',        '2026-03-01', 'ノートPC用スタンド',         3,  4500.00),
  (22, '藤田翔',     'sho.fujita@dev.co.jp',            '2026-03-04', 'SSDポータブル',             2, 15800.00);

22件のデータが投入されていればOKです。

2026-06-06_23h13_55

試してみた

1. 基本:算術演算で仮想列を作る

まず最もシンプルなパターンとして、quantity × unit_price で合計金額を算出する仮想列 total_priceALTER TABLE で追加します。

ALTER TABLE orders_raw
  ADD COLUMN total_price NUMBER(12, 2) AS ((quantity * unit_price)::NUMBER(12, 2));

追加後にSELECTして確認します。

SELECT order_id, customer_name, quantity, unit_price, total_price
FROM orders_raw
ORDER BY order_id
LIMIT 5;

total_pricequantity × unit_price の計算結果が表示されていればOKです。テーブルに値は保存されておらず、クエリのたびに計算されています。

2026-06-06_23h18_00

2. 仮想列を仮想列から参照する(チェーン定義)

続いて、先ほど定義した仮想列 total_price を参照する形で、消費税額 tax_amount(10%)を計算する仮想列を追加します。

ALTER TABLE orders_raw
  ADD COLUMN tax_amount NUMBER(12, 2) AS ((total_price * 0.1)::NUMBER(12, 2));
SELECT order_id, total_price, tax_amount
FROM orders_raw
ORDER BY order_id
LIMIT 5;

tax_amounttotal_price × 0.1 の値になっていればOKです。このように、先に定義した仮想列を参照する形で別の仮想列を定義できます。

2026-06-06_23h19_03

3. 文字列関数で派生列を作る

次に、文字列操作の例として、email 列からメールドメインを抽出する仮想列 email_domain を追加します。POSITION@ の位置を特定し、そこより後の文字列を SUBSTR で取り出します。

ALTER TABLE orders_raw
  ADD COLUMN email_domain STRING AS (SUBSTR(email, POSITION('@', email) + 1));
SELECT customer_name, email, email_domain
FROM orders_raw
ORDER BY order_id
LIMIT 5;

email_domain 列に example.comdev.co.jp などのドメイン部分が表示されていればOKです。SUBSTRPOSITION のような決定的な組み込み文字列関数であれば仮想列の式に使用できます。

2026-06-06_23h19_50

4. CASE 式で分類列を作る

CASE 式を使って、合計金額の金額帯を分類する仮想列 price_tier を追加します。total_price(仮想列)を参照して金額区分を算出します。

ALTER TABLE orders_raw
  ADD COLUMN price_tier STRING AS (
    CASE
      WHEN total_price >= 20000 THEN 'high'
      WHEN total_price >= 5000  THEN 'mid'
      ELSE                           'low'
    END
  );
SELECT order_id, total_price, price_tier
FROM orders_raw
ORDER BY total_price DESC;

total_price の金額に応じて high / mid / low に分類されていればOKです。

2026-06-06_23h20_34

5. DESC TABLE / SHOW COLUMNS で仮想列を識別する

テーブルの定義を確認してみます。DESC TABLE を使うと KIND 列で通常の列か仮想列かを識別できます。

DESC TABLE orders_raw;

通常の列は KINDCOLUMN、仮想列は KINDVIRTUAL と表示され、EXPRESSION 列に定義した式が入っていればOKです。

2026-06-06_23h21_54

SHOW COLUMNS でも同様の情報を確認できます。こちらは kind 列が VIRTUAL_COLUMNexpression 列に式が入ります。

SHOW COLUMNS IN TABLE orders_raw;

2026-06-06_23h24_12

6. CTAS で仮想列付きテーブルを作る

CREATE TABLE AS SELECT(CTAS) でも仮想列付きテーブルを作成できます。仮想列は SELECT 句の列数にカウントされないため、非仮想列の数だけ SELECT に指定すればOKです。

商品カテゴリマスタと orders_raw を JOIN して、カテゴリ名と税込み価格(仮想列)を持つ集計テーブルを作ります。

まず、商品カテゴリマスタを作成します。

CREATE OR REPLACE TABLE product_categories (
  product_name STRING,
  category     STRING
);

INSERT INTO product_categories VALUES
  ('ワイヤレスマウス',         'ポインティングデバイス'),
  ('メカニカルキーボード',     '入力デバイス'),
  ('USBハブ',                 '周辺機器'),
  ('ノートPC用スタンド',       'アクセサリ'),
  ('HDMI変換アダプタ',         '変換器'),
  ('ウェブカメラ',             '映像機器'),
  ('SSDポータブル',           'ストレージ'),
  ('キーボード用リストレスト', 'アクセサリ'),
  ('LEDデスクライト',          '照明');

次に、CTASで仮想列 tax_included_price(税込み価格)付きのテーブルを作成します。

CREATE OR REPLACE TABLE orders_with_category (
  order_id           INT,
  customer_name      STRING,
  product_name       STRING,
  category           STRING,
  total_price        NUMBER(12, 2),
  tax_included_price NUMBER(12, 2) AS ((total_price * 1.1)::NUMBER(12, 2))
)
AS SELECT o.order_id, o.customer_name, o.product_name, c.category, o.total_price
   FROM orders_raw o
   LEFT JOIN product_categories c ON o.product_name = c.product_name;
SELECT * FROM orders_with_category ORDER BY order_id LIMIT 5;

tax_included_pricetotal_price × 1.1 の税込み金額が表示されていればOKです。CTASでも問題なく仮想列を定義できます。

2026-06-06_23h26_06

7. 禁止される式を試してエラーを確認する

仮想列で使用できない式を実際に試し、エラーメッセージを確認します。

非決定的関数(RANDOM)

ALTER TABLE orders_raw
  ADD COLUMN random_val INT AS (RANDOM());

2026-06-06_23h27_16

NOT NULL 制約

ALTER TABLE orders_raw
  ADD COLUMN total_price2 NUMBER(12, 2) AS ((quantity * unit_price)::NUMBER(12, 2)) NOT NULL;

2026-06-06_23h27_35

依存列の DROP COLUMN

仮想列が依存している列を DROP COLUMN しようとするとブロックされます。

ALTER TABLE orders_raw DROP COLUMN quantity;

2026-06-06_23h28_04

最後に

Snowflake の Virtual Columns(仮想列)を複数のパターンで試してみました。

これまで「テーブル+View」のペアで実現していた計算列をテーブル本体に統合でき、管理するオブジェクト数を減らせるのは大きなメリットだと感じました。ストレージコストも追加発生しないため、積極的に使っていける機能だと思います。

一方で、使用できない式の種類(非決定的関数・集計・UDFなど)、NOT NULL / CHECK 制約の非対応、COALESCE × LEFT JOIN の既知問題など、押さえておくべき制限は多めのため注意は必要です。

GAになったばかりの機能なので、今後の改善にも期待したいところです。ぜひ試してみてください。


Snowflakeの導入支援はクラスメソッドに!

クラスメソッドでは Snowflake の導入を支援しております。
製品の詳細や支援の内容についてお気軽にお問い合わせください。

Snowflakeの詳細を見る

この記事をシェアする

関連記事