使用UNION和ORDER BY选择TOP N.

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

标签: sql-server tsql

鉴于下面的示例,为什么在下面的查询2中使用UNION ALL和两个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;

显然,解决方法对我有用,但我想了解为什么查询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);

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

3 个答案:

答案 0 :(得分:6)

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]; 

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

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;

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

答案 1 :(得分:0)

您应该在子查询中移动整个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
ID | Description | Inactive
-: | :---------- | :-------
 4 | Four        | False   
 6 | Six         | False   
10 | Ten         | False   
 2 | Two         | False   
 8 | Eight       | True    

答案 2 :(得分:0)

查询#2和#3之间存在重大差异 - #T1上的ORDER BY子句。

如果你没有指定ORDER BY原因,那么拇指规则是-in SQL查询," TOP"将只返回随机记录集。在你得到这些"随机"行,排序发挥作用。因此,没有"适当的"排序发生。