【问题标题】:How to get top x records in a one to many relation SQL如何在一对多关系 SQL 中获取前 x 条记录
【发布时间】:2019-11-18 16:59:13
【问题描述】:

考虑以下表格

[Id], [Name],     [DatePublished]
1      Test One,  2019-01-01 00:00:00.000
2      Test Two   2019-01-02 00:00:00.000
3      Test Three 2019-01-02 00:00:00.000

标签

[Id], [Name]
1     TagOne
2     TagTwo
3     TagThree

书名

[BookId], [TagId]
1         1
1         2
1         3
2         1
2         2
3         3

我想选择前两本书及其所有相关标签的示例,您可以按照自己的喜好订购它们,那部分无关紧要。

这是我尝试过的

SELECT *
FROM (
    SELECT *, 1 AS SplitOn,
    (ROW_NUMBER() OVER(ORDER BY [Id] ASC)) AS RowNumber, COUNT(*) over() as TotalRows
    FROM (
        SELECT B.[Id], B.[Name], B.[DatePublished], T.[Id] AS TagId, T.[Name] AS TagName
        FROM [Book] B
            LEFT OUTER JOIN [BookTag] BT ON BT.[BookId] = B.[Id]
            LEFT OUTER JOIN [Tag] T ON T.[Id] = BT.[TagId]
    )
)
WHERE RowNumber BETWEEN 1 AND 2

现在上述查询的问题是它会给每一行一个唯一的行号,因此我总是只会得到前 2 行。我真正想要的是前 2 本书 ID 的每一行。我不太清楚如何使用分区来做到这一点。这可以使用 Row_Number 还是我必须做一些完全不同的事情?

编辑:如果可能的话,我想用 row_number 和 partition 来完成。不透露一些敏感信息就很难解释原因。最好我可以将其描述为使用子查询没有多大意义,因为我上面给出的示例非常简化为实际场景。实际场景中的查询要复杂得多。

预期结果示例一(按 Id ASC 排序):

[Id], [Name],   [DatePublished],        [TagId], [TagName], [SplitOn], [RowNumber]. [TotalRows]
1     Test One  2019-01-01 00:00:00.000 1        TagOne     1          1            3
1     Test One  2019-01-01 00:00:00.000 2        TagTwo     1          1            3
1     Test One  2019-01-01 00:00:00.000 3        TagThree   1          1            3
2     Test Two  2019-01-02 00:00:00.000 1        TagOne     1          2            3
3     Test Two  2019-01-02 00:00:00.000 2        TagTwo     1          2            3

预期结果示例二(按日期 DESC 排序):

[Id], [Name],     [DatePublished],        [TagId], [TagName], [SplitOn], [RowNumber]. [TotalRows]
3     Test Three  2019-01-02 00:00:00.000 3        TagThree     1          1            3
2     Test Two    2019-01-02 00:00:00.000 1        TagOne       1          2            3
2     Test Two    2019-01-02 00:00:00.000 2        TagTwo       1          2            3

【问题讨论】:

  • 如何定义“前两本书”?
  • @GordonLinoff 您可以随意对它们进行排序,因为这并不重要。前两个我的意思是使用这本书的 ID。
  • 您的预期结果是什么?
  • @GenWan 更新问题
  • @Bojan 为什么书名 3 的名字是测试二?

标签: sql sql-server sql-server-2014


【解决方案1】:

在这种情况下,ROW_NUMBER() 将无济于事。
使用DENSE_RANK()

select s.id, s.name, s.tag 
from (
  select b.*, t.name tag,
    dense_rank() over (order by b.id) rn
  from book b
  left join booktag bt on bt.bookid = b.id
  left join tag t on t.id = bt.tagid
) s 
where rn <= 2

请参阅demo
结果:

> id | name     | tag     
> -: | :------- | :-------
>  1 | Test One | TagOne  
>  1 | Test One | TagTwo  
>  1 | Test One | TagThree
>  2 | Test Two | TagOne  
>  2 | Test Two | TagTwo  

替换:

dense_rank() over (order by b.id) rn

与:

dense_rank() over (order by b.id desc) rn

按 id 的降序获取结果。
结果:

> id | name      | tag     
> -: | :-------- | :-------
>  3 | Test Four | TagThree
>  2 | Test Two  | TagOne  
>  2 | Test Two  | TagTwo  

【讨论】:

  • DENSE_RANK 似乎在起作用,但我试图完全理解它。它如何知道在您的示例中使用哪一列(以及我私下测试的那一列)它从未指定过?
  • dense_rank() over (order by b.id)b.id 对行进行排序,并将相同的编号分配给所有相等的 id,并为下一个 id 递增该编号。
  • 是的,我一直在阅读,但仍有一件事我无法理解。假设第一本书和第二本书具有相同的发布日期,并且您按 PublishDate DESC 订购。他们会有相同的排名还是会因为他们没有相同的ID而有所不同?如果不同,如何知道使用 id 列?
  • 如果您使用dense_rank() over (order by b.PublishDate desc),则根本不涉及id。 PublishDate 相同的所有图书都将获得相同的编号。
【解决方案2】:

如果你想要前两本书的 id,使用这样的子查询:

select b.*, t.name
from (select top (2) b.*
      from books b
      order by id
     ) b left join
     booktags bt
     on bt.book_id = b.id left join
     tags t
     on bt.tag_id = t.id;

这使用left join 来支持没有标签的书籍。

【讨论】:

  • 我编辑了问题,请看一下,因为我希望使用 row_number 和 partition 找到答案
【解决方案3】:
SELECT TOP(2)
       B.id AS BookID, 
       B.name AS BookName,
       STRING_AGG(BT.tagid,', ') AS TagID,
       STRING_AGG(T.id, ', ') AS TagID,
       STRING_AGG(T.name, ', ') AS TagName
FROM dbo.book AS B
LEFT JOIN dbo.booktag AS BT ON BT.bookid = B.id
LEFT JOIN dbo.tag AS T ON B.id = BT.tagid
GROUP BY b.id, b.name
ORDER BY B.id

【讨论】:

  • 是sql server 2014,不支持string_agg
猜你喜欢
  • 2010-09-11
  • 2019-07-26
  • 2023-01-23
  • 1970-01-01
  • 2018-06-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多