首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> SQL Server 資料庫 >> 關於SqlServer

SQL Server 動態行轉列實戰:多表級聯 PIVOT 顯示每日商品點擊統計

     編輯:關於SqlServer

業務場景

  • A 表:商品點擊日志 (Time, AId...)
  • B 表:商品信息 (AId, Title...)
  • 需求:輸出每日所有商品點擊量列表,含總數,各商品為列,按日期倒序。

動態 SQL + 條件聚合 實現方案

DECLARE @sql NVARCHAR(MAX) = N'';

-- 1. 構建 SELECT 列:固定列 + 動態商品列
SET @sql = N'SELECT 
    CONVERT(VARCHAR(10), a.[Time], 23) AS [日期],
    COUNT(b.Title) AS [總數]';

-- 2. 從 B 表獲取所有商品名,動態拼接 SUM(CASE...) 列
SELECT @sql += N', SUM(CASE b.Title WHEN ' + QUOTENAME(Title, '''') + N' THEN 1 ELSE 0 END) AS ' + QUOTENAME(Title)
FROM (SELECT DISTINCT Title FROM B) AS B;

-- 3. 拼接 FROM/GROUP BY/ORDER BY
SET @sql += N' 
FROM A AS a 
LEFT JOIN B AS b ON a.AId = b.AId
GROUP BY CONVERT(VARCHAR(10), a.[Time], 23)
ORDER BY CONVERT(VARCHAR(10), a.[Time], 23) DESC';

-- 4. 執行動態 SQL
EXEC sp_executesql @sql;

輸出結果示例

日期總數商品1商品2商品3
2013-11-115104
2013-11-10132111

關鍵技術點解析

1. 條件聚合模擬 PIVOT

標準 PIVOT 語法靜態、難維護。使用 SUM(CASE WHEN ... THEN 1 ELSE 0 END) 更靈活,支持動態列名。

2. QUOTENAME 防注入與合法標識符

  • QUOTENAME(Title, ''''):用單引號包裹字符串字面量,防 SQL 注入。
  • QUOTENAME(Title):用方括號包裹列別名,支持含空格/特殊字符的商品名。

3. sp_executeshl 執行動態 SQL

優於 EXEC(@sql),支持參數化,計劃復用率更高。

4. 日期標準化分組

CONVERT(VARCHAR(10), Time, 23) → YYYY-MM-DD 格式,保證分組正確且排序友好。

進階增強:帶參數、分頁、多維度

CREATE PROCEDURE dbo.sp_DailyProductClickStats
    @StartDate DATE = NULL,
    @EndDate   DATE = NULL,
    @PageIndex INT = 1,
    @PageSize  INT = 20
AS
BEGIN
    SET NOCOUNT ON;
    SET @StartDate = ISNULL(@StartDate, DATEADD(DAY, -30, CAST(GETDATE() AS DATE)));
    SET @EndDate   = ISNULL(@EndDate, CAST(GETDATE() AS DATE));

    DECLARE @sql NVARCHAR(MAX) = N'', @cols NVARCHAR(MAX) = N'';

    -- 動態構建商品列
    SELECT @cols += N', SUM(CASE b.Title WHEN ' + QUOTENAME(Title, '''') + N' THEN 1 ELSE 0 END) AS ' + QUOTENAME(Title)
    FROM (SELECT DISTINCT Title FROM B) AS B;

    -- 主查詢 (使用 CTE 分頁)
    SET @sql = N'
    WITH DailyStats AS (
        SELECT 
            CONVERT(VARCHAR(10), a.[Time], 23) AS [Date],
            COUNT(b.Title) AS [Total]' + @cols + N'
        FROM A AS a 
        LEFT JOIN B AS b ON a.AId = b.AId
        WHERE a.[Time] >= @StartDate AND a.[Time] < DATEADD(DAY, 1, @EndDate)
        GROUP BY CONVERT(VARCHAR(10), a.[Time], 23)
    )
    SELECT * FROM DailyStats
    ORDER BY [Date] DESC
    OFFSET (@PageIndex - 1) * @PageSize ROWS
    FETCH NEXT @PageSize ROWS ONLY;';

    EXEC sp_executesql @sql, 
        N'@StartDate DATE, @EndDate DATE, @PageIndex INT, @PageSize INT',
        @StartDate, @EndDate, @PageIndex, @PageSize;
END

性能優化建議

  • 索引:A 表建立 (Time, AId) 覆蓋索引;B 表 AId 有索引。
  • 商品數量:若商品種類超過 100+,列過多導致查詢緩慢、結果集過寬。建議改為「長表」格式 (日期, 商品, 點擊數) 由應用層/前端透視。
  • 緩存:每日統計數據變動小,可物化視圖/定時任務預計算存入報表表,查詢直接讀報表表。
  • 分區:大量日志表按時間分區 (Partition),歷史分區唯讀,壓縮存儲。

替代方案對比

方案優點缺點適用場景
動態 SQL + 條件聚合列名動態、邏輯可控、無需 PIVOT 語法限制需動態執行、計劃難復用、列過多性能差列數 < 50、臨時分析、報表導出
靜態 PIVOT語法標準、計劃可復用列名硬編碼、新增商品需改代碼固定維度、高頻調用
應用層透視 (長表輸出)SQL 簡單、列數固定、擴展性強需前端/中間層處理商品多、前端框架成熟 (React/Vue Table/Pivot)
物化視圖/報表表查詢極快、不佔業務資源數據有延遲、維護成本核心儀表盤、高併發查詢
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved