按同一分区的排序顺序对连接的结果集进行分组

时间:2017-11-10 07:23:21

标签: sql sql-server

我有3个表代表关系/层次结构的表:

A.ID  
    B.AID
        C.BID

DDL和sqlfiddle :(在实际数据库中有更多列)

CREATE TABLE A
    ([ID] int, [Title] varchar(50), [Sort] int)
;    
INSERT INTO A
    ([ID], [Title], [Sort])
VALUES
    (5, 'a5', 1),
    (4, 'a4', 2),
    (7, 'a7', 3)
;

CREATE TABLE B
    ([ID] int, [AID] int, [Title] varchar(50), [Sort] int)
;    
INSERT INTO B
    ([ID], [AID], [Title], [Sort])
VALUES
    (2, 5, 'b2', 1), -- a5
    (3, 5, 'b3', 2),
    (8, 4, 'b8', 1), -- a4
    (4, 7, 'b4', 1), -- a7
    (6, 7, 'b6', 2),
    (5, 7, 'b5', 3)
;

CREATE TABLE C
    ([ID] int, [BID] int, [Title] varchar(50), [Sort] int)
;    
INSERT INTO C
    ([ID], [BID], [Title], [Sort])
VALUES
    (1, 2, 'c1', 1), -- b2
    (8, 2, 'c8', 2),
    (2, 3, 'c2', 1), -- b3
    (3, 8, 'c3', 1), -- b8
    (7, 4, 'c7', 1), -- b4
    (4, 6, 'c4', 1), -- b6
    (6, 5, 'c6', 1), -- b5
    (5, 5, 'c5', 2)
;

我需要获得一个按表关系分组的结果集,但顺序是由每个组的Sort列确定的​​(分区?)。

我用过:

SELECT * FROM 
A INNER JOIN B ON A.ID = B.AID
INNER JOIN C ON B.ID = C.BID
ORDER BY A.Sort, B.Sort, C.Sort;

获得所需结果:

ID    Title  Sort  ID    AID   Title  Sort  ID    BID   Title  Sort
5     a5     1     2     5     b2     1     1     2     c1     1
5     a5     1     2     5     b2     1     8     2     c8     2
5     a5     1     3     5     b3     2     2     3     c2     1
4     a4     2     8     4     b8     1     3     8     c3     1
7     a7     3     4     7     b4     1     7     4     c7     1
7     a7     3     6     7     b6     2     4     6     c4     1
7     a7     3     5     7     b5     3     6     5     c6     1
7     a7     3     5     7     b5     3     5     5     c5     2

或包含Sort列的层次结构视图:

a5 (1)
|__
    b2 (1)
    |__
        c1 (1)
        c8 (2)    
    b3 (2)
    |__
        c2 (1)
a4 (2)
|__
    b8 (1)
    |__
        c3 (1)

a7 (3)
|__
    b4 (1)
    |__
        c7 (1)
    b6 (2)
    |__
        c4 (1)
    b5 (3)
    |__
        c6 (1)
        c5 (2)

Sort值未“标准化”时,问题就开始了。当a5a7 Sort == 1(相同的值)时,例如。或者当所有A.Sort值都设置为0时(表BC也是如此)。

注意:“未规范化”是指每个表中的Sort值可以是任意数。 e.g。

UPDATE A SET Sort = 0;

看到分组中断(a7),我得到随机结果:

ID    Title  Sort  ID    AID   Title  Sort  ID    BID   Title  Sort
4     a4     0     8     4     b8     1     3     8     c3     1
7     a7     0     4     7     b4     1     7     4     c7     1 <-- a7
5     a5     0     2     5     b2     1     1     2     c1     1
5     a5     0     2     5     b2     1     8     2     c8     2
5     a5     0     3     5     b3     2     2     3     c2     1
7     a7     0     6     7     b6     2     4     6     c4     1 <-- a7
7     a7     0     5     7     b5     3     6     5     c6     1
7     a7     0     5     7     b5     3     5     5     c5     2

我想尽最大努力让Sort订单正确,但保持分组/层次结构正确。怎么办呢?

我也尝试过:

ORDER BY 
  A.Sort, A.ID,
  B.Sort, B.ID,
  C.Sort, C.ID;

似乎有效。但不知何故,这对我来说并不合适。 我几乎可以肯定解决方案是类似

ROW_NUMBER() OVER(PARTITION BY A.ID, B.ID, C.ID ORDER BY A.Sort, B.Sort, C.Sort) 

但我无法得到正确的结果。

编辑#1 :我认为这应该有效,但我仍然需要测试,因为我不确定:

SELECT 
  ROW_NUMBER() OVER(ORDER BY A.Sort) s1,
  ROW_NUMBER() OVER(PARTITION BY A.ID ORDER BY B.Sort) s2,
  ROW_NUMBER() OVER(PARTITION BY B.ID ORDER BY C.Sort) s3,
  * 
FROM 
  A INNER JOIN B ON A.ID = B.AID
  INNER JOIN C ON B.ID = C.BID  
  ORDER by s1, s2, s3

编辑#2 :编辑后,编辑#1没有按预期工作。 似乎工作的唯一方法就是简单:

ORDER BY A.Sort, A.ID, 
         B.Sort, B.ID, 
         C.Sort, C.ID; -- actually the C.ID is not needed

2 个答案:

答案 0 :(得分:0)

试试这个

SELECT * FROM 
A INNER JOIN B ON A.ID = B.AID
INNER JOIN C ON B.ID = C.BID
ORDER BY CASE WHEN A.Sort = 0 THEN A.ID ELSE A.Sort END,
         CASE WHEN B.Sort = 0 THEN B.ID ELSE B.Sort END,
         CASE WHEN C.Sort = 0 THEN C.ID ELSE C.Sort END

编辑随机数 - 请尝试以下

SELECT *,DENSE_RANK() OVER (ORDER BY A.ID,A.Sort) AS RNK1 
        ,DENSE_RANK() OVER (ORDER BY B.ID,B.Sort) AS RNK2
        ,DENSE_RANK() OVER (ORDER BY C.ID,C.Sort) AS RNK3
FROM         
A INNER JOIN B ON A.ID = B.AID
INNER JOIN C ON B.ID = C.BID
ORDER BY RNK1,RNK2,RNK3

答案 1 :(得分:0)

如果它只是你要保留的'分组',你可以通过id或title的条件排序来完成这个,如下所示:

SELECT * 
FROM A INNER JOIN B ON A.ID = B.AID
       INNER JOIN C ON B.ID = C.BID
ORDER BY CASE WHEN A.Sort = 0 THEN A.ID ELSE A.Sort END, 
         CASE WHEN B.Sort = 0 THEN B.ID ELSE B.Sort END, 
         CASE WHEN C.Sort = 0 THEN C.ID ELSE C.Sort END;

这样,如果排序值为零,它将仅按id排序。 您还可以通过子查询获取MAX和MIN排序值,并检查它们是否相等。

更新:MAX / MIN聚合示例:

SELECT * 
FROM A INNER JOIN B ON A.ID = B.AID
       INNER JOIN C ON B.ID = C.BID
       LEFT JOIN (SELECT MAX(Sort) amax, MIN(Sort) amin FROM A) aggA ON 1 = 1
       LEFT JOIN (SELECT MAX(Sort) bmax, MIN(Sort) bmin FROM A) aggB ON 1 = 1
       LEFT JOIN (SELECT MAX(Sort) cmax, MIN(Sort) cmin FROM A) aggC ON 1 = 1
ORDER BY CASE WHEN amax = amin THEN A.ID ELSE A.Sort END, 
         CASE WHEN bmax = bmin THEN B.ID ELSE B.Sort END, 
         CASE WHEN cmax = cmin THEN C.ID ELSE C.Sort END;

UPDATE2: 此尝试计算唯一的排序值和ID,并检查它们是否相同。如果它们不同,则意味着两个或多个id具有相同的排序值,因此按id排序。 也许这更接近你需要的解决方案

SELECT * 
FROM A INNER JOIN B ON A.ID = B.AID
       INNER JOIN C ON B.ID = C.BID
       LEFT JOIN (SELECT COUNT(DISTINCT Sort) sorts, COUNT(ID) ids FROM A) aggA ON 1=1
       ...
ORDER BY CASE WHEN aggA.sorts <> aggA.ids THEN A.ID ELSE A.Sort END,
         ...
         ;