How to execute UNION without sorting? (SQL)

前端 未结 10 608
猫巷女王i
猫巷女王i 2021-01-30 21:34

UNION joins two results and remove duplicates, while UNION ALL does not remove duplicates.
UNION also sort the final output.

W

10条回答
  •  有刺的猬
    2021-01-30 22:06

    Consider these tables (Standard SQL code, runs on SQL Server 2008):

    WITH A 
         AS 
         (
          SELECT * 
            FROM (
                  VALUES (1), 
                         (2), 
                         (3), 
                         (4), 
                         (5), 
                         (6) 
                 ) AS T (col)
         ),
         B 
         AS 
         (
          SELECT * 
            FROM (
                  VALUES (9), 
                         (8), 
                         (7), 
                         (6), 
                         (5), 
                         (4) 
                 ) AS T (col)
         ), ...
    

    The desired effect is this to sort table A by col ascending, sort table B by col descending then unioning the two, removing duplicates, retaining order before the union and leaving table A results on the "top" with table B on the "bottom" e.g. (pesudo code)

    (
     SELECT *
       FROM A
      ORDER 
         BY col
    )
    UNION
    (
     SELECT *
       FROM B
      ORDER 
         BY col DESC
    );
    

    Of course, this won't work in SQL because there can only be one ORDER BY clause and it can only be applied to the top level table expression (or whatever the output of a SELECT query is known as; I call it the "resultset").

    The first thing to address is the intersection between the two tables, in this case the values 4, 5 and 6. How the intersection should be sorted needs to be specified in SQL code, therefore it is desirable that the designer specifies this too! (i.e. the person asking the question, in this case).

    The implication in this case would seem to be that the intersection ("duplicates") should be sorted within the results for table A. Therefore, the sorted resultset should look like this:

          VALUES (1), -- A including intersection, ascending
                 (2), -- A including intersection, ascending
                 (3), -- A including intersection, ascending
                 (4), -- A including intersection, ascending
                 (5), -- A including intersection, ascending
                 (6), -- A including intersection, ascending
                 (9), -- B only, descending 
                 (8), -- B only, descending  
                 (7), -- B only, descending 
    

    Note in SQL "top" and "bottom" has no inferent meaning and a table (other than a resultset) has no inherent ordering. Also (to cut a long story short) consider that UNION removes duplicate rows by implication and must be applied before ORDER BY. The conclusion has to be that each table's sort order must be explicitly defined by exposing a sort order column(s) before being unioned. For this we can use the ROW_NUMBER() windowed function e.g.

         ...
         A_ranked
         AS
         (
          SELECT col, 
                 ROW_NUMBER() OVER (ORDER BY col) AS sort_order_1
            FROM A                      -- include the intersection
         ),
         B_ranked
         AS
         (
          SELECT *, 
                 ROW_NUMBER() OVER (ORDER BY col DESC) AS sort_order_1
            FROM B
           WHERE NOT EXISTS (           -- exclude the intersection
                             SELECT * 
                               FROM A
                              WHERE A.col = B.col 
                            )
         )
    SELECT *, 1 AS sort_order_0 
      FROM A_ranked
    UNION
    SELECT *, 2 AS sort_order_0 
      FROM B_ranked
    ORDER BY sort_order_0, sort_order_1;
    

提交回复
热议问题