在 SSMS V22 上發現沒有辦法使用內建匯出資料功能,查發現原來 SSMS V22 的商業智慧功能並不是預設安裝的,安裝起來就可以使用
SSMS V22 上匯入、匯出資料反灰無法使用
在 Visual Studio Installer 內把 [商業智慧] 安裝起來,內含 SQL Server Integration Services (SSIS),就可以正常使用
~楓花雪岳~
星期一, 10月 05, 2026
星期三, 9月 23, 2026
[SSRS] 巢狀清單
以前一直認為清單 (List) 內無法放置資料表 (Tablix),在報表設計階段雖然 Tablix 可以放進 List 內,但只要報表一執行就會出現錯誤
後來發現因為是 List 和 Tablix 資料列群組 (Row Group) 內預設有個明細資料 (Detail Row),因為兩個 Detail Row 才導致報表無法 Render
該筆記就是要把 List 內 Detail Row 轉為 Group Row 當成報表頭,List 內的 Tablix Detail Row 來呈現資料當成報表身,以此來呈現 AdventureWorks2025 內的訂單報表 (Master-Detail)
AsventureWorks2025 資料存取
只抓取兩筆訂單資料
兩種方式有可以達到
Detail Row => 滑鼠右鍵 => 群組屬性 群組屬性 => 一般內進行修改
報表執行結果
後來發現因為是 List 和 Tablix 資料列群組 (Row Group) 內預設有個明細資料 (Detail Row),因為兩個 Detail Row 才導致報表無法 Render
該筆記就是要把 List 內 Detail Row 轉為 Group Row 當成報表頭,List 內的 Tablix Detail Row 來呈現資料當成報表身,以此來呈現 AdventureWorks2025 內的訂單報表 (Master-Detail)
AsventureWorks2025 資料存取
只抓取兩筆訂單資料
SELECT
soh.SalesOrderNumber,
soh.OrderDate,
sod.OrderQty,
sod.UnitPrice,
sod.LineTotal,
p.ProductNumber,
p.[Name] AS ProductName
FROM Sales.SalesOrderHeader AS soh
INNER JOIN Sales.SalesOrderDetail AS sod ON soh.SalesOrderID = sod.SalesOrderID
INNER JOIN Production.[Product] AS p ON sod.ProductID = p.ProductID
WHERE soh.SalesOrderID IN (43659 , 75123)
把 List Detail Row 轉為 Group Row
兩種方式有可以達到
- 直接把 Detail Row 轉為 Group Row
- 在 Detail Row 上建立父群組後,再把 Detail Row 刪除
Detail Row => 滑鼠右鍵 => 群組屬性 群組屬性 => 一般內進行修改
- 名稱:修正為 grpSalesOrderNumber,建議要修改,避免還是維持 Detail 預設名稱,但實際是進行 Group 行為
- 群組運算式:把 SalesOrderNumber 欄位加入群組對象
報表執行結果
星期四, 9月 17, 2026
[GAS] Gemini API
在 GAS 內呼叫 Gemini 來進行應用,該筆記分兩部分
申請 API Key
進入 Google AI Studio 建立 API Key,步驟依序為
Google AI Studio => MANAGE => Dashboard
PROJECT => API Keys
API Keys 內右上角 [Create API Key]
Create a new Key 頁面,專案選項可以在該畫面新增、選擇現有或是匯入
API Key Detail
呼叫 Gemini
重點
官方 response json 範例
- Google AI Studio 申請 API Key
- 在 GAS 內透過 UrlFetchApp.fetch 呼叫 Gemini
申請 API Key
進入 Google AI Studio 建立 API Key,步驟依序為
Google AI Studio => MANAGE => Dashboard
PROJECT => API Keys
API Keys 內右上角 [Create API Key]
Create a new Key 頁面,專案選項可以在該畫面新增、選擇現有或是匯入
API Key Detail
呼叫 Gemini
重點
- 官方文件-互動 API 內有使用模型可以參考,該範例是使用 gemini-3.8-flash
- Gemini API 內有 response json 範例可以參考,看完後才能比較理解 getGeminiText() 內是在拆解什麼
官方 response json 範例
{
"id": "v1_ChdPU0F4YWFtNkFwS2kxZThQZ05lbXdROBIXT1NBeGFhbTZBcEtpMWU4UGdOZW13UTg",
"model": "gemini-3-flash-preview",
"status": "completed",
"object": "interaction",
"created": "2025-11-26T12:25:15Z",
"updated": "2025-11-26T12:25:15Z",
"steps": [
{
"type": "model_output",
"content": [
{
"type": "text",
"text": "I'm doing great, thank you for asking! How can I help you today?"
}
]
}
]
}
code.gs
const GEMINI_MODEL = "gemini-3.8-flash";
/**
* 呼叫 Gemini API
*
* @param {string} prompt 使用者輸入的問題
* @return {string} Gemini 回覆
*/
function askGemini(prompt) {
// 1. 取得 API Key
const apiKey = PropertiesService
.getScriptProperties()
.getProperty("GEMINI_API_KEY");
if (!apiKey) {
throw new Error("找不到 GEMINI_API_KEY,請先設定 Script Properties。");
}
// 2. Gemini API URL
const url = "https://generativelanguage.googleapis.com/v1beta/interactions";
// 3. 要傳給 Gemini 的資料
const payload = {
model: GEMINI_MODEL,
input: prompt
};
// 4. HTTP Request 設定
const options = {
method: "post",
contentType: "application/json",
headers: {
"x-goog-api-key": apiKey
},
payload: JSON.stringify(payload),
muteHttpExceptions: true
};
// 5. 呼叫 Gemini
const response = UrlFetchApp.fetch(url, options);
// 6. 取得 HTTP Status Code
const statusCode = response.getResponseCode();
// 7. 取得 JSON 字串
const responseText = response.getContentText();
// 8. API 發生錯誤
if (statusCode < 200 || statusCode >= 300) {
throw new Error(
"Gemini API 呼叫失敗\n" +
"HTTP Status: " + statusCode + "\n" +
responseText
);
}
// 9. JSON → JavaScript Object
const data = JSON.parse(responseText);
// 10. 取得 Gemini 回覆文字
const result = getGeminiText(data);
return result;
}
/**
* 從 Gemini Interaction Response 取得文字
*/
function getGeminiText(data) {
if (!data.steps) {
throw new Error("Gemini Response 沒有 steps 資料。");
}
let result = [];
data.steps.forEach(step => {
if (step.type !== "model_output") {
return;
}
if (!step.content) {
return;
}
step.content.forEach(content => {
if (content.type === "text" && content.text) {
result.push(content.text);
}
});
});
if (result.length === 0) {
throw new Error("Gemini 沒有回傳文字內容。");
}
return result.join("\n");
}
測試
/**
* 測試 Gemini API
*/
function testGemini() {
const prompt = "請用 100 個字介紹高雄。";
const result = askGemini(prompt);
console.log(result);
}
星期六, 9月 05, 2026
星期三, 9月 02, 2026
[GAS] 自訂側欄
用 agy 寫網頁應用程式時,AI 自行在 Google Sheet 內設計 sidebar 來呈現網頁應用程式,那就來筆記 sidebar 效果囉
code.gs
gs code 內的 OnOpen() 在 Google Sheet 開啟時會有自訂功能
透過 sidebar 開啟 sitebar.html 網頁
code.gs
/**
* 當試算表開啟時,自動新增自訂功能表
*/
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('🛠️ 自訂功能')
.addItem('開啟側邊欄', 'showSidebar')
.addToUi();
}
/**
* 載入 Sidebar.html 並在右側開啟側邊欄
*/
function showSidebar() {
const htmlOutput = HtmlService.createHtmlOutputFromFile('Sidebar')
.setTitle('側邊欄說明')
.setWidth(300); // 側邊欄預設寬度通常為 300px
SpreadsheetApp.getUi().showSidebar(htmlOutput);
}
sidebar.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<!-- 引入 Google Material/標準風格樣式簡潔化介面 -->
<link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons1.css">
<style>
body {
padding: 12px;
font-family: Arial, sans-serif;
color: #333;
line-height: 1.5;
}
.card {
background-color: #f8f9fa;
border: 1px solid #dadce0;
border-radius: 8px;
padding: 12px;
margin-bottom: 12px;
}
h2 {
margin-top: 0;
color: #1a73e8;
font-size: 18px;
}
ul {
padding-left: 20px;
margin: 8px 0;
}
li {
margin-bottom: 6px;
}
.footer {
font-size: 12px;
color: #70757a;
margin-top: 20px;
border-top: 1px solid #eee;
padding-top: 8px;
}
</style>
</head>
<body>
<h2>📌 側邊欄功能說明</h2>
<div class="card">
<p>歡迎使用自訂側邊欄!這是一個透過 <code>HtmlService</code> 建立的客製化介面。</p>
</div>
<h3>主要特色</h3>
<ul>
<li><strong>即時操作:</strong> 不影響目前試算表的編輯流程。</li>
<li><strong>雙向溝通:</strong> 可透過 <code>google.script.run</code> 與後端 Apps Script 進行資料交換。</li>
<li><strong>豐富介面:</strong> 支援 HTML5、CSS3 及 JavaScript 各種前端框架。</li>
</ul>
<div class="footer">
💡 提示:點擊右上角「✕」即可隨時關閉此側邊欄。
</div>
</body>
</html>
gs code 內的 OnOpen() 在 Google Sheet 開啟時會有自訂功能
透過 sidebar 開啟 sitebar.html 網頁
訂閱:
文章 (Atom)


















