【问题标题】:Is it possible to get two sets of data with one query一次查询可以得到两组数据吗
【发布时间】:2012-12-25 10:58:05
【问题描述】:

我需要从数据库中获取一些项目,每个项目的前三个 cmets。

现在我有两个存储过程GetAllItemsGetTopThreeeCommentsByItemId

在应用程序中我得到 100 个项目,然后在 foreach 循环中我调用 GetTopThreeeCommentsByItemId 过程来获得前三个 cmets。

我知道从性能的角度来看这很糟糕。

是否有某种技术可以通过一个查询来获得此信息?

我可以使用OUTER APPLY 获得一条好评(如果有),但我不知道如何获得三条。

Items {ItemId, Title, Description, Price etc.}
Comments {CommentId, ItemId etc.}

我想要获取的样本数据

  • 物品_1
    -- 评论_1
    -- 评论_2
    -- 评论_3

  • 物品_2
    -- 评论_4
    -- 评论_5

【问题讨论】:

  • 请添加表结构和示例数据

标签: sql-server sql-server-2008 tsql stored-procedures


【解决方案1】:

如果您使用的是 SQL Server 2005 及更高版本(您在这方面不够具体),则一种方法是使用 CTE(通用表表达式)。

使用此 CTE,您可以按某些标准(即您的 ItemId)对数据进行分区,并让 SQL Server 为每个“分区”从 1 开始为您的所有行编号,并按某些标准排序。

所以试试这样的:

;WITH ItemsAndComments AS
(
   SELECT 
       i.ItemId, i.Title, i.Description, i.Price,
       c.CommentId, c.CommentText,
       ROW_NUMBER() OVER(PARTITION BY i.ItemId ORDER BY c.CommentId) AS 'RowNum'
   FROM 
       dbo.Items i
   LEFT OUTER JOIN 
       dbo.Comments c ON c.ItemId = i.ItemId
   WHERE
      ......
)
SELECT 
   ItemId, Title, Description, Price,
   CommentId, CommentText
FROM 
   ItemsAndComments
WHERE
   RowNum <= 3

在这里,我为每个“分区”(即每个项目)选择最多三个条目(即 cmets) - 由 CommentId 排序。

这是否符合您的要求??

【讨论】:

  • 我使用 sql server 2008。这样我得到三行(每个评论)。我认为有某种方法可以以某种方式将它排成一行。这意味着在 ASP.NET 方面,我需要检查 ItemId 的所有行并将每个注释添加到它的 Item。我说的对吗?
  • @1110:对于每个 item,您将获得最多 3 行 - 如果 item 具有三个或更多 cmets。每对 (item/comment) 对应一行,因此如果一个 item 有多个 comments,则 Items 表中的列会重复 - 每条评论一次。
【解决方案2】:

您可以编写一个调用 GetAllItems 和 GetTopThreeeCommentsByItemId 的存储过程,在临时表中获取结果并连接这些表以生成您需要的单个结果集。

如果您没有机会使用存储过程,您仍然可以通过从数据访问层运行单个 SQL 脚本来执行相同操作,该脚本调用 GetAllItems 和 GetTopThreeeCommentsByItemId 并将结果放入临时表中,稍后将它们加入以返回单个结果集。

【讨论】:

    【解决方案3】:

    这得到二哥使用OUTER APPLY:

    select m.*, elder.*
    from Member m
    outer apply
    (
     select top 2 ElderBirthDate = x.BirthDate, ElderFirstname = x.Firstname
     from Member x 
     where x.BirthDate < m.BirthDate 
     order by x.BirthDate desc
    ) as elder
    order by m.BirthDate, elder.ElderBirthDate desc
    

    来源数据:

    create table Member
    (
     Firstname varchar(20) not null, 
     Lastname varchar(20) not null,
     BirthDate date not null unique
    );
    
    insert into Member(Firstname,Lastname,Birthdate) values
    ('John','Lennon','Oct 9, 1940'),
    ('Paul','McCartney','June 8, 1942'),
    ('George','Harrison','February 25, 1943'),
    ('Ringo','Starr','July 7, 1940');
    

    输出:

    Firstname            Lastname             BirthDate  ElderBirthDate ElderFirstname
    -------------------- -------------------- ---------- -------------- --------------------
    Ringo                Starr                1940-07-07 NULL           NULL
    John                 Lennon               1940-10-09 1940-07-07     Ringo
    Paul                 McCartney            1942-06-08 1940-10-09     John
    Paul                 McCartney            1942-06-08 1940-07-07     Ringo
    George               Harrison             1943-02-25 1942-06-08     Paul
    George               Harrison             1943-02-25 1940-10-09     John
    
    (6 row(s) affected)
    

    现场测试:http://www.sqlfiddle.com/#!3/19a63/2

    marc's answer 更好,如果您需要查询与主实体“近”的实体(例如地理空间、兄弟、最接近到期​​日等),只需使用 OUTER APPLY。

    外部应用演练:http://www.ienablemuch.com/2012/04/outer-apply-walkthrough.html

    不过,您可能需要 DENSE_RANK 而不是 ROW_NUMBER/RANK,因为评论的最高标准可能会产生平局。 TOP 1 可以产生超过 1 个,TOP 3 也可以产生超过 3 个。该场景的示例(DENSE_RANK 演练):http://www.anicehumble.com/2012/03/postgresql-denserank.html

    【讨论】:

    • 这是正确的,但我的上帝让这个例子令人困惑。示例数据中没有兄弟,示例数据中的每个人都被视为彼此的兄弟。如果使用具有“真实”家庭关系和关联表的示例数据,此示例会更好。
    • @Confusion 我从没想过他们会让人感到困惑,不过我把披头士乐队视为彼此的兄弟 :-) 如果我把“真正的”家庭关系放在一起,就会混淆获得最佳结果的逻辑对于每个实体,这会更令人困惑
    【解决方案4】:

    最好用row_number语句选择语句,单独选择前3个

    从中选择一个.* ( 选择 *,row_number() over(按列分区)[dup] ) 作为一个 哪里重复

    【讨论】:

      猜你喜欢
      • 2019-09-20
      • 1970-01-01
      • 2011-12-25
      • 2010-11-21
      • 2011-04-06
      • 2012-10-26
      • 2021-10-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多