【问题标题】:SQL performance on ROWCOUNT check - better way?ROWCOUNT 检查的 SQL 性能 - 更好的方法?
【发布时间】:2015-09-10 10:50:40
【问题描述】:

我有一个 MS SQL 2005 存储过程,我将一个 ID 从网页传递给它。

ID 用于将预先设置的搜索条件从小部件类别登录页面的数据库中读取到一组变量中。

然后在 SELECT 中使用这些变量来搜索小部件项目的数据库。访问者的页面选择可以覆盖某些变量,例如最高价格。

我想检查 SELECT 是否返回记录,如果没有,则默认为着陆页的正常标准,以确保访问者总是看到一些项目。

我曾尝试使用@@ROWCOUNT 来检查第一个 SELECT,但是由于 SELECT 相当复杂(我在下面的示例中删除了很多字段),因此运行它两次的性能损失是不可接受的。第一个 SELECT 本身需要大约 1 秒,而检查 @@ROWCOUNT = 0 并再次运行 SELECT 大约需要 4 秒。

如果第一个 SELECT 没有返回任何记录,是否有更好的方法来完成此检查和返回记录?

我还想在存储过程结束时只返回一个记录集,而不是两个。

对此进行研究后,我发现有人在两次选择中使用 UNION ALL,但我认为在这种情况下它对我没有帮助。我还想通过一个内容为 True 或 False 的字段来了解使用了哪个 SELECT,这样我就可以在网站上标记是否没有找到记录。

感谢您的帮助。

CREATE PROCEDURE dbo.NW_LANDING_GET_Widgets

/* options from the web page */

   @WidgetID int,
   @PageNumber int,
   @WidgetsPerPage int,
   @Sort VARCHAR(1),       
   @MinPrice int,
   @MaxPrice int,
   @Override_WidgetType varchar(20),
   @Override_WidgetInfo1 int

AS
SET NOCOUNT ON
BEGIN

/* ---------- Declare variables for criteria to be gathered from WidgetLanding table ------------ */

   DECLARE @WidgetCategory1 varchar(50)
   DECLARE @WidgetCategory2 varchar(50)       
   DECLARE @WidgetType varchar(30)
   DECLARE @WidgetInfo1 int  


/* ---------- Read default criteria into variables from WidgetLanding table ----- */

   SELECT
   @WidgetCategory1=Criteria_WidgetCategory1,
   @WidgetCategory2=Criteria_WidgetCategory2,       
   @WidgetType=Criteria_WidgetType                    
   FROM dbo.WidgetLanding
   WHERE pk_WidgetID = @WidgetID


/* -------- Set PageNumber variable for SELECT of Widgets ----------- */

   SET @PageNumber=(@PageNumber-1)*@WidgetsPerPage

   /* Set Minimum and Maximum Prices - if Null, set highest and lowest number possible */
   DECLARE @Min int
   DECLARE @Max int
   SET @Min = ISNULL(@MinPrice,0)
   SET @Max = ISNULL(@MaxPrice,999999999)                         


/* -------- Override variables if visitor has changed the search criteria from default ------------------- */

   IF @Override_WidgetType is not null
     BEGIN
       SET @WidgetType=@Override_WidgetType
     END

   IF @Override_WidgetInfo1 is not null
     BEGIN
       SET @WidgetInfo1=@Override_WidgetInfo1
     END

/* ------------------- Retrieve widget records based on variables ------ */

   SELECT TOP(@WidgetsPerPage) * FROM (SELECT RowID=ROW_NUMBER() 
   OVER (ORDER BY 
   CASE WHEN dbo.Widgets.Featured_WidgetLandingID = @WidgetID then 1 else 0 end DESC,
   CASE WHEN @Sort = 'D' THEN dbo.Widgets.Price END DESC, /* Price Descending */
   CASE WHEN @Sort = 'U' THEN dbo.Widgets.Price END ASC, /* Price Ascending */
   CASE WHEN @Sort = 'P' THEN dbo.Widgets.viewed END DESC, /* Popular */
   CASE WHEN @Sort = 'L' THEN dbo.Widgets.Date END DESC), /* Latest */
   Count(dbo.Widgets.WidgetID) OVER() As TotalRecords,       
   dbo.Widgets.Price,       
   dbo.Widgets.WidgetID,
   dbo.Widgets.WidgetCategory1,
   dbo.Widgets.WidgetCategory2,
   dbo.Widgets.WidgetType,
   dbo.Widgets.WidgetType2,
   dbo.Widgets.WidgetInfo1
   FROM dbo.Widgets       

   WHERE 
   (WidgetCategory1 = @WidgetCategory1 OR @WidgetCategory1 is null) AND 
   (WidgetCategory2 = @WidgetCategory2 OR @WidgetCategory2 is null) AND
   (Price >= @Min AND Price <= @Max) AND
   (WidgetInfo1 >= @WidgetInfo1 OR @WidgetInfo1 is null)

   )  TAB WHERE TAB.RowId > CAST(@PageNumber AS INT) 


/*
-----------------------------
THIS IS WHERE I WANT TO CHECK IF RECORDS ARE RETURNED - IF NOT DO ANOTHER SELECT BUT WITHOUT OVERRIDING VARIABLES SO RECORDS WILL ALWAYS BE RETURNED
-----------------------------
*/

END

SET NOCOUNT OFF

【问题讨论】:

  • 如果您进行计数(),查询会运行得更快吗?换句话说,声明一个recordcount变量,设置recordcount = count() ...
  • 恐怕我不是 SQL 专家。你能解释一下我应该把这个放在哪里吗?非常感谢。

标签: sql sql-server stored-procedures rowcount


【解决方案1】:

最简单的方法是这样的代码:

SELECT TOP(@WidgetsPerPage) * 
INTO #Res
FROM (SELECT RowID=ROW_NUMBER() 
OVER (ORDER BY 
CASE WHEN dbo.Widgets.Featured_WidgetLandingID = @WidgetID then 1 else 0 end DESC,
CASE WHEN @Sort = 'D' THEN dbo.Widgets.Price END DESC, /* Price Descending */
CASE WHEN @Sort = 'U' THEN dbo.Widgets.Price END ASC, /* Price Ascending */
CASE WHEN @Sort = 'P' THEN dbo.Widgets.viewed END DESC, /* Popular */
CASE WHEN @Sort = 'L' THEN dbo.Widgets.Date END DESC), /* Latest */
Count(dbo.Widgets.WidgetID) OVER() As TotalRecords,       
dbo.Widgets.Price,       
dbo.Widgets.WidgetID,
dbo.Widgets.WidgetCategory1,
dbo.Widgets.WidgetCategory2,
dbo.Widgets.WidgetType,
dbo.Widgets.WidgetType2,
dbo.Widgets.WidgetInfo1
FROM dbo.Widgets       

WHERE 
(WidgetCategory1 = @WidgetCategory1 OR @WidgetCategory1 is null) AND 
(WidgetCategory2 = @WidgetCategory2 OR @WidgetCategory2 is null) AND
(Price >= @Min AND Price <= @Max) AND
(WidgetInfo1 >= @WidgetInfo1 OR @WidgetInfo1 is null)

)  TAB WHERE TAB.RowId > CAST(@PageNumber AS INT) 

IF (@@ROWCOUNT>0)
BEGIN
    INSERT #Res (......)
    SELECT
END

SELECT * FROM #Res

但是无论如何你的查询应该被重写。

【讨论】:

  • 您好,非常感谢您的帮助。当你说它应该重写时,是因为我所拥有的只是在检索记录方面表现不佳,还是出于其他原因?我希望同时提高 SELECT 本身的性能,以及简化检查返回记录和在未找到时输出默认记录的特殊问题
  • 还有更多的cmets吗?非常感谢您的帮助
猜你喜欢
  • 1970-01-01
  • 2012-07-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-08
  • 1970-01-01
相关资源
最近更新 更多