在 Oracle 上使用 ORDER 插入
Posted
技术标签:
【中文标题】在 Oracle 上使用 ORDER 插入【英文标题】:INSERT with ORDER on Oracle 【发布时间】:2009-08-27 09:47:42 【问题描述】:在 Oracle 10g 上,我们需要将视图中的记录插入表中,以支持本身没有排序或 ORDER 选项的哑客户端应用程序。有什么方法可以控制我们的 INSERT 语句将记录添加到目标表的顺序吗?
【问题讨论】:
你为什么需要它?是否有一些触发器可以在插入时执行某些操作? 与***.com/questions/33841234/…相关 @zendar 如果这就像我需要对国家表做的事情,可能会有记录绑定到网页上的下拉列表或列表框,并希望它们都按字母顺序排列按名称,而不是按 ID 的数字顺序,并且有新记录作为事后的想法或由于当前事件而添加并变得无序,需要重新编码页面以具有排序的下拉列表/列表框,当您可以修复它时而是在数据库中。 @vapcguy 阅读了所选答案的第一行 - 如果没有“ORDER BY”,即使您按排序顺序插入记录,Oracle 也会以什么顺序返回记录。答案是七年了,它仍然有效。 @zendar “即使您按排序顺序插入记录,您也不知道 Oracle 将按什么顺序返回记录”- B.S.如何实际尝试我在下面发布的答案。你是反对者吗?我证明我能做到。是的,ORDER BY
不会在普通的INSERT
上强制执行。从接受的答案中添加/*+APPEND*/
提示(他驳回了这个提示,这就是为什么我否决了他并添加了我自己的例子,它在哪里起作用)和voilà,它起作用了!这就是为什么 ORDER BY
首先存在的原因 - 如果您按照找到的顺序附加行,这就是您控制插入的方式。
【参考方案1】:
您可以不可靠地控制 Oracle 在没有 ORDER BY
的情况下检索表行的顺序。
此外,如果没有/*+APPEND*/
提示,Oracle 会将行物理存储在有空间的堆表中,可能不在表的末尾!您可能认为 Oracle 按顺序插入它们,但任何 DML 或并发活动(插入 2 个以上会话)可能会产生不同的物理组织。
您可以使用INDEX ORGANIZED table 按PK 的顺序存储行。此后对该表的大多数简单查询将产生一组已排序的行。 但是,如果您不指定 ORDER BY,这并不能保证 oracle 会按该顺序选择行(取决于查询和访问路径,行可能以任何顺序出现)。
您也可以使用带有 order by 的视图,如果您无法触摸应用程序,这可能是您最好的选择(重命名表,使用表名创建视图,让应用程序认为它查询桌子)。我不知道你的情况是否可行。
【讨论】:
+1 创建视图应该会产生预期的结果,并且还可以避免重复数据。事实上 OP 提到他们已经在使用一个视图,所以也许只是添加一个 ORDER BY 就足够了。 “您无法可靠地控制 Oracle 将行物理存储在堆表中的顺序”......我认为这并不完全正确。直接路径插入将保留范围内的物理顺序,因此可用于改进数据段压缩。不过,这是一个专门的案例,当然选择顺序完全不能保证。 -1 应该说如何使用/*+APPEND*/
提示,而不是仅仅提到它,然后将其驳回。这实际上是如何做的答案!对我来说效果很好!【参考方案2】:
除非您指定 ORDER BY,否则您永远无法保证 Oracle 从 SELECT 返回行的顺序
【讨论】:
【参考方案3】:只需使用 ORDER BY。类似的东西
INSERT INTO table
(
SELECT
column1, column2
FROM
view
ORDER BY
column2
)
编辑,这实际上不起作用。您可以创建一个具有适当顺序的行的临时视图,然后进行插入。
【讨论】:
您不必控制插入...请参阅@Vincent 的回答。即使您将它们“按顺序”插入,没有 ORDER BY 的选择也会失败(迟早),这种情况下最好的方法是按视图排序...【参考方案4】:您的主要问题,即您的应用程序未在其查询中添加 ORDER BY 的问题,可能会通过在您希望排序的列上使用索引来解决,然后使用存储的大纲来导致使用索引访问表的查询。
您必须进行测试以查看这是否有效 - 请注意,仅添加 INDEX() 提示可能还不够,因为优化器可能会找到一种方法来尊重提示而不访问索引以正确的顺序;如果查询连接到其他表,则排序可能会丢失。
【讨论】:
【参考方案5】:是的,IS可以控制您的订单。我通过经验发现我有一张国家/地区表,我们将其命名为OLD_COUNTRIES
,看起来像这样:
-----------------------------------
| ID | CODE | NAME |
-----------------------------------
|112099 | AF | Afghanistan |
|112100 | AA | Albania |
|... | .. | ... |
|112358 | ZB | Zimbabwe |
|112359 | AZ | Azores Islands |
|... | .. | ... |
-----------------------------------
我希望在主要国家列表之后添加的项目(如亚速尔群岛和后来添加的其他岛屿)在我将它们插入新表时与其他国家/地区按字母顺序实际出现:
CREATE TABLE MYAPP.COUNTRIES
(
ID NUMBER(*, 0)
, CODE NVARCHAR2(20)
, NAME NVARCHAR2(250)
);
然后我运行它以使其工作:
INSERT /*+ append */ INTO MYAPP.COUNTRIES (ID, CODE, NAME)
SELECT ID, CODE, NAME FROM MYAPP.OLD_COUNTRIES_TABLE ORDER BY NAME ASC;
COMMIT;
而我的新 COUNTRIES
表是按名称按字母顺序排列的。
注意:COMMIT
是必需的,否则当您尝试在 Oracle SQL Developer 中打开表查看时,您会收到错误:ORA-12838: cannot read/modify an object after modifying it in parallel
。
注意:如果您不使用/*+ append */
,它将不会按照您指定的顺序插入 - 它将忽略ORDER BY
。而且我知道,当我使用/*+ append */
时,它使用了我的ORDER BY
,而不仅仅是默认为旧表或新表上的主键,因为它们都没有主键。
CAVEAT:根据已接受答案的作者的说法,附加提示与“任何 DML 或并发活动......可能会产生不同的物理组织”一样。虽然可能是真的,但在此期间不要执行任何并发操作,并且在您的代码中使用 COMMIT
语句,这样就不会发生这种情况!
【讨论】:
感谢那些自己投赞成票或反对投反对票的人-想要注意-我之前曾遇到过一些挫败,但这不起作用,但我的例子证明了这一点。如果您想提倡并发操作破坏它的可能性,这就是为什么我添加了如何避免这种情况(顺便说一句,很容易做到)作为警告,所以请在评论/投票之前阅读我的整个答案。此外,作为一个正在创建的新表,甚至没有人应该使用这个表。现有数据被复制,也许是另一回事,但这就是我们有维护窗口的原因。谢谢。 。 .代码工作一次的演示并不能保证它总是工作。这就像说因为2 + 2
是4
,所以将任何整数添加到自身总是4
。
@GordonLinoff 是的,但考虑到我上面设置的条件几乎总是相同的(在你的例子中总是“2”而不是“3”等),它应该几乎总是工作。 “几乎总是”警告允许人们在应该使用“2”时使用“3”的情况:例如。您不能将脚本运行到表中,因为它们正在被修改并期望完美的结果。如果人们将源数据注入到源表中,则无法在源表上运行脚本来复制数据。这些事情应该不言而喻。锁定内容或创建副本,然后替换为您制作的副本。以上是关于在 Oracle 上使用 ORDER 插入的主要内容,如果未能解决你的问题,请参考以下文章
IntegrityError 在表“orders_order”上插入或更新违反外键约束“