【问题标题】:SQL to get range of results in alphabetical orderSQL按字母顺序获取结果范围
【发布时间】:2011-06-25 00:02:14
【问题描述】:

我有一个表,tblTags,它的工作方式与 StackOverflows 标记系统大致相同。

当我查看标签页面时,假设标签Tutorial 我想按字母顺序显示其前后的 10 个标签。

因此,如果我们给定了 ID 30 的标签 Tutorial,我们如何返回一个类似以下顺序的记录集:

Tap
Tart
> Tutorial
Umbrellas
Unicorns
Xylaphones

我认为这样做很糟糕,因为它们涉及检索大量数据。

我不确定是否可以按照(伪)的方式做一些事情:

SELECT RANGE(0 - 30) FROM tblTags ORDER BY Name ASC

但是如何在不遍历整个列表直到找到它的情况下,高效地知道教程标签在列表中的位置呢?

我正在使用带有 LINQ 的 SQL Server 2008 R2 Express,如果它有任何不同,SQL 查询或 LINQ 将是很好的答案,谢谢!

【问题讨论】:

  • @Teo.sk 我们知道 ID 和名称
  • @Teo.sk BETWEEN 看起来不错!但我需要确保它保证获取合理数量的记录,根据标签的数量/类型,可能返回太多或太少
  • 我认为下面的联合解决方案会根据您的表格大小给出良好的结果。英语的单词少于 200K,所以我不会想象您的表格会如此之大以至于大小会影响性能(除非您允许部分单词作为标签)。同时为您的姓名列编制索引以获得最佳性能。
  • @Cordsen,谢谢,在其他答案的帮助下,我设法制作了一个有效的 linq 解决方案!

标签: sql linq sql-server-2008


【解决方案1】:

根据 Jacob 的 UNION 建议,您可以使用表变量来选择匹配的 TagID,然后再加入到 Tag 表中以获取匹配的记录。它不像我想要的那样优雅,但它确实有效。

附带说明,我认为 UNION 方法会起作用,但 AFAIK SQL Server 仅允许在最后一个 SELECT 上进行 ORDER BY,并且 ORDER BY 适用于整个结果集(this post 也说明了同样的事情)。

DECLARE @tags AS TABLE(TagID INT, Name VARCHAR(30))
INSERT INTO @tags VALUES(1, 'a')
INSERT INTO @tags VALUES(2, 'b')
INSERT INTO @tags VALUES(3, 'c')
INSERT INTO @tags VALUES(4, 'd')
INSERT INTO @tags VALUES(5, 'e')
INSERT INTO @tags VALUES(6, 'f')
INSERT INTO @tags VALUES(7, 'g')
INSERT INTO @tags VALUES(8, 'h')
INSERT INTO @tags VALUES(9, 'i')
INSERT INTO @tags VALUES(10, 'j')

DECLARE @selectedTags AS TABLE(TagID INT)
INSERT INTO @selectedTags
SELECT TOP 2 TagID FROM @tags WHERE Name < 'e' ORDER BY Name DESC
INSERT INTO @selectedTags
SELECT TOP 2 TagID FROM @tags WHERE Name >= 'e' ORDER BY Name

SELECT * 
FROM @tags T
JOIN @selectedTags ST ON ST.TagID = T.TagID
ORDER BY T.Name

【讨论】:

    【解决方案2】:

    也许工会会起作用。 (我确定我有一些语法错误,但你明白了)

    (编辑:感谢 cmets 和其他答案,尤其是 rsbarro)


    DECLARE @tags AS TABLE(TagID INT, tag VARCHAR(30))
    INSERT INTO @tags VALUES(1, 'a')
    INSERT INTO @tags VALUES(2, 'b')
    INSERT INTO @tags VALUES(3, 'c')
    INSERT INTO @tags VALUES(4, 'd')
    INSERT INTO @tags VALUES(5, 'e')
    INSERT INTO @tags VALUES(6, 'f')
    INSERT INTO @tags VALUES(7, 'g')
    INSERT INTO @tags VALUES(8, 'h')
    INSERT INTO @tags VALUES(9, 'i')
    INSERT INTO @tags VALUES(10, 'j');
    
    select * from (select top(2) tag
    from @tags
    where tag < 'f'
    order by tag desc
    ) a
    
    union
    
    select * from (select top(3) tag
    from @tags
    where tag >= 'f'
    order by tag) b;
    

    但是,我会进行性能检查,看看这是否确实比返回更多行然后过滤更快。我感觉有一种性能更高的方法。

    【讨论】:

    • +1 我喜欢这种方法。您可能正在使用 table 变量查看与我的方法类似的性能(可能会更好一些,因为您不必重新加入表,但如果所有内容都正确索引,它可能不会对小型数据集产生影响) .不过,看到有人在一次选择中这样做会很有趣。 =]
    【解决方案3】:
    With X As (select tblTag.*, Row_Number() Over(Order By tag) R_NUMB From tblTag )
    Select tag From X
    Where X.R_NUMB  Between (Select X.R_NUMB From X  Where tag = 'Tutorial') - 10 
    And (Select X.R_NUMB From X  Where tag = 'Tutorial') + 10
    

    【讨论】:

      【解决方案4】:

      正如 Jacob 所建议的,我的想法完全相同,只有 SQL Server 会获得“前 10”,而不是限制。

      select top 3 * 
      from names
      where name<'d'
      union
      select top 3 * 
      from names
      where name>='d'
      order by name 
      

      (在 SQL Server 2008 R2 上测试过这个)

      编辑: 正如正确指出的那样,前面的查询没有提供所需的结果。但是,这应该有更有效的方法来做同样的事情。

      select name from names
      where name in (
          select top 3 name from names where name<'e' order by name desc
          )or name in (
          select top 3 name from names where name>='e')
      order by name 
      

      【讨论】:

      • 不幸的是,这不起作用。假设你的表有 'a' 到 'j' 并且你运行了 'e' 超过 2 条记录。你会得到'a','b','e','f'。
      • @rsbarro 你是绝对正确的!不幸的是,sql-server 不允许在 Union join 中使用 2 个“order by”子句。
      • @rsbarro 请查看更新后的帖子。与您的想法类似,但没有临时表。
      【解决方案5】:

      花了我一段时间才弄明白,感谢工会的想法。不太确定为什么这行得通,.take(n) 似乎需要大量工作 不太清楚为什么会这样,这段代码似乎可以 100% 正常工作,并且每边都需要 5 个:

      var q = (
          from c in db.tblTags
          where
              !(from o in db.tblTagSynonyms
                  select o.SynonymTagID)
              .Contains(c.ID)
              &&
              c.Name.CompareTo(AnchorTagName) < 1
          orderby c.Name descending
          select new
              {
                  c.ID,
                  c.Name,
                  c.Priority,
                  TotalResources = (from a in db.tblTagAnchors where a.TagID == c.ID select new { a.ID }).Count()
              }
          ).Take(7).ToArray().Reverse().Union(
          from c in db.tblTags
          where
              !(from o in db.tblTagSynonyms
                  select o.SynonymTagID)
              .Contains(c.ID)
              &&
              c.Name.CompareTo(AnchorTagName) >= 1
          orderby c.Name ascending
          select new
          {
              c.ID,
              c.Name,
              c.Priority,
              TotalResources = (from a in db.tblTagAnchors where a.TagID == c.ID select new { a.ID }).Count()
          }
      ).Skip(1).Take(11).ToArray();
      

      【讨论】:

        猜你喜欢
        • 2020-09-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-08-15
        • 1970-01-01
        • 2023-03-28
        • 1970-01-01
        相关资源
        最近更新 更多