星期日, 6月 28, 2026

[SSRS] 框線

之前都是在屬性視窗內設定框線,這次無意中進入 IDE 內進行設定,發現原來框線 IDE 設定是有操作順序,以矩形為例來記錄

框線 IDE 設定

矩形 => 滑鼠右鍵 => 矩形屬性 => 框線
上圖橘框內的樣式、寬度、色彩,要先設定後,選擇黃框外框會套用該設定,而黃框外框選擇 [無] 後離開的話,會一併恢復橘框內的三個設定預設值。

屬性視窗設定

點選控件後理論上右側屬性視窗會自動出現,或是按 F4 快捷鍵
屬性視窗內設定會比較直覺,設定就是直接套用該效果

星期六, 6月 27, 2026

[SSRS] 在頁首或頁尾中顯示每筆資料

在 SSRS 報表內,資料來源為 DataSet 的話,要顯示每一筆資料必須把資料放在報表主體 (body) 內搭配清單 (List) 或 Tablix 等控件來顯示,而頁首、頁尾只能插入文字方塊 (TextBox)、線條、矩形、影像 (Image) 這四種控件,直接從 DataSet 字串欄位拉進頁首、頁尾的話,文字方塊值會有彙總運算式如下
=First(Fields!Data.Value, "DataSet 名稱")
運算式會自帶 First() 並只顯示第一筆資料的欄位資料,跳至其他分頁的話,該文字方塊不會跟著變化,有嘗試把 First() 移除,SSRS 雖然不會報錯,但仍然維持相同結果。

在官方文章 - 在頁首或頁尾中顯示變數資料 有提到繞道做法,必須在頁首、頁尾上透過 ReportItems (Report 控件) 來顯示 DataSet 內的每一筆資料、圖片

資料來源 DataSet

只塞入兩筆資料為別為 1234567890、ABCDEFGHIJ,在 AP 端是透過 Xing 套件把這兩筆資料轉成 Barcode 並用 byte[] 來儲存
public class ReportModel
{
        public string Data { get; set; }
        public byte[] Barcode { get; set; }
}

報表 layout 設計

黃框為頁首、綠框為 Body

在頁首、頁尾顯示文字

在頁首、頁尾內插入 TextBox 後,值設定值直接指定 Boby 內的 txtData 控件,設定如下
=ReportItems!txtData.Value
從下圖執行結果內的頁碼可以發現,跳至第二筆資料後,直接抓 txtData 控件可以顯示第二筆資料 ABCDEFGHIJ

在頁首、頁尾顯示圖片

ReportItems 是 TextBox 控件集合,不包含 Image 控件,沒有辦法像文字一樣,直接指定控件,所以圖片要特別處理,首先要在 Body 插入 TextBox (命名為 txtHiddenBarcode),並把圖片轉為字串如下
=Convert.ToBase64String(Fields!Barcode.Value)
頁首、頁尾的 Image 控件,則是抓取 ReportItems 的 txtHiddenBarcode 值再透過 Convert.FromBase64String 轉回圖片來顯示
=Convert.FromBase64String(ReportItems!txtHiddenBarcode.Value)
實務上使用會把 txtHiddenBarcode 隱藏起來,畢竟它只是傳遞資料媒介

星期一, 6月 22, 2026

[GAS] Goolge Cloud Logging

Google Apps Script 預設會自動建立一個 Google Cloud 專案 (預設專案),可以變更為自行建立的標準 Google Cloud 專案 (標準專案),一般來說使用預設專案就行,實務上有 Log 保存和查詢需求才會使用標準專案,一旦轉成標準專案後,無法再切回預設專案

GAS 專案 => 專案設定 => Google Cloud Platform (GCP) 專案,就可以看見 GCP 為預設
按下上圖的 [變更專案] 按鈕後會出現標準專案的設定說明

點選步驟 1. 的 [這裡] 連結,會跳轉至 GCP 平台上,建立一個標準專案並取得專案編號
在 GAS 專案內設定 GCP 專案編號,會出現 [如要變更專案,請設定 OAuth 同意畫面。設定 OAuth 同意畫面詳細資料] 的錯誤訊息
按下 [OAuth 同意畫面詳細資料] 連結後,會跳至 GCP 設定畫面
按下 [開始] 進入專案設定
應用程式資訊,請輸入 [應用程式名稱] 和 [使用者支援電子郵件]
目標對象,請選擇 [外部],該筆記 GAS 和 GCP 都是使用相同帳號,就不需要特別去設定測試使用者
聯絡資訊,請輸入電子郵件地址
完成,需要勾選同意相關條款
回到 GAS 選項內就可以把 GCP 專案編號設定上去,從下圖就可以變成標準專案

在 GAS 專案內的 gs Code 同時有 Logger 和 console 輸出並執行
function Log2GCP() {
  Logger.log("Logger.log 輸出文字");
  console.info("console.Info 輸出文字");
}

在執行項目內可以看見剛剛執行留下的 Log

點選上圖的 [前往 CLOUD 紀錄查看] 會自動跳轉至 Logs Exploer 去,不論使用 Logger 或 console 都可以把 log 送上 GCP Logging 去

星期六, 6月 20, 2026

[SQL] UTF-8 定序

SQL Server 2019 功能 - [UTF-8 定序],可以讓 char、varchar 資料型態可以儲存 Unicode

範例說明
USE AdventureWorks2025
GO

DROP TABLE IF EXISTS Utf8Demo

-- 建立測試資料表
CREATE TABLE Utf8Demo (
    ID INT IDENTITY(1,1) PRIMARY KEY,

    -- 一般的 VARCHAR (使用資料庫預設定序,台灣通常是 Chinese_Taiwan_Stroke_CI_AS)
    NormalVarchar VARCHAR(50),
    
    -- 支援 UTF-8 的 VARCHAR (透過指定 _UTF8 結尾的定序)
    Utf8Varchar VARCHAR(50) COLLATE Chinese_Traditional_Stroke_Count_100_CI_AS_SC_UTF8,
    
    -- 傳統支援 Unicode 的 NVARCHAR
    StandardNvarchar NVARCHAR(50)
);

-- 插入測試資料
INSERT INTO Utf8Demo (NormalVarchar, Utf8Varchar, StandardNvarchar)
VALUES 
    ('💩' , '💩' , N'💩') ,  -- UTF-8 varchar 沒有 N
    ('🚀' , N'🚀' , N'🚀') , -- UTF-8 varchar 加上 N
    ('堃' , N'堃' , N'堃');

-- 查詢顯示結果與儲存空間
SELECT 
    NormalVarchar,
    Utf8Varchar,
    StandardNvarchar,
    DATALENGTH(NormalVarchar) AS Normal_Bytes,
    DATALENGTH(Utf8Varchar) AS Utf8_Bytes,
    DATALENGTH(StandardNvarchar) AS Nvarchar_Bytes
FROM Utf8Demo;
從查詢結果可以觀察到
  • Unicode N 符號:可以觀察到 UTF-8 定序 搭配 varchar 欄位情況下,要 insert 進 Unicode,仍然必須加上 N 符號
  • 佔用空間 (Bytes):繁體中文堃在 nvarchar 佔 2 byte、在 UTF-8 定序搭配 varchar 資料型態佔 3 byte,而 Emoji 則是都佔 4 byte

查詢繁體中文 UTF-8 定序

使用 sys.fn_helpcollations 來查詢相關定序
SELECT *
FROM fn_helpcollations()  
WHERE (Name LIKE 'Chinese_Traditional%' OR Name LIKE 'Chinese_Taiwan%')
	AND Name LIKE '%UTF8'

星期三, 6月 17, 2026

[GAS] Log

在 Google App Script 內要記錄 log 的話,可以使用 Loggerconsole。Logger 是 GAS 早期專屬的簡易除錯工具,console 則是現代 JavaScript 原生標準日誌工具且支援日誌層(Severity),有 .log、.info、.warn、.error 可以使用。

簡易範例

輸出一個多層次複雜物件,包含字串、數字、巢狀物件與陣列
function demoObjectParsing() {
  // 多層次複雜物件,包含字串、數字、巢狀物件與陣列
  const orderData = {
    orderId: "A-2026-9981",
    totalAmount: 1700,
    customer: {
      name: "王小明",
      email: "ming@example.com"
    },
    items: [
      { productName: "無線滑鼠", price: 500 },
      { productName: "機械鍵盤", price: 1200 }
    ]
  };

  // 直接輸出差異比較 
  // Logger.log("--- 直接輸出差異比較 ---");
  Logger.log(orderData);
  console.log(orderData);

  // 使用 + 號串接文字 
  // Logger.log("--- 使用 + 號串接文字  ---");
  Logger.log("訂單資料為: " + orderData);
  console.log("訂單資料為: " + orderData); 

  // 使用 JSON.stringify 格式化
  //    第一個參數是物件
  //    第二個參數(取代函數)給 null
  //    第三個參數給 2 (代表縮排 2 格)
  Logger.log("--- JSON.stringify 格式化  ---");
  Logger.log("訂單資料為: \n%s", JSON.stringify(orderData, null, 2));
  console.log(`訂單資料為: 
${JSON.stringify(orderData, null, 2)}`);
}

直接輸出差異比較

Logger 文字輸出會糊成一團,console 會有一個簡易排版存在
使用 + 號串接文字

Logger 和 console 都會輸出 [object Object] 資訊,沒有辦法閱讀輸出內容
使用 JSON.stringify 格式化

避免截圖太長,該截圖只呈現一個結果就好
執行項目

這裡會有每次執行 Logger、console 紀錄的 log 資訊,但該 log 資訊並不會保留很久,官方文件 - Use the Apps Script execution log 說明為
These logs are intended for checks during development and debugging, and don't persist very long.
AI 解釋有明確指出是保留 7 天,但不可考單純紀錄參考