使用UNION INSERT INTO SELECT奇怪的顺序

时间:2012-09-10 21:20:04

标签: sql-server sql-server-2008 tsql union

我有一个典型的非规范化表格(tempTable),其中包含多个编号列(rep1rep2,...)。 所以我编写了一个脚本,将非规范化数据插入到规范化表(myTable)中:

insert into myTable
select idRep,rep FROM
(
    select idRep, ISNULL(rep1,'') as rep FROM tempTable
    union
    select idRep, ISNULL(rep2,'') as rep FROM tempTable
    union
    select idRep, ISNULL(rep3,'') as rep FROM tempTable
    union
    select idRep, ISNULL(rep4,'') as rep FROM tempTable
    union
    select idRep, ISNULL(rep5,'') as rep FROM tempTable
) as t

注意:表格myTable还包含一个自动递增的IDENTITYPRIMARY KEY

订单rep1,rep2,rep3,rep4,rep5在我的场景中非常重要。奇怪的是,当我执行脚本时,数据没有以正确的顺序插入,例如自动生成的id'1000'的值来自'rep3'而id'1001'的值来自'rep1'。< / p>

为什么?脚本是如何执行的?

3 个答案:

答案 0 :(得分:8)

在使用UNION时它没有按照你期望的顺序进行的原因是union会尝试强制执行uniquness,因此它会将所有这些行一起处理并按照最方便引擎的顺序将它们引出。

如果您切换到UNION ALL(不会尝试强加唯一性),Parado建议它不会进行处理,它们将按照您放入的顺序进入表格,几乎每时每刻。然而,这并不是很高兴的,其他过程中发生的某些非常特殊的情况(特别是那些以某种方式触及你的tempTable)可能会影响它。

如果您按照Kash的建议使用订单,则会获得ID的顺序(这可能很重要),但技术上并不是行插入的顺序(在实践中这很少很重要)。

MSDN上有一些很好的总结。

所以,这就解决了原因。至于如何获得你真正想要的东西,我会使用Kash的建议添加一个用于order by子句的列,但我会使用UNION ALL而不是UNION。使用UNION就像添加和隐含的“不同”要求一样,它占用了处理器周期并使查询计划更加复杂。

答案 1 :(得分:4)

您的外部Select没有订单,因此INSERT不会像看起来那样排序。

SQL Server中有一些Ordering Guarantees,带有ORDER BY的SELECT INSERT保证将身份值计算为引用:

  

INSERT查询使用SELECT和ORDER BY来填充行   保证如何计算身份值,但不保证计算身份的顺序   插入行

更改SQL以使其有序:

insert into myTable
select idRep,rep FROM
(
    select idRep, ISNULL(rep1,'') as rep, 1 as Grp FROM tempTable
    union
    select idRep, ISNULL(rep2,'') as rep, 2 as Grp FROM tempTable
    union
    select idRep, ISNULL(rep3,'') as rep, 3 as Grp FROM tempTable
    union
    select idRep, ISNULL(rep4,'') as rep, 4 as Grp FROM tempTable
    union
    select idRep, ISNULL(rep5,'') as rep, 5 as Grp FROM tempTable
) as t ORDER BY Grp

答案 2 :(得分:2)

尝试使用union all。它没有对数据进行排序:

insert into myTable
select idRep,rep FROM
(
    select idRep, ISNULL(rep1,'') as rep FROM tempTable
    union all
    select idRep, ISNULL(rep2,'') as rep FROM tempTable
    union all
    select idRep, ISNULL(rep3,'') as rep FROM tempTable
    union all
    select idRep, ISNULL(rep4,'') as rep FROM tempTable
    union all
    select idRep, ISNULL(rep5,'') as rep FROM tempTable
) as t