编写以下 SQL 查询的更有效方法

Posted

技术标签:

【中文标题】编写以下 SQL 查询的更有效方法【英文标题】:More effective way to write following SQL query 【发布时间】:2017-12-11 13:23:08 【问题描述】:

我正在编写查询以返回新闻门户主页的文章列表。 要求如下。

每个需要在首页上的分类需要按以下标准显示5篇文章。

每个类别需要有一篇文章是该类别的主要新闻,其次是当前最热门的4条新闻。 如果类别集没有第一条新闻,则显示 5 个最受欢迎的 insted。

我编写了一个带有 CategoryID 参数的 SQL 函数和另一个调用该函数 N 次的 SQL 过程。

有没有更有效的方法来编写这个查询?

功能

CREATE FUNCTION [dbo].[Fn_FetchHomepageCategory] 
(   
    -- Add the parameters for the function here
    @categoryId int

)
RETURNS @ArticlesToReturn TABLE 
                      ( Id int, 
                        Title nvarchar(500),
                        Slug nvarchar(500),
                        Summary nvarchar(1500),
                        IsCategoryFirst bit,
                        RootCategoryId int,
                        RootCategory nvarchar(500),
                        OldFacebookCommentsUrl nvarchar(500),
                        Icon nvarchar(500),
                        TopicName nvarchar(500),
                        MainArticlePhoto nvarchar(500),
                        FrontPagePhoto nvarchar(500),
                        PublishDate datetime

                      )  
AS
BEGIN


    -- select category first news if any
    INSERT INTO @ArticlesToReturn
    SELECT TOP 1
    ART.Id, ART.Title, ART.InitialTitle, ART.Summary,ART.IsCategoryFirst,
    ART.RootCategoryId, CAT.Name, ART.OldFacebookCommentsUrl, ICO.CssClass,
    ART.TopicName, ART.MainArticlePhoto, ART.FrontPagePhoto, ART.PublishDate
    FROM Articles ART WITH (NOLOCK)
    INNER JOIN ArticleViewCountSum AVS WITH (NOLOCK) ON AVS.ArticleId = ART.Id 
    INNER JOIN Categories CAT WITH (NOLOCK) ON CAT.Id = ART.RootCategoryId 
    LEFT JOIN ArticleIcons ICO WITH (NOLOCK) ON ICO.Id = ART.IconId
    WHERE ART.RootCategoryId = @categoryId
    AND ART.PublishDate < GETDATE() 
    AND ART.Active = 1
    AND IsCategoryFirst = 1

    -- select 5 most popular by coefficient
    INSERT INTO @ArticlesToReturn
    SELECT TOP 5 
    ART.Id, ART.Title, ART.InitialTitle, ART.Summary,ART.IsCategoryFirst,
    ART.RootCategoryId, CAT.Name, ART.OldFacebookCommentsUrl, ICO.CssClass,
    ART.TopicName, ART.MainArticlePhoto, ART.FrontPagePhoto, ART.PublishDate
    FROM Articles ART WITH (NOLOCK)
    INNER JOIN ArticleViewCountSum AVS WITH (NOLOCK) ON AVS.ArticleId = ART.Id 
    INNER JOIN Categories CAT WITH (NOLOCK) ON CAT.Id = ART.RootCategoryId 
    LEFT JOIN ArticleIcons ICO WITH (NOLOCK) ON ICO.Id = ART.IconId
    WHERE ART.RootCategoryId = @categoryId
    AND ART.PublishDate < GETDATE() 
    AND ART.Active = 1

    ORDER BY  ART.Coefficient  DESC


    RETURN 

END

存储过程

CREATE PROCEDURE [dbo].[Fetch_HomePageArticles]

AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;
    DECLARE @dateNow datetime = GETDATE();

    -- first main news
    SELECT TOP 1 * FROM Articles 
    WHERE IsFirst = 1 AND PublishDate < @dateNow


    --TODO: featured
    SELECT TOP 10 * From Featured 
    WHERE PublishDate < @dateNow AND Active = 1 
    ORDER BY PublishDate DESC


    SELECT TOP 5 * FROM Fn_FetchHomepageCategory(3) 
    SELECT TOP 5 * FROM Fn_FetchHomepageCategory(150) 
    SELECT TOP 5 * FROM Fn_FetchHomepageCategory(1523) 
    SELECT TOP 5 * FROM Fn_FetchHomepageCategory(1509) 
    SELECT TOP 5 * FROM Fn_FetchHomepageCategory(1569) 
    SELECT TOP 5 * FROM Fn_FetchHomepageCategory(1545) 
    SELECT TOP 5 * FROM Fn_FetchHomepageCategory(1548) 
    SELECT TOP 5 * FROM Fn_FetchHomepageCategory(67)

END

我尝试将函数修改为只有一个 SELECT 并包含 Order BY IsFirstCategory DESC,但查询运行速度要慢得多。

【问题讨论】:

【参考方案1】:

一个潜在的改进是将 Fn_FetchHomepageCategory 函数中的两个 SELECT 子句通过添加一个新的合成系数参数合并到一个查询中:

SELECT 
    TOP 5 ART.Id, 
    ART.Title, 
    ART.InitialTitle, 
    ART.Summary, 
    ART.IsCategoryFirst, 
    ART.RootCategoryId, 
    CAT.Name, 
    ART.OldFacebookCommentsUrl, 
    ICO.CssClass, 
    ART.TopicName, 
    ART.MainArticlePhoto, 
    ART.FrontPagePhoto, 
    ART.PublishDate
 FROM 
  Articles ART WITH (NOLOCK) 
  INNER JOIN ArticleViewCountSum AVS WITH (NOLOCK) ON AVS.ArticleId = ART.Id 
  INNER JOIN Categories CAT WITH (NOLOCK) ON CAT.Id = ART.RootCategoryId 
  LEFT JOIN ArticleIcons ICO WITH (NOLOCK) ON ICO.Id = ART.IconId 
WHERE 
  ART.RootCategoryId = @categoryId 
  AND ART.PublishDate < GETDATE() 
  AND ART.Active = 1 
ORDER BY 
  CASE IsCategoryFirst
  WHEN 1    THEN 1000000
  ELSE  ART.Coefficient 
  END DESC

您可以将 1000000 替换为另一个大数字。它唯一的一点是将最高的协同效率分数分配给 IsCategoryFirst = 1 的帖子。 请注意,只有当您只有一个 IsCategoryFirst = 1 的帖子时,它才能正常工作。

【讨论】:

这是对代码的巧妙更改,但它使我的查询速度减慢了 3 倍。

以上是关于编写以下 SQL 查询的更有效方法的主要内容,如果未能解决你的问题,请参考以下文章

有没有更有效的方法来执行这个嵌套的 SQL 查询?

从文件中存储记录的更有效方法

查询与 Hive QL 中另一列中的每个值关联的最短字符串值的更有效方法

在向量中查找连续数字的更有效方法?

如何最有效地编写SQL

检索 Firebase 数据的更有效方法?