简体   繁体   English

在sql server 2005中选择

[英]select in sql server 2005

I have a table follow: 我有一张桌子跟随:

ID  |  first  |  end
--------------------
a   |      1  |    3
b   |      3  |    8
c   |      8  |   10

I want to select follow: 我想选择以下内容:

 ID  |  first  |  end
---------------------
a-c  |      1  |   10   

But i can't do it. 但我不能这样做。 Please! 请! help me. 帮我。 Thanks! 谢谢!

This works for me: 这对我有用:

SELECT MIN(t.id)+'-'+MAX(t.id) AS ID,
       MIN(t.[first]) AS first,
       MAX(t.[end]) AS [end]
  FROM dbo.YOUR_TABLE t

But please , do not use reserved words like "end" for column names . 请注意不要为列名使用“end”之类的保留字

I believe you can do this using a recursive Common Table Expression as follows, especially if you're not expecting very long chains of records: 我相信你可以使用递归的Common Table Expression来做到这一点,特别是如果你不期望很长的记录链:

WITH Ancestors AS
(
    SELECT
        InitRow.[ID] AS [Ancestor],
        InitRow.[ID],
        InitRow.[first],
        InitRow.[end],
        0 AS [level],
        '00000' + InitRow.[ID] AS [hacky_level_plus_ID]
    FROM
        YOUR_TABLE AS InitRow
    WHERE
        NOT EXISTS
        (
            SELECT * FROM YOUR_TABLE AS PrevRow
            WHERE PrevRow.[end] = InitRow.[first]
        )
    UNION ALL
    SELECT
        ParentRow.Ancestor,
        ChildRow.[ID],
        ChildRow.[first],
        ChildRow.[end],
        ParentRow.level + 1 AS [level],
        -- Avoids having to build the recursive structure more than once.
        -- We know we will not be over 5 digits since CTEs have a recursion
        -- limit of 32767.
        RIGHT('00000' + CAST(ParentRow.level + 1 AS varchar(4)), 5)
            + ChildRow.[ID] AS [hacky_level_plus_ID]
    FROM
        Ancestors AS ParentRow
        INNER JOIN YOUR_TABLE AS ChildRow
            ON ChildRow.[first] = ParentRow.[end]
)
SELECT
    Ancestors.Ancestor + '-' + SUBSTRING(MAX([hacky_level_plus_ID]),6,10) AS [IDs],
-- Without the [hacky_level_plus_ID] column, you need to do it this way:
--  Ancestors.Ancestor + '-' +
--      (SELECT TOP 1 Children.ID FROM Ancestors AS Children
--      WHERE Children.[Ancestor] = Ancestors.[Ancestor]
--      ORDER BY Children.[level] DESC) AS [IDs],
    MIN(Ancestors.[first]) AS [first],
    MAX(Ancestors.[end]) AS [end]
FROM
    Ancestors
GROUP BY
    Ancestors.Ancestor
-- If needed, add OPTION (MAXRECURSION 32767)

A quick explanation of what each part does: 快速解释每个部分的作用:

The WITH Ancestors AS (...) clause creates a Common Table Expression (basically a subquery) with the name Ancestors . WITH Ancestors AS (...)子句创建一个名为Ancestors的公用表表达式(基本上是一个子查询)。 The first SELECT in that expression establishes a baseline: all the rows that have no matching entry prior to it. 该表达式中的第一个SELECT建立一个基线:所有在它之前没有匹配条目的行。

Then, the second SELECT is where the recursion kicks in. Since it references Ancestors as part of the query, it uses the rows it has already added to the table and then performs a join with new ones from YOUR_TABLE . 然后,第二个SELECT是递归所在的位置。由于它将Ancestors作为查询的一部分引用,它使用已添加到表中的行,然后使用YOUR_TABLE新行执行连接。 This will recursively find more and more rows to add to the end of each chain. 这将以递归方式查找要添加到每个链末尾的越来越多的行。

The last clause is the SELECT that uses this recursive table we've built up. 最后一个子句是SELECT ,它使用我们构建的这个递归表。 It does a simple GROUP BY since we've saved off the original ID in the Ancestor column, so the start and end are a simple MIN and MAX . 它做了一个简单的GROUP BY因为我们已经在Ancestor列中保存了原始ID,因此开始和结束都是一个简单的MINMAX

The tricky part is figuring out the ID of the last row in the chain. 棘手的部分是找出链中最后一行的ID。 There are two ways to do it, both illustrated in the query. 有两种方法可以做到,两者都在查询中说明。 You can either join back with the recursive table, in which case it will build the recursive table all over again, or you can attempt to keep track of the last item as you go. 您可以使用递归表连接回来,在这种情况下,它将重新构建递归表,或者您可以尝试跟踪最后一个项目。 (If building the recursive list of chained records is expensive, you definitely want to minimize the number of times you need to do that.) (如果构建链式记录的递归列表很昂贵,那么您肯定希望最大限度地减少需要执行此操作的次数。)

The way it keeps track as it goes is to keep track of its position in the chain (the level column -- notice how we add 1 each time we recurse), zero-pad it, and then stick the ID at the end. 它跟踪的方式是跟踪它在链中的位置( level列 - 注意我们每次递增时加1),零填充它,然后将ID粘贴在最后。 Then, getting the item with the max level is simply a MAX followed by stripping the level data out. 然后,获取具有最大level的项目只是MAX然后剥离level数据。

If the CTE has to recurse too much, it will generate an error, but I believe you can tweak that using the MAXRECURSION option. 如果CTE必须递归太多,它将产生错误,但我相信您可以使用MAXRECURSION选项进行调整。 The default is 100. If you have to set it higher than that, you may want to consider not using a recursive CTE to do this. 默认值为100.如果必须将其设置为高于此值,则可能需要考虑不使用递归CTE来执行此操作。

This also doesn't handle malformed data very well. 这也不能很好地处理格式错误的数据。 If you have two records with the same first or a record where first == end , then this won't work right and you may have to tweak the join conditions inside the CTE or go with another approach. 如果您有两个记录具有相同的first或记录,其中first == end ,那么这将无法正常工作,您可能必须调整CTE内的连接条件或采用其他方法。

This isn't the only way to do it. 这不是唯一的方法。 I believe it would be easier to follow if you built a custom procedure and did all the steps manually. 我相信如果您构建自定义过程并手动执行所有步骤,则会更容易理解。 But this has the advantage of operating in a single statement. 但这具有在单一陈述中操作的优点。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM