Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Azure SQL Datavaseでベクター検索を活用しよう

Azure SQL Datavaseでベクター検索を活用しよう

2026/1/24(土)に開催された .NETラボ 勉強会 2026年1月( https://dotnetlab.connpass.com/event/374748/ ) に参加した時に当日受付LTで発表した資料です。 #dotnetlab

Avatar for なかしょ

なかしょ

January 24, 2026
Tweet

More Decks by なかしょ

Other Decks in Technology

Transcript

  1. 自己紹介 • なかしょ(中島進也) @nakasho_dev • 所属:NTTテクノクロス株式会社 デジタルトランスフォーメーション事業部 • 業務:MaaS関連のスマートフォンアプリ開発担当 •

    趣味: ➢妻とモンハンデート ➢IT関連の勉強会(主にモバイル系 or アジャイル系 or Microsoft系) ➢技術コミュニティの運営スタッフ ✓eXtreme Programming Japan User Group(XPJUG) 2019〜 ✓TDD BootCamp Online (TDDBC) 2020~ ※本資料は私個人の意見であり、所属企業・部門見解を代表するものではありません。
  2. こういう検索にはN-gramがよく使われる 6 TOUKYOU TOK TOUKYOU OUK TOUKYOU UKY TOUKYOU KYO

    TOUKYOU YOU インデックス側(TOUKYOU) TOKYO TOK TOKYO OKY TOKYO KYO 検索クエリ(TOKYO) 共通トークン: KYO
  3. N-gramのメリット・デメリット 7 • メリット(このケース) ➢ローマ字表記揺れに強制的に耐性がつく ➢設定が比較的簡単 ➢日本語・英数字混在に強い ➢サジェストとの相性が良い • デメリット

    ➢ノイズが爆増する ✓京都(KYOTO)、共済ビル(KYOSAI) ✓3-gramの場合、3文字あっていればヒットする ➢スコア設計が地獄 ✓関連が高いものと、たまたま部分一致しただけのものをスコアで分離する必要がある ➢インデックスサイズ肥大 ✓N-gramは文字数×Nの分だけトークンが増え、検索速度・メモリに影響
  4. 8

  5. 試したデータ 9 • 全国の駅データCSV:9,423件 ➢駅データ.jp(https://www.ekidata.jp/)のデータを加工して使用 ➢Embedding(数値ベクトル)に変換しやすいようにまとめたcombined_txt 列名 入力例 駅コード 1110101

    駅名 函館 駅名かな はこだて 駅名ローマ字 hakodate 住所 北海道函館市若松町12-13 緯度 140.726413 経度 41.773709 Combined_text 函館 (はこだて) [hakodate] 北海道函館市若松町12-13
  6. 開発環境 10 • Kotlin 2.2.21 • Spring Boot 4.0.1 •

    Spring Data JPA • Azure SQL Database (Vector Support) • Azure OpenAI ➢モデル:text-embedding-3-small(1536次元)
  7. テキストからベクトル埋め込みを生成する 13 /** * * @param text 埋め込みを生成するテキスト(例: "東京 とうきょう

    tokyo") * @return 1536次元の浮動小数点数リスト */ fun generateEmbedding(text: String): List<Float> { // テキストをリストでラップ(API は複数テキストの一括処理にも対応) val embeddingsOptions = EmbeddingsOptions(listOf(text)) // Azure OpenAI API を呼び出してベクトル埋め込みを取得 val embeddings = client.getEmbeddings(embeddingDeployment, embeddingsOptions) // レスポンスの最初の埋め込みベクトルを返す // data.first().embedding は List<Double> なので、Float に変換 return embeddings.data.first().embedding.map { it } }
  8. VECTOR型の登録方法 14 ベクトル埋め込みを SQL Server に格納可能な文字列形式に変換する 【Azure SQL Database の

    VECTOR 型への格納方法】 VECTOR 型にデータを挿入するには、JSON 配列形式の文字列を CAST 関数で変換する必要がある 例: ```sql INSERT INTO table (embedding_column) VALUES (CAST('[0.123, -0.456, 0.789, ...]' AS VECTOR(1536))) ```
  9. VECTOR型を利用した検索方法 16 ```sql SELECT s.station_cd, s.station_name, s.station_name_k, s.station_name_r, s.address, s.lon,

    s.lat, s.combined_text, (1 - VECTOR_DISTANCE('cosine', s.name_embedding, CAST(:embedding AS VECTOR(1536)))) AS score FROM stations_with_vector s WHERE s.name_embedding IS NOT NULL ORDER BY score DESC OFFSET 0 ROWS FETCH NEXT :limit ROWS ONLY ```
  10. TOKYOで検索して「東京」がヒット 17 { "stationCd": 1130101, "stationName": "東京", "stationNameK": "とうきょう", "stationNameR":

    "toukyou", "address": "東京都千代田区丸の内一丁目", "lon": 139.766103, "lat": 35.681391, "combinedText": "東京 (とうきょう) [toukyou] 東京都千代田区丸の 内一丁目", "score": 0.8853111863136292 },
  11. 東京でヒットした検索結果 18 東京 (とうきょう) [toukyou] 東京都千代田区丸の内一 東京テレポート (とうきょうてれぽーと) [toukyouterepooto] 江東区青海1-2

    東京国際クルーズターミナル (とうきょうこくさいくるーずたーみなる) [toukyoukokusaikuruuzutaaminaru] 江東区青海1-1-49 東京ビッグサイト (とうきょうびっぐさいと) [toukyoubiggusaito] 江東区有明 三-6-15 田京 (たきょう) [takyou] 伊豆の国市田京 渋谷 (しぶや) [shibuya] 東京都渋谷区道玄坂一-4-1 徳島 (とくしま) [tokushima] 徳島市寺島本町西2
  12. osakiでヒットした検索結果 19 大崎 (おおさき) [oosaki] 東京都品川区大崎一-21-4 大阪 (おおさか) [oosaka] 大阪市北区梅田3

    上岡 (かみおか) [kamioka] 佐伯市上岡区上岡 岡 (おか) [oka] 角田市岡白岩 岡崎 (おかざき) [okazaki] 岡崎市羽根町東荒子 小佐野 (おさの) [osano] 釜石市小佐野町1 早岐 (さき) [saki] 佐世保市早岐1
  13. n-gramとベクター検索の比較 20 観点 n-gram ベクター検索(Azure SQL) 表記揺れ耐性 強い 非常に強い 意味理解

    なし あり ノイズ 多い 少ない プレフィックス(入力途 中) 強い 弱い 厳密制御 簡単 工夫が必要 サジェスト相性 良 条件付きで良
  14. 21