在SELECT INTO中保留ORDER BY

时间:2013-01-20 13:18:00

标签: sql-server tsql sql-order-by select-into

我有一个tSQL查询,它从一个表中获取数据并将其复制到一个新表中,但只有满足特定条件的行:

SELECT VibeFGEvents.* 
INTO VibeFGEventsAfterStudyStart 
FROM VibeFGEvents
LEFT OUTER JOIN VibeFGEventsStudyStart
ON 
    CHARINDEX(REPLACE(REPLACE(REPLACE(logName, 'MyVibe ', ''), ' new laptop', ''), ' old laptop', ''), excelFilename) > 0
    AND VibeFGEventsStudyStart.MIN_TitleInstID <= VibeFGEvents.TitleInstID
    AND VibeFGEventsStudyStart.MIN_WinInstId <= VibeFGEvents.WndInstID
WHERE VibeFGEventsStudyStart.excelFilename IS NOT NULL
ORDER BY VibeFGEvents.id

使用该表的代码依赖于它的顺序,上面的副本不保留我预期的顺序。即新表VibeFGEventsAfterStudyStart中的行在VibeFGEventsAfterStudyStart.id复制的VibeFGEvents.id列中不会单调增加。

在tSQL中,如何保留VibeFGEventsVibeFGEventsStudyStart行的排序?

9 个答案:

答案 0 :(得分:24)

为什么?

点是 - 表中的数据未订购。在SQL Server中,表的固有存储顺序是(如果已定义)聚簇索引。

插入数据的顺序基本上是“无关紧要的”。忘记数据写入表格的那一刻。

因此,即使你得到这些东西也没有任何好处。如果在处理数据时需要订单,则必须在获取它的选择上放置order by子句。其他任何东西都是随机的 - 即您的数据顺序未确定且可能会发生变化。

因此,在尝试实现插件时,在插件上设置特定顺序是没有意义的。

SQL 101:集合没有订单。

答案 1 :(得分:18)

我知道这有点旧,但我需要做类似的事情。我想将一个表的内容插入到另一个表中,但是以随机顺序插入。我发现我可以使用select top norder by newid()来完成此操作。没有&#39; top n&#39;,订单未被保留,第二个表的行与第一个表的顺序相同。但是,使用&#39; top n&#39;,保留了订单(在我的情况下是随机的)。我使用了&#39; n&#39;这大于行数。所以我的查询是:

insert Table2 (T2Col1, T2Col2)
  select top 10000 T1Col1, T1Col2
  from Table1
  order by newid()

答案 2 :(得分:0)

您不能使用ORDER BY进行此操作,但是如果在SELECT INTO之后在VibeFGEvents.id上创建聚簇索引,则该表将通过VibeFGEvents.id在磁盘上进行排序。

答案 3 :(得分:0)

之所以希望这个(特定的顺序)是因为您不能在子查询中定义顺序,因此,想法是,如果创建一个表变量,然后从该表变量中进行查询,会认为您会保留顺序(例如,连接必须按顺序排列的行(例如,对于XML或json)),但您不能这样做。 所以你会怎么做? 答案是通过选择中使用TOP来强制SQL对其进行排序(只需选择一个足以覆盖所有行的数字)。

答案 4 :(得分:0)

我发现了一种特定的情况,我们希望按列内容中的特定顺序创建新表:

  • 行数非常大(从200到2000万行),所以我们使用SELECT INTO而不是CREATE TABLE + INSERT,因为需要尽快加载(最小记录)。我们已经测试过使用trace flag 610加载具有聚簇索引的已经创建的空表,但是仍然比以下方法花费更长的时间。
  • 我们需要按特定列对数据进行排序以提高查询性能,因此在表加载后就创建了CLUSTERED INDEX。我们舍弃了创建非聚集索引的原因,因为它需要再次读取索引中不包含在有序列中的数据,而舍弃了创建完全覆盖的非聚集索引,因为这实际上会使所需的空间量增加一倍住桌子。

碰巧的是,如果您设法以某种方式创建具有已“排序”的列的表,则创建聚簇索引(具有相同的顺序)所需的时间要比不包含数据的时间少很多。下令。有时(您必须测试一下情况),对SELECT INTO中的行进行排序要比不按顺序加载和稍后创建聚簇索引快。

问题在于,SQL Server 2012+在执行ORDER BY或执行INSERT INTO时将忽略SELECT INTO列列表。如果您在ORDER BY上指定了IDENTITY列,或者插入的表中有SELECT INTO列,即 ,它将考虑IDENTITY列。确定标识值 ,而不是确定基础表中的实际存储顺序。在这种情况下,排序很可能会发生,但不能保证,因为它高度依赖于执行计划。

我们发现的一个技巧是,对SELECT INTO的结果执行UNION ALL会使引擎执行SORT(有时并不总是显式SORT运算符MERGE JOIN CONCATENATION等),如果您有一个ORDER BY列表。通过这种方式,select into已经按照我们稍后创建聚簇索引的顺序已经创建了新表,因此索引花费的时间更少。

因此您可以重写此查询:

SELECT
    FirstColumn = T.FirstColumn,
    SecondColumn = T.SecondColumn
INTO
    #NewTable
FROM
    VeryBigTable AS T
ORDER BY            -- ORDER BY is ignored!
    FirstColumn,
    SecondColumn

SELECT
    FirstColumn = T.FirstColumn,
    SecondColumn = T.SecondColumn
INTO
    #NewTable
FROM
    VeryBigTable AS T

UNION ALL

-- A "fake" row to be deleted
SELECT
    FirstColumn = 0,
    SecondColumn = 0

ORDER BY
    FirstColumn,
    SecondColumn

我们已经使用过几次这个技巧,但是我不能保证它总是会排序。我只是将其发布为可能的解决方法,以防有人遇到类似情况。

答案 5 :(得分:0)

我遇到了同样的问题,需要保留订单的原因之一是当我尝试使用ROLLUP来基于原始数据而不是该列中的平均值来获得加权平均值时。例如,假设我想根据四个商店位置所售出的商品数量来查看平均利润?通过创建方程式Profit / #Units = Avg,我可以很容易地做到这一点。现在,我在GROUP BY中加入了ROLLUP,这样我也可以看到所有位置的平均值。现在,我对自己想:“这是个很好的信息,但我希望按最佳平均水平(更差)的顺序查看它,并将“总体”保持在列表的底部(或顶部))。 ROLLUP将使您无法做到这一点,因此您可以采用其他方法。

为什么不根据需要保留的顺序(顺序)创建行号?

    SELECT OrderBy = ROW_NUMBER() OVER(PARTITION BY 'field you want to count' ORDER BY 'field(s) you want to use ORDER BY')
    , VibeFGEvents.*  
    FROM VibeFGEvents
    LEFT OUTER JOIN VibeFGEventsStudyStart
    ON 
        CHARINDEX(REPLACE(REPLACE(REPLACE(logName, 'MyVibe ', ''), ' new laptop', ''), ' old laptop', ''), excelFilename) > 0
        AND VibeFGEventsStudyStart.MIN_TitleInstID <= VibeFGEvents.TitleInstID
        AND VibeFGEventsStudyStart.MIN_WinInstId <= VibeFGEvents.WndInstID
    WHERE VibeFGEventsStudyStart.excelFilename IS NOT NULL

现在,您可以使用表中的OrderBy字段来设置值的顺序。我从上面的查询中删除了ORDER BY语句,因为它不影响数据如何加载到表中。

答案 6 :(得分:0)

只需将awk ' ##Starting awk program from here. { for(i=1;i<=NF;i++){ ##Starting for loop here to loop through each field of currnet line. if($i~/^\*/){ ##Checking condition if line starts from * then do following. sub(/^\*/,"",$i) ##Substituting starting * with NULL in current field. print $i ##Printing current field value here. } } } ' Input_file ##Mentioning Input_file name here. 添加到您的sql中,其数字应大于实际的行数:

top

答案 7 :(得分:-1)

尝试使用INSERT INTO代替SELECT INTO

INSERT INTO VibeFGEventsAfterStudyStart 
SELECT VibeFGEvents.* 
FROM VibeFGEvents
LEFT OUTER JOIN VibeFGEventsStudyStart
ON 
    CHARINDEX(REPLACE(REPLACE(REPLACE(logName, 'MyVibe ', ''), ' new laptop', ''), ' old laptop', ''), excelFilename) > 0
    AND VibeFGEventsStudyStart.MIN_TitleInstID <= VibeFGEvents.TitleInstID
    AND VibeFGEventsStudyStart.MIN_WinInstId <= VibeFGEvents.WndInstID
WHERE VibeFGEventsStudyStart.excelFilename IS NOT NULL
ORDER BY VibeFGEvents.id`

答案 8 :(得分:-1)

我在MS SQL 2012上进行了测试,它清楚地向我展示了,将...插入...选择...的顺序是有道理的。这是我所做的:

create table tmp1 (id int not null identity, name sysname);
create table tmp2 (id int not null identity, name sysname);

insert into tmp1 (name) values ('Apple');
insert into tmp1 (name) values ('Carrot');
insert into tmp1 (name) values ('Pineapple');
insert into tmp1 (name) values ('Orange');
insert into tmp1 (name) values ('Kiwi');
insert into tmp1 (name) values ('Ananas');
insert into tmp1 (name) values ('Banana');
insert into tmp1 (name) values ('Blackberry');

select * from tmp1 order by id;

我得到了这个清单:

  • 1个苹果
  • 2胡萝卜
  • 3个菠萝
  • 4橙
  • 5猕猴桃
  • 6个凤梨
  • 7个香蕉
  • 8个黑莓手机

这里没有惊喜。然后我以这种方式从tmp1复制到tmp2:

insert into tmp2 (name)
select name
from tmp1
order by id;

select * from tmp2 order by id;

我得到了和以前一样的确切答复。苹果到黑莓。 现在按相反的顺序进行测试:

delete from tmp2;

insert into tmp2 (name)
select name
from tmp1
order by id desc;

select * from tmp2 order by id;
  • 9个黑莓手机
  • 10个香蕉
  • 11个凤梨
  • 12猕猴桃
  • 13橙
  • 14个菠萝
  • 15胡萝卜
  • 16个苹果

因此,tmp2中的顺序也被颠倒了,因此排序依据在目标表中存在标识列时才有意义!