2010-08-24 131 views
3

选择我有一个表如下:在SQL Server 2005

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

我想选择如下:

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

但我不能这样做。请!帮我。谢谢!

回答

1

这个工作对我来说:

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

不要使用类似 “结束” 保留字列名

+0

我明白了。但是我有一个表如下: ID第一端 b 3分配8 -C 8 10 d 15 19 E-10 12 F-19战斗机23 我认为它假。 因为我要选择 ID第一端 一个-E 3.0 12 d-F 15 23 – 2010-08-24 03:42:33

+0

@Vuong:我认为,虽然,你可以扩展这一理念,以满足您的需求。如果您将计算列添加到您的选择中以生成分组标题(考虑使用'case'语句计算列),然后对计算进行分组,您仍然可以按照此处的建议应用“MIN”和“MAX”。 – kbrimington 2010-08-24 03:52:53

+0

@Voung Mao:这是一个与你在问题中发布的数据集不同的数据集 - 如果这是你处理的内容,你应该发布这个问题的细节,因为任何人都必须阅读你的评论以了解其他标准。目前还不清楚你如何知道该数据集中有两个组,或者他们开始​​和结束的位置。 – 2010-08-24 04:03:13

0

我相信你可以做到这一点使用recursive 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) 

对各部分做一个快速的解释:

WITH Ancestors AS (...)子句创建名称为Ancestors的公用表表达式(基本上是子查询)。该表达式中的第一个SELECT建立了一个基线:在它之前没有匹配条目的所有行。

然后,第二SELECT就是递归踢。因为它引用Ancestors作为查询的一部分,它使用它已添加到表中的行,然后进行与新的从YOUR_TABLE加入。这将递归地发现越来越多的行添加到每个链的末尾。

最后一项是SELECT,它使用我们构建的这个递归表。它做了一个简单的GROUP BY,因为我们保存了Ancestor列中的原始ID,所以开始和结束是一个简单的MINMAX

棘手的部分是找出链中最后一行的ID。有两种方法可以做到这一点,都在查询中说明。您可以使用递归表加入,在这种情况下,它将重新构建递归表,或者您可以尝试跟踪最后一个项目。 (如果构建链式记录的递归列表的代价很高,那么您肯定希望将您需要的次数降至最低。)

它跟踪它的方式是跟踪它在链中的位置(level列 - 注意每次我们递归时我们如何加1),零填充它,然后将ID粘贴到最后。然后,获取具有最大level的项目简单地是MAX,接着剥离level数据。

如果CTE必须递归太多,它会产生一个错误,但我相信你可以使用MAXRECURSION选项来调整。默认值为100.如果您必须将其设置得高于此值,则可能需要考虑不使用递归CTE来执行此操作。

这也不能很好地处理格式错误的数据。如果您有两个记录具有相同的first或记录first == end,那么这将无法正常工作,您可能需要调整CTE内部的连接条件或采用其他方法。

这不是唯一的方法。我相信如果您构建自定义过程并手动完成所有步骤,将会更容易。但是这具有以单一语句操作的优点。

+0

非常感谢你。你回答简单吗?我只使用小型数据库。 – 2010-08-25 03:28:44