需一次更新書本主檔與多筆章節明細(N < 1000)。若逐筆呼叫 Stored Procedure,1000 並發將產生 100 萬次資料庫往返,嚴重拖垮效能。解決方案:將明細序列化為 XML 字串,單次呼叫 SP 解析批次寫入。
CREATE PROCEDURE UP_Book_Insert
@BookId INT,
@ChapterXml XML
AS
BEGIN
-- 使用暫存表或表值變數接收解析結果
CREATE TABLE #ChapterTmp (
ChapterId INT,
ChapterName VARCHAR(255),
Price INT
)
INSERT #ChapterTmp
SELECT
X.C.value('Id[1]', 'int') AS ChapterId,
X.C.value('Name[1]', 'varchar(255)') AS ChapterName,
X.C.value('Price[1]', 'int') AS Price
FROM @ChapterXml.nodes('Chapter') AS X(C) -- X(C) 為必要的命名空間別名
-- 批次寒入目標表
INSERT INTO tbChapter (BookId, ChapterId, ChapterName, Price)
SELECT @BookId, ChapterId, ChapterName, Price FROM #ChapterTmp
END
EXEC UP_Book_Insert 10000,
'268 第268章 100
273 第273章 100
275 第275章 100 '
public static string FormatXmlInfo(List<ChapterInfo> list)
{
if (list == null || list.Count == 0) return String.Empty;
var sb = new StringBuilder();
foreach (var info in list)
{
sb.AppendFormat("<Chapter><Id>{0}</Id><Name>{1}</Name><Price>{2}</Price></Chapter>",
info.ChapterId, info.ChapterName, info.Price);
}
return sb.ToString();
}
// 呼叫 SP
using (var cmd = new SqlCommand("UP_Book_Insert", conn) { CommandType = CommandType.StoredProcedure })
{
cmd.Parameters.AddWithValue("@BookId", bookId);
cmd.Parameters.AddWithValue("@ChapterXml", xmlString); // DbType.String 即可
cmd.ExecuteNonQuery();
}
CREATE TYPE ... AS TABLE 配合 `SqlParameter.TypeName`,比 XML 解析更快、型別安全。| 方案 | 資料庫往返 | 網路封包 | 解析開銷 | 適用版本 |
|---|---|---|---|---|
| 逐筆 SP | N 次 | N 個 | 無 | 所有版本 |
| XML 批次 | 1 次 | 1 個 | 低 (XQuery) | 2005+ |
| TVP 批次 | 1 次 | 1 個 | 極低 (原生) | 2008+ |
| BULK INSERT | 1 次 | 串流 | 最低 | 所有版本 |
實測 1000 筆章節:XML 批次約 50-100ms,逐筆約 3-5 秒(含網路延遲)。