36

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

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.idVibeFGEvents.id

在 T-SQL 中,我如何保留 in 中的行VibeFGEvents顺序VibeFGEventsStudyStart

4

9 回答 9

53

我知道这有点旧,但我需要做类似的事情。我想将一个表的内容插入另一个表,但顺序是随机的。我发现我可以通过使用select top nand来做到这一点order by newid()。如果没有“top n”,则不会保留顺序,并且第二个表的行顺序与第一个表相同。但是,使用“前 n”,顺序(在我的情况下是随机的)被保留。我使用了大于行数的“n”值。所以我的查询是这样的:

insert Table2 (T2Col1, T2Col2)
  select top 10000 T1Col1, T1Col2
  from Table1
  order by newid()
于 2014-04-23T07:17:38.540 回答
25

做什么的?

要点是——表中的数据没有排序。在 SQL Server 中,表的内在存储顺序是(如果已定义)聚集索引的顺序。

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

因此,即使你得到了这些东西,也没有任何收获。如果在处理数据时需要排序,则必须在获取它的 select 上放置 order by 子句。其他任何事情都是随机的 - 即您输入数据的顺序未确定并且可能会更改。

因此,当您尝试实现时,在插入件上具有特定顺序是没有意义的。

SQL 101:集合没有顺序。

于 2013-01-20T13:53:40.790 回答
5

我发现了一个特定场景,我们希望在列内容中以特定顺序创建新表:

  • 行数非常大(从 200 到 20 亿行),所以我们使用SELECT INTO而不是CREATE TABLE + INSERT因为需要尽可能快地加载(最小日志记录)。我们已经测试了使用跟踪标志 610来加载已创建的具有聚集索引的空表,但仍然比以下方法花费更长的时间。
  • 我们需要按特定列对数据进行排序以提高查询性能,因此我们CLUSTERED INDEX在表加载后创建一个。我们放弃创建非聚集索引,因为它需要再次读取未包含在索引中的有序列中的数据,并且我们放弃创建全覆盖非聚集索引,因为它实际上会使所需空间量翻倍拿着桌子。

碰巧的是,如果您设法以某种方式创建具有已“排序”列的表,则创建聚集索引(具有相同顺序)所花费的时间比未排序数据时要少得多。有时(您必须测试您的案例),对中的行进行排序SELECT INTO比无序加载并稍后创建聚集索引要快。

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

我们发现的一个技巧是,如果你有一个列表SELECT INTO,对 a 的结果执行 aUNION ALL会使引擎执行 a SORT(并不总是显式SORT运算符,有时是 aMERGE 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

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

于 2019-05-06T12:46:10.990 回答
5

只需top使用大于实际行数的数字添加到您的 sql 中:

SELECT top 25000 * 
into spx_copy
  from SPX
  order by date
于 2020-12-13T12:57:50.337 回答
2

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

于 2018-10-17T18:28:43.227 回答
2

我已经对 MS SQL 2012 进行了测试,它清楚地向我展示了 insert into ... select ... order by 是有道理的。这是我所做的:

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 中的顺序也颠倒了,所以当目标表中有标识列时order by才有意义!

于 2020-08-25T08:16:46.590 回答
0

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

于 2019-02-07T02:45:58.843 回答
0

我遇到了同样的问题,我需要保留订单的一个原因是当我尝试使用 ROLLUP 根据原始数据而不是该列中的平均值来获取加权平均值时。例如,假设我想查看基于四个商店位置销售的单位数量的平均利润?通过创建等式利润/#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 语句,因为它不会影响数据如何加载到表中。

于 2019-08-15T14:50:57.227 回答
-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`
于 2013-01-20T14:03:13.130 回答