SQL按组重新排序项目

时间:2010-08-20 18:06:13

标签: sql sql-server

假设我有一个如下所示的数据库:

tblA:
ID, Name, Sequence, tblBID
1     a       5        14
2     b       3        15
3     c       3        16
4     d       3        17

tblB:
ID, Group
14    1
15    1
16    2
17    3

我想对A进行排序,以便每组B的序列为1 ... n。 因此,在这种情况下,下降的序列应为1,2,1,1。

订购需要与当前订购一致,但不保证当前订购。

我不是一个sql主人,我确信有一个相当简单的方法可以做到这一点,但我真的不知道正确的路线。任何提示?

3 个答案:

答案 0 :(得分:5)

如果您使用的是SQL Server 2005+或更高版本,则可以使用排名功能:

Select tblA.Id, tblA.Name
    , Row_Number() Over ( Partition By tblB.[Group] Order By tblA.Id ) As Sequence
    , tblA.tblBID
From tblA
    Join tblB
        On tblB.tblBID = tblB.ID

Row_Number ranking function

这是另一个可以在SQL Server 2000及之前版本中使用的解决方案。

Select A.Id, A.Name
    , (Select Count(*)
        From tblB As B1
        Where B1.[Group] = B.[Group]
            And B1.Id < B.ID) + 1 As Sequence
    , A.tblBID
From tblA As A
    Join tblB As B
        On B.Id = A.tblBID

修改

  

还想明确表示我想实际更新tblA以反映正确的序列。

在SQL Server中,您可以在Update语句中使用其专有的From子句,如下所示:

Update tblA
Set Sequence =  (
                Select Count(*)
                From tblB As B1
                Where B1.[Group] = B.[Group]
                    And B1.Id < B.ID
                ) + 1
From tblA As A
    Join tblB As B
        On B.Id = A.tblBID

Hoyle ANSI解决方案可能类似于:

Update tblA
Set Sequence = (
                Select (Select Count(*)
                        From tblB As B1
                        Where B1.[Group] = B.[Group]
                            And B1.Id < B.ID) + 1
                From tblA As A
                    Join tblB As B
                        On B.Id = A.tblBID
                Where A.Id = tblA.Id
                )

修改

  

我们可以根据A.Sequence而不是B.ID来进行[内部组]比较吗?

Select A1.*
    , (Select Count(*)
        From tblB As B2
            Join tblA As A2
                On A2.tblBID = B2.Id
        Where B2.[Group] = B1.[Group]
            And A2.Sequence < A1.Sequence) + 1
From tblA As A1
    Join tblB As B1
        On B1.Id = A1.tblBID

答案 1 :(得分:2)

因为它是SQL 2000,所以我们不能使用窗口函数。没关系。

托马斯的询问很好并且会有效。但是,随着行数的增加,它们会变得越来越糟 - 具有不同的特征,具体取决于行的宽度(组数)和深度(每组的项目数)。这是因为那些查询使用了部分交叉连接,也许我们可以将其称为“金字塔交叉连接”,其中交叉部分限于小于左侧值的右侧值,而不是左侧交叉到所有正确的值。

怎么办?

我认为你会惊讶地发现,以下长而痛苦的剧本将在一定数量的数据(可能不是那么大)上胜过金字塔连接,最终, 包含非常大的数据集必须被视为尖叫表演者

CREATE TABLE #tblA (
   ID int identity(1,1) NOT NULL,
   Name varchar(1) NOT NULL,
   Sequence int NOT NULL,
   tblBID int NOT NULL,
   PRIMARY KEY CLUSTERED (ID)
)

INSERT #tblA VALUES ('a', 5, 14)
INSERT #tblA VALUES ('b', 3, 15)
INSERT #tblA VALUES ('c', 3, 16)
INSERT #tblA VALUES ('d', 3, 17)

CREATE TABLE #tblB (
   ID int NOT NULL PRIMARY KEY CLUSTERED,
   GroupID int NOT NULL
)
INSERT #tblB VALUES (14, 1)
INSERT #tblB VALUES (15, 1)
INSERT #tblB VALUES (16, 2)
INSERT #tblB VALUES (17, 3)

CREATE TABLE #seq (
   seq int identity(1,1) NOT NULL,
   ID int NOT NULL,
   GroupID int NOT NULL,
   PRIMARY KEY CLUSTERED (ID)
)

INSERT #seq
SELECT
   A.ID,
   B.GroupID
FROM
   #tblA A
   INNER JOIN #tblB B ON A.tblBID = b.ID
ORDER BY B.GroupID, A.Sequence

UPDATE A
SET A.Sequence = S.seq - X.MinSeq + 1
FROM
   #tblA A
   INNER JOIN #seq S ON A.ID = S.ID
   INNER JOIN (
      SELECT GroupID, MinSeq = Min(seq)
      FROM #seq
      GROUP BY GroupID
   ) X ON S.GroupID = X.GroupID

SELECT * FROM #tblA

DROP TABLE #seq
DROP TABLE #tblB
DROP TABLE #tblA

如果我理解正确,那么ORDER BY B.GroupID, A.Sequence是正确的。如果没有,您可以将A.Sequence切换为B.ID。

另外,我应该对临时表上的索引进行实验。对于一定数量的行,以及这些行的宽度和深度特征,在#seq表中的另外两列之一上进行聚类可能会有所帮助。

最后,可能存在不同的数据组织:将GroupID从#seq表中删除并再次加入。我怀疑情况会更糟,但并非百分百肯定。

答案 2 :(得分:0)

类似的东西:

SELECT a.id, a.name, row_number() over (partition by b.group order by a.id)
FROM tblA a
  JOIN tblB on a.tblBID = b.ID;