使用UNION和ORDER BY选择TOP N.

izz*_*zzy 3 t-sql sql-server

鉴于下面的示例,为什么在下面的查询2中使用UNION ALLwith两个SELECT TOP 5语句似乎不尊重该ORDER BY子句?

查询1返回预期结果,但不包括所需的联合.查询2演示了意外行为.查询3是我目前用于获得所需结果的解决方法.

CREATE TABLE #T1 ([ID] int IDENTITY(1,1), [Description] varchar(100), [Inactive] bit);
CREATE TABLE #T2 ([ID] int IDENTITY(1,1), [Description] varchar(100), [Inactive] bit);

INSERT INTO #T1([Description], [Inactive]) VALUES ('One', 1);
INSERT INTO #T1([Description], [Inactive]) VALUES ('Two', 0);
INSERT INTO #T1([Description], [Inactive]) VALUES ('Three', 1);
INSERT INTO #T1([Description], [Inactive]) VALUES ('Four', 0);
INSERT INTO #T1([Description], [Inactive]) VALUES ('Five', 1);
INSERT INTO #T1([Description], [Inactive]) VALUES ('Six', 0);
INSERT INTO #T1([Description], [Inactive]) VALUES ('Seven', 1);
INSERT INTO #T1([Description], [Inactive]) VALUES ('Eight', 1);
INSERT INTO #T1([Description], [Inactive]) VALUES ('Nine', 1);
INSERT INTO #T1([Description], [Inactive]) VALUES ('Ten', 0);

-- Query 1, works as expected giving all 4 records with Inactive = 0 plus one more
SELECT TOP 5 [ID], [Description], [Inactive]
FROM #T1
ORDER BY [Inactive], [Description]; 

-- Query 2, does not work as expected, as only 2 of the Inactive = 0 records are present
SELECT TOP 5 [ID], [Description], [Inactive]
FROM #T1
UNION ALL
SELECT TOP 5 [ID], [Description], [Inactive]
FROM #T2
ORDER BY [Inactive], [Description]; 

-- Query 3, Workaround to produce desired results
WITH T1 AS (
    SELECT TOP 5 [ID], [Description], [Inactive]
    FROM #T1
    ORDER BY [Inactive], [Description]
),
T2 AS (
    SELECT TOP 5 [ID], [Description], [Inactive]
    FROM #T2
    ORDER BY [Inactive], [Description]
)
SELECT [ID], [Description], [Inactive] FROM T1
UNION ALL
SELECT [ID], [Description], [Inactive] FROM T2
ORDER BY [Inactive], [Description]; 

DROP TABLE #T1;
DROP TABLE #T2;
Run Code Online (Sandbox Code Playgroud)

显然,解决方法对我有用,但我想了解为什么查询2不能达到我的预期.如果你想知道我为什么要打扰空表#T2,结果实际上受到WHERE我的生产示例中的一个子句的限制- 但是在这里留空是为了提供一个类似的例子,而不用费心去填充它.

如果你填充#T2以下内容,我发现结果同样奇怪 - 查询2只给出了四个结果Inactive = 0.

INSERT INTO #T2([Description], [Inactive]) VALUES ('Eleven', 1);
INSERT INTO #T2([Description], [Inactive]) VALUES ('Twelve', 0);
INSERT INTO #T2([Description], [Inactive]) VALUES ('Thirteen', 1);
INSERT INTO #T2([Description], [Inactive]) VALUES ('Fourteen', 0);
INSERT INTO #T2([Description], [Inactive]) VALUES ('Fifteen', 1);
INSERT INTO #T2([Description], [Inactive]) VALUES ('Sixteen', 0);
INSERT INTO #T2([Description], [Inactive]) VALUES ('Seventeen', 1);
INSERT INTO #T2([Description], [Inactive]) VALUES ('Eighteen', 1);
INSERT INTO #T2([Description], [Inactive]) VALUES ('Nineteen', 1);
INSERT INTO #T2([Description], [Inactive]) VALUES ('Twenty', 0);
Run Code Online (Sandbox Code Playgroud)

我在SQL Server 2014和SQL Server 2008 R2上运行此脚本的结果相同.

Tho*_*ner 7

因此,Union查询的工作原理是:执行查询,然后应用order by子句.所以

SELECT TOP 5 [ID], [Description], [Inactive]
FROM #T1
UNION ALL
SELECT TOP 5 [ID], [Description], [Inactive]
FROM #T2
ORDER BY [Inactive], [Description]; 
Run Code Online (Sandbox Code Playgroud)

你从#T1中选择五个任意选择的记录加上来自#T2的五个任意选择的记录然后你订购这些记录.所以你需要子查询或子句.例如:

SELECT * FROM
(
  (
    SELECT TOP 5 [ID], [Description], [Inactive]
    FROM #T1
    ORDER BY [Inactive], [Description]
  )
  UNION ALL
  (
    SELECT TOP 5 [ID], [Description], [Inactive]
    FROM #T2
    ORDER BY [Inactive], [Description]
  )
) t;
Run Code Online (Sandbox Code Playgroud)

因此,您的解决方法根本不是解决方法,而是正确的查询.

  • 好吧,它总是适用于联合的*结果*(这对于顶级条款来说太晚了). (2认同)

McN*_*ets 6

您应该在子查询中移动整个 UNION ALL:

SELECT *
FROM (SELECT TOP 5 [ID], [Description], [Inactive] 
      FROM #T1 
      ORDER BY [Inactive], [Description]
      UNION ALL
      SELECT TOP 5 [ID], [Description], [Inactive] 
      FROM #T2 
      ORDER BY [Inactive], [Description]) T3
ORDER BY [Inactive], [Description];
GO
Run Code Online (Sandbox Code Playgroud)
身份证 | 描述 | 不活跃
-: | :---------- | :--------
 4 | 四 | 错误的   
 6 | 六| 错误的   
10 | 10 十 | 错误的   
 2 | 两个 | 错误的   
 8 | 八 | 真的