使用UNION的意外结果

时间:2017-08-31 10:57:29

标签: sql sql-server

我正在编写一个存储过程来填充多选控件,我想从数据库表中删除至少100个选项,但我想确保我带回用户已经拥有的所有选项地选择。

现有查询仅返回前100行:

SELECT TOP 100
    t2.Id,
    t2.value2
FROM
    table1 t1
INNER JOIN
    table2 t2 
  ON
    t1.clientId = t2.clientId AND t1.Id = t2.Id
WHERE
    t1.clientId = 1

我已在此查询中添加了UNION,以确保我始终可以获取所选的选项:

SELECT TOP 100
        t2.Id,
        t2.value2
  FROM
        table1 t1
INNER JOIN
        table2 t2 ON
        t1.clientId = t2.clientId AND t1.Id = t2.Id
 WHERE
        t1.clientId = 1
 UNION 
SELECT  t2.id, 
        t_si.externalCertificateNumber 
  FROM  table2 t2
INNER JOIN 
        Table3 t3 
    ON t2.value = 333

在我的一个测试示例中,有4个先前选择的选项。其中3个将返回前100名(1不是,因此需要更新)。

我预计会回到101行,并且很惊讶地回到104行。我认为这与DISTINCT中固有的UNION工作方式有关。

然而,我也看到了 100 返回的不同结果。有些(超过3个)不同的值出现。还有一些在之前的TOP 100中消失了!

所以我的问题是,UNION是否有某种类型的内置排序会影响结果?或者还有其他事情发生在这里吗?

----------------------------- EDIT ----------------- -------------

如果我将上述查询更改为TOP 5:

之前选择的值是:

1, Blue
2, Green
20, Violet
100 Indigo

SELECT的结果本身如下:

1, Blue
2, Green
7, Red
15, Cyan
20, Violet

SELECT和UNION的结果

1, Blue
2, Green
33, Cyan
20, Violet
24, Yellow
100, Indigo
60, Aero
40, Amber
25, Black

这是一个不是来自DB的组成示例,但请注意SELECT和UNION中缺少SELECT中的红色

2 个答案:

答案 0 :(得分:2)

UNION运算符会删除重复记录。要保留所有记录,请使用UNION ALL

SELECT TOP 100
    t2.Id,
    t2.value2
FROM
    table1 t1
    INNER JOIN
    table2 t2 ON
        t1.clientId = t2.clientId AND
        t1.Id = t2.Id
WHERE
    t1.clientId = 1
UNION ALL
SELECT t2.id, t_si.externalCertificateNumber FROM table2 t2
    INNER JOIN Table3 t3 
    ON t2.value = 333

TOP 100查询中缺少的记录可以通过UNION轻松解释,也可以通过结果集中不同的记录数来解释。

答案 1 :(得分:2)

  

我开始怀疑添加UNION已经改变了   执行计划,因为没有明确设置的顺序,这个   正在影响即将发生的事情。

你是对的。

没有TOP 100

ORDER BY会返回一些 100行。未定义返回哪些行。

没有UNION的查询的第一个版本返回了一组100行,带有UNION的查询的第二个版本返回了另一组100行。

要获得可预测和预期的结果,您应该将ORDER BY添加到使用TOP的查询中。理想情况下,排序应该是明确的。

是的,UNION确实有“内置”排序。如果你看一下执行计划,你通常会看到它。

我更喜欢使用公用表表达式(CTE)来编写复杂的查询,如下所示:

WITH
CTE
AS
(
    SELECT TOP 100
        t2.Id,
        t2.value2
    FROM
        table1 t1
        INNER JOIN table2 t2 
            ON t1.clientId = t2.clientId 
            AND t1.Id = t2.Id
    WHERE
        t1.clientId = 1
    ORDER BY
        t2.Id
)
SELECT
    Id,
    value2
FROM
    CTE

UNION 

SELECT
    t2.id,
    t_si.externalCertificateNumber 
FROM  
    table2 t2
    INNER JOIN Table3 t3 ON t2.value = 333
;