使用循环对列表进行排序
Sort a list using loops
我还是 PL/SQL 的新手,我尝试了很长时间自己解决以下问题,但我无法找到解决方案(而且我没有找到类似的问题解决了)因此我想我问自己。
我正在尝试按日期(表示 'time_left_to_live')对列表进行排序,以便结果是相同的列表,但是,如您所猜,按日期排序。
编辑:也许我应该更准确:
我最初的目标是编写一个 AFTER UPDATE TRIGGER,如果 time_left_to_live 更新,它会再次对 table 进行排序。我的想法是编写一个对列表进行排序(或更多更新)并调用它的过程。
说明示例:
UPDATE testv1 SET time_left_to_live = '01.05.2020' WHERE list_id = 3;
所以之后 list_id 3 的旧数据应该是 1,因为它的剩余生存时间最短,其他数据应该递增 1。
1 01.05.2020
2 03.05.2020
3 31.05.2020
我希望我能把情况解释得很好。
我用嵌套循环尝试过,但我根本无法在里面思考。感谢任何帮助。
我的table:
DROP TABLE testv1;
DROP PROCEDURE wlp_sort;
CREATE TABLE testv1(
list_ID INT,
time_left_to_live DATE
);
INSERT INTO testv1 VALUES (3, '01.06.2020');
INSERT INTO testv1 VALUES (2, '31.05.2020');
INSERT INTO testv1 VALUES (1, '03.05.2020');
--UPDATE testv1 SET time_left_to_live = '01.05.2020' WHERE list_ID = 2;
SELECT * FROM testv1;
我建议你不要那样做。我认为您作为示例发布的 table 正是 - 示例 。实际上,它更复杂。此外,我认为 list_id
列标识每一行,这是好的,但用于排序目的是错误的,特别是不是你想要的方式 - 通过数据库触发器更新它的值。
那你该怎么办?使用 ORDER BY
因为它是保证行将按所需顺序返回的唯一机制。很久以前,我认为最后一个 Oracle 数据库版本是 8i,你可以使用 group by
子句,它 在后台 为你排序结果,但那些时代已经过去了 - order by
是。
这是我的意思的一个例子。
这是您 table 的原始内容。查询包括附加列 - 您可能想要使用的列 - row_number
动态分析函数 "calculates" 序号。首先,它匹配 list_id
值。
SQL> select list_id,
2 time_left_to_live,
3 row_number() over (order by time_left_to_live) rn
4 from testv1
5 order by time_left_to_live;
LIST_ID TIME_LEFT_ RN
---------- ---------- ----------
1 03.05.2020 1
2 31.05.2020 2
3 01.06.2020 3
SQL>
让我们更新一行(同样来自您的示例),看看会发生什么(请注意,我使用了日期文字;您使用 字符串 更新了日期列。Oracle 确实隐式尝试了- 并成功 - 将其转换为有效的日期值,但你不应该依赖它。01.05.2020 可能是 5 月 1 日或 1 月 5 日,具体取决于日期格式,这可能会发生变化并且在不同的数据库中可能会有所不同。另一方面,日期文字 总是 格式 date 'yyyy-mm-dd'
并且没有混淆):
SQL> update testv1 set time_left_to_live = date '2020-05-01' where list_id = 2;
1 row updated.
SQL> select list_id,
2 time_left_to_live,
3 row_number() over (order by time_left_to_live) rn
4 from testv1
5 order by time_left_to_live;
LIST_ID TIME_LEFT_ RN
---------- ---------- ----------
2 01.05.2020 1
1 03.05.2020 2
3 01.06.2020 3
SQL>
ORDER BY
子句对数据进行排序,但现在list_id
和rn
不同; list_id
没变,但是rn
代表新订单。
如果您的下一步是对序号为 1
的行执行 某事,您只需使用我建议的查询作为内联视图并获取rn = 1
:
的值
SQL> select list_id,
2 time_left_to_live
3 from (select list_id,
4 time_left_to_live,
5 row_number() over (order by time_left_to_live) rn
6 from testv1
7 )
8 where rn = 1;
LIST_ID TIME_LEFT_
---------- ----------
2 01.05.2020
SQL>
我建议你使用这个选项。
与数据库触发器相关的其他缺点:如果您编写修改 list_id
的更新语句,没问题 - 它在触发器 外部 和 list_id
之外工作并且 rn
再次 同步 :
SQL> update testv1 a set
2 a.list_id = (select x.rn
3 from (select b.list_id,
4 b.time_left_to_live,
5 row_number() over (order by b.time_left_to_live) rn
6 from testv1 b
7 ) x
8 where x.list_id = a.list_id
9 );
3 rows updated.
SQL> select list_id,
2 time_left_to_live,
3 row_number() over (order by time_left_to_live) rn
4 from testv1
5 order by time_left_to_live;
LIST_ID TIME_LEFT_ RN
---------- ---------- ----------
1 01.05.2020 1
2 03.05.2020 2
3 01.06.2020 3
SQL>
但是,在触发器中,您修改一个列,该列触发一个触发器,该列修改一个列,该列触发一个触发器,该触发器修改一个列...直到您超过最大递归数 SQL 级别,然后 Oracle引发错误。
或者,如果您计划从 table select 然后用它做一些事情,您将点击 变异 table 错误,因为您不能从正在修改的 table 中 select。的确,您可能会使用复合触发器(或者 - 在以前的 Oracle 数据库版本中 - 包和自定义类型),但是 - 再一次,在我看来,这不是你应该处理问题的方式。
完全同意@Littlefoot。但是,即使您可以在触发器中构建用户写入排序,也没有隔离区在写入时保留顺序。根据定义,table 是一组无序的行,SQL 引擎没有义务维护所显示行的任何顺序。保证任意顺序的方式是order by clause
我还是 PL/SQL 的新手,我尝试了很长时间自己解决以下问题,但我无法找到解决方案(而且我没有找到类似的问题解决了)因此我想我问自己。
我正在尝试按日期(表示 'time_left_to_live')对列表进行排序,以便结果是相同的列表,但是,如您所猜,按日期排序。
编辑:也许我应该更准确:
我最初的目标是编写一个 AFTER UPDATE TRIGGER,如果 time_left_to_live 更新,它会再次对 table 进行排序。我的想法是编写一个对列表进行排序(或更多更新)并调用它的过程。
说明示例:
UPDATE testv1 SET time_left_to_live = '01.05.2020' WHERE list_id = 3;
所以之后 list_id 3 的旧数据应该是 1,因为它的剩余生存时间最短,其他数据应该递增 1。
1 01.05.2020
2 03.05.2020
3 31.05.2020
我希望我能把情况解释得很好。
我用嵌套循环尝试过,但我根本无法在里面思考。感谢任何帮助。
我的table:
DROP TABLE testv1;
DROP PROCEDURE wlp_sort;
CREATE TABLE testv1(
list_ID INT,
time_left_to_live DATE
);
INSERT INTO testv1 VALUES (3, '01.06.2020');
INSERT INTO testv1 VALUES (2, '31.05.2020');
INSERT INTO testv1 VALUES (1, '03.05.2020');
--UPDATE testv1 SET time_left_to_live = '01.05.2020' WHERE list_ID = 2;
SELECT * FROM testv1;
我建议你不要那样做。我认为您作为示例发布的 table 正是 - 示例 。实际上,它更复杂。此外,我认为 list_id
列标识每一行,这是好的,但用于排序目的是错误的,特别是不是你想要的方式 - 通过数据库触发器更新它的值。
那你该怎么办?使用 ORDER BY
因为它是保证行将按所需顺序返回的唯一机制。很久以前,我认为最后一个 Oracle 数据库版本是 8i,你可以使用 group by
子句,它 在后台 为你排序结果,但那些时代已经过去了 - order by
是。
这是我的意思的一个例子。
这是您 table 的原始内容。查询包括附加列 - 您可能想要使用的列 - row_number
动态分析函数 "calculates" 序号。首先,它匹配 list_id
值。
SQL> select list_id,
2 time_left_to_live,
3 row_number() over (order by time_left_to_live) rn
4 from testv1
5 order by time_left_to_live;
LIST_ID TIME_LEFT_ RN
---------- ---------- ----------
1 03.05.2020 1
2 31.05.2020 2
3 01.06.2020 3
SQL>
让我们更新一行(同样来自您的示例),看看会发生什么(请注意,我使用了日期文字;您使用 字符串 更新了日期列。Oracle 确实隐式尝试了- 并成功 - 将其转换为有效的日期值,但你不应该依赖它。01.05.2020 可能是 5 月 1 日或 1 月 5 日,具体取决于日期格式,这可能会发生变化并且在不同的数据库中可能会有所不同。另一方面,日期文字 总是 格式 date 'yyyy-mm-dd'
并且没有混淆):
SQL> update testv1 set time_left_to_live = date '2020-05-01' where list_id = 2;
1 row updated.
SQL> select list_id,
2 time_left_to_live,
3 row_number() over (order by time_left_to_live) rn
4 from testv1
5 order by time_left_to_live;
LIST_ID TIME_LEFT_ RN
---------- ---------- ----------
2 01.05.2020 1
1 03.05.2020 2
3 01.06.2020 3
SQL>
ORDER BY
子句对数据进行排序,但现在list_id
和rn
不同; list_id
没变,但是rn
代表新订单。
如果您的下一步是对序号为 1
的行执行 某事,您只需使用我建议的查询作为内联视图并获取rn = 1
:
SQL> select list_id,
2 time_left_to_live
3 from (select list_id,
4 time_left_to_live,
5 row_number() over (order by time_left_to_live) rn
6 from testv1
7 )
8 where rn = 1;
LIST_ID TIME_LEFT_
---------- ----------
2 01.05.2020
SQL>
我建议你使用这个选项。
与数据库触发器相关的其他缺点:如果您编写修改 list_id
的更新语句,没问题 - 它在触发器 外部 和 list_id
之外工作并且 rn
再次 同步 :
SQL> update testv1 a set
2 a.list_id = (select x.rn
3 from (select b.list_id,
4 b.time_left_to_live,
5 row_number() over (order by b.time_left_to_live) rn
6 from testv1 b
7 ) x
8 where x.list_id = a.list_id
9 );
3 rows updated.
SQL> select list_id,
2 time_left_to_live,
3 row_number() over (order by time_left_to_live) rn
4 from testv1
5 order by time_left_to_live;
LIST_ID TIME_LEFT_ RN
---------- ---------- ----------
1 01.05.2020 1
2 03.05.2020 2
3 01.06.2020 3
SQL>
但是,在触发器中,您修改一个列,该列触发一个触发器,该列修改一个列,该列触发一个触发器,该触发器修改一个列...直到您超过最大递归数 SQL 级别,然后 Oracle引发错误。
或者,如果您计划从 table select 然后用它做一些事情,您将点击 变异 table 错误,因为您不能从正在修改的 table 中 select。的确,您可能会使用复合触发器(或者 - 在以前的 Oracle 数据库版本中 - 包和自定义类型),但是 - 再一次,在我看来,这不是你应该处理问题的方式。
完全同意@Littlefoot。但是,即使您可以在触发器中构建用户写入排序,也没有隔离区在写入时保留顺序。根据定义,table 是一组无序的行,SQL 引擎没有义务维护所显示行的任何顺序。保证任意顺序的方式是order by clause