首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計疑難解答

SQL Server 存儲過程多條件降級查詢:IF EXISTS 判斷或 TRY-CATCH 異常捕獲

     編輯:程式設計疑難解答

業務邏輯

三條 SQL 降級查詢:
1. 查詢 Bgoods 表 (本地商品),有數據返回前 20 條 + 總數。
2. 無數據則查 prodh.dbo.G1982 (異庫商品),返回前 20 條 + 總數。
3. 仍無數據則查 Bgoods 不限距離/分類,返回前 20 條。

方案一:IF EXISTS 判斷行數 (推薦、可讀性強、性能好)


CREATE PROCEDURE dbo.usp_GetGoods_Fallback
    @Lng DECIMAL(18,14),
    @Lat DECIMAL(18,14),
    @Keyword NVARCHAR(50),
    @PageSize INT = 20
AS
BEGIN
    SET NOCOUNT ON;
    
    -- 臨時表存儲最終結果
    CREATE TABLE #Result (
        company NVARCHAR(50), code NVARCHAR(50), name NVARCHAR(100),
        hyprice DECIMAL(18,2), comeFrom NVARCHAR(50), finish NVARCHAR(50),
        smallfanwei NVARCHAR(50), distance DECIMAL(18,6),
        rn INT IDENTITY(1,1) -- 行號用於分頁
    );
    
    -- 1. 第一優先級查詢
    INSERT INTO #Result (company, code, name, hyprice, comeFrom, finish, smallfanwei, distance)
    SELECT TOP (@PageSize) 
        a.company, a.code, a.name, b.hyprice, a.comeFrom, 
        ISNULL(a.finish,''), c.smallfanwei,
        dbo.GetDistancefromLnglat(@Lng, @Lat, c.lng, c.lat)/1000.0
    FROM Bgoods a
    JOIN Bgoodsdtl b ON a.code=b.code AND a.company=b.company
    JOIN shopperA c ON a.company=c.SEQ
    WHERE (c.lng BETWEEN @Lng-0.0488 AND @Lng+0.0488) -- 經度範圍約 5km
      AND (c.lat BETWEEN @Lat-0.0449 AND @Lat+0.0449) -- 緯度範圍約 5km
      AND a.KgCN = @Keyword
      AND b.display IN ('1','2','3')
      AND dbo.GetDistancefromLnglat(@Lng, @Lat, c.lng, c.lat)/1000.0 <= 5
    ORDER BY distance;
    
    IF EXISTS (SELECT 1 FROM #Result)
    BEGIN
        SELECT * FROM #Result WHERE rn BETWEEN 1 AND @PageSize;
        SELECT COUNT(*) AS TotalCount FROM #Result;
        DROP TABLE #Result;
        RETURN;
    END
    
    -- 2. 第二優先級查詢 (清空臨時表重用)
    TRUNCATE TABLE #Result;
    INSERT INTO #Result (company, code, name, hyprice, comeFrom, finish, smallfanwei, distance)
    SELECT TOP (@PageSize)
        a.company, a.code, a.oldcode, a.name, b.hyprice, a.comeFrom,
        ISNULL(a.finish,''), NULL, -- 此表無 smallfanwei
        dbo.GetDistancefromLnglat(@Lng, @Lat, c.lng, c.lat)/1000.0
    FROM prodh.dbo.G1982 a
    JOIN prodh.dbo.G1982dtl b ON a.code=b.code
    JOIN shq520.dbo.ghuser c ON a.company=c.SEQ
    WHERE (c.lng BETWEEN @Lng-0.0488 AND @Lng+0.0488)
      AND (c.lat BETWEEN @Lat-0.0449 AND @Lat+0.0449)
      AND a.name LIKE '%' + @Keyword + '%'
      AND b.dhdisplay = '1'
      AND dbo.GetDistancefromLnglat(@Lng, @Lat, c.lng, c.lat)/1000.0 <= 5
    ORDER BY distance;
    
    IF EXISTS (SELECT 1 FROM #Result)
    BEGIN
        SELECT * FROM #Result WHERE rn BETWEEN 1 AND @PageSize;
        SELECT COUNT(*) AS TotalCount FROM #Result;
        DROP TABLE #Result;
        RETURN;
    END
    
    -- 3. 第三優先級查詢 (兜底)
    TRUNCATE TABLE #Result;
    INSERT INTO #Result (company, code, name, hyprice, comeFrom, finish, smallfanwei, distance)
    SELECT TOP (@PageSize)
        a.company, a.code, a.name, b.hyprice, a.comeFrom,
        ISNULL(a.finish,''), c.smallfanwei,
        dbo.GetDistancefromLnglat(@Lng, @Lat, c.lng, c.lat)/1000.0
    FROM Bgoods a
    JOIN Bgoodsdtl b ON a.code=b.code AND a.company=b.company
    JOIN shopperA c ON a.company=c.SEQ
    WHERE b.display IN ('1','2','3')
      AND (c.lng BETWEEN @Lng-0.0488 AND @Lng+0.0488)
      AND (c.lat BETWEEN @Lat-0.0449 AND @Lat+0.0449)
    ORDER BY distance;
    
    SELECT * FROM #Result WHERE rn BETWEEN 1 AND @PageSize;
    SELECT COUNT(*) AS TotalCount FROM #Result;
    DROP TABLE #Result;
END

方案二:TRY-CATCH 捕獲「無數據」異常 (不推薦,語義不清)

SQL Server 無「無數據異常」,SELECT 返回空結果集**不拋異常**。強行用 RAISERROR 模擬異常流程屬於濫用異常控制流,**極不推薦**。

性能優化關鍵點

  • 避免重複計算距離: 子查詢/CTE 先算距離存入臨時表,外層直接用 distance 列過濾/排序,避免 ORDER BY dbo.GetDistance... 導致全表掃描無法用索引。
  • 空間索引: shopperA(lng, lat)、ghuser(lng, lat) 建立空間索引或復合索引,配合經緯度範圍過濾 (BETWEEN) 命中索引求交集。
  • 參數化查詢: 存儲過程參數化防注入,執行計劃復用。
  • 臨時表復用: TRUNCATE TABLE #Result 比 DROP/CREATE 快,保留統計信息。

總結

SQL Server 存儲過程多條件降級查詢,核心模式: 臨時表存結果 → INSERT TOP(N) 第一優先級 → IF EXISTS 判斷有無數據 → 有則返回退出 → 無則 TRUNCATE 臨時表 → 繼續下一優先級。
**拒絕用 TRY-CATCH 模擬業務流程**,IF EXISTS 語義清晰、性能最優、易維護。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved