在 PL/SQL 中使用变量和常量的困惑
Posted
技术标签:
【中文标题】在 PL/SQL 中使用变量和常量的困惑【英文标题】:Confusion on using variables & constants in PL/SQL 【发布时间】:2015-01-22 19:39:23 【问题描述】:我是 PL/SQL 的新手(自从我使用普通 SQL 以来已经有一段时间了)。我有一个我继承的查询,我试图在 TOAD 中安排。为了让它工作,我必须更改硬编码的日期引用以在运行时计算。
为此,我在查询前面添加了一个 Declare 语句,添加了必要的常量,在声明时设置它们,然后让查询使用它们。
当我尝试执行时,会抛出一个错误,说 Select Into
。据我了解,SELECT Into
用于根据数据库中的值设置变量(基于Constants in Oracle SQL query),而我希望定义独立于数据库中任何值的值(在本例中为日期在服务器上)。完整的错误如下:ORA-06550: line 6, column 5:
PLS-00428: an INTO clause is expected in the Select statement
因此,我正在寻找一些关于我对 PL/SQL 中的变量/常量的理解不正确的指导,并帮助执行以下操作:
DECLARE OLD CONSTANT char(11):= to_Char(SYSDATE - 6, 'DD-MON-YYYY');
NEW CONSTANT char(11):= to_char(SYSDATE, 'DD-MON-YYYY');
BEGIN
SELECT CASE
WHEN (userhost LIKE 'a%'
AND userid IN ('s',
'sub')) THEN 'BATCH'
WHEN userid LIKE 'N%' THEN 'N'
WHEN ((userhost LIKE 'b%'
OR userhost LIKE 'c%')
AND userid IN ('s',
'sub')) THEN 'Forms'
WHEN ((userid LIKE '%_IU%'
OR userid LIKE 'RPT%'
OR userid IN ('q',
'r',
'p'))
AND userhost <> 'n%') THEN 'Interface'
ELSE 'Other'
END app_type , round(sum(sessioncpu/100), 1) cpu_seconds , (sum(sessioncpu/100) / (119*1*60*60) * 100) pct_of_cpu,
trunc(ntimestamp#,'MI')
FROM PERFSTAT.AUD$_ARCHIVE
WHERE ntimestamp# BETWEEN to_timestamp(OLD || ' 23:59','DD-MON-YYYY HH24:MI') AND to_timestamp(NEW || ' 00:00','DD-MON-YYYY HH24:MI')
AND logoff$time < to_date(NEW || ' 00:00','DD-MON-YYYY HH24:MI')
GROUP BY CASE
WHEN (userhost LIKE 'a%'
AND userid IN ('s',
'sub')) THEN 'BATCH'
WHEN userid LIKE 'N%' THEN 'N'
WHEN ((userhost LIKE 'b%'
OR userhost LIKE 'c%')
AND userid IN ('s',
'sub')) THEN 'Forms'
WHEN ((userid LIKE '%_IU%'
OR userid LIKE 'RPT%'
OR userid IN ('q',
'r',
'p'))
AND userhost <> 'n%') THEN 'Interface'
ELSE 'Other'
END app_type,
trunc(ntimestamp#,'MI')
ORDER BY trunc(ntimestamp#,'MI'),
1;
END;
【问题讨论】:
你得到的完整错误是什么?我没有看到任何关于select into
的信息。
Using variables in PLSQL SELECT statement的可能重复
我不确定你为什么使用TO_CHAR()
,而你可以使用TRUNC()
来获取没有时间部分的日期。然后你根本不需要常量,你可以简单地执行以下操作:AND logoff$time < TRUNC(sysdate)
我将日期设置为字符串,以便将日期连接到固定时间(“00:00”或“23:59”)。也就是说,如果您可以在不强制转换为字符串的情况下连接日期,那肯定会起作用。我有点好奇如何让它按原样工作,所以我理解基本概念。
当您说“在 Toad 中调度”时,您是指使用 Toad 的 UI 到 Oracle 的调度程序还是“实用程序|任务调度程序”下的菜单项。另外,您期望预定作业的输出是什么?仅按计划运行查询没有多大意义:结果需要存储或报告到某个地方。
【参考方案1】:
这里有两个问题。第一个是尝试使用 CHAR 数据类型,然后不给它一个长度。这默认为 CHAR(1),即单个字符。对于内存问题,您也可以考虑使用 VARCHAR2。 https://docs.oracle.com/cd/E17952_01/refman-5.1-en/char.html
第二个问题与您的问题中提到的 INTO 子句有关。当您在 PL/SQL(与 DML 无关)中运行 SELECT 语句时,您必须为 Oracle 提供一些东西以将结果集返回到其中。然后,您可以使用这些变量,无论是打印它们、存储它们还是使用它们进行处理。
【讨论】:
我定义了字符串的长度。我仍然收到 Select Into 错误。使用完整的错误消息更新了原始问题。 你似乎错过了我回复的后半部分。您还缺少SELECT-INTO
的INTO
子句。您必须为返回的数据提供变量(或与返回匹配的单个集合)。【参考方案2】:
我必须看到错误,但我认为它可能希望你为你的 char 设置一个长度。所以,像 char(30) 这样的东西。 另外,我是 varchar2 的忠实粉丝。仅在数据库中使用与变量中的字符一样多的空间。所以,它是 varchar2(500) 并且有 8 个字符,它只使用 8 个字符的内存。
【讨论】:
【参考方案3】:您的查询有一个固有缺陷,即在 23:59 到 0:00 之间发生的任何事情都将满足范围两端的条件(例如,发生在 23:59:30 的事情)。如果这个查询是我的责任,我会完全摆脱变量和文本转换:
WHERE ntimestamp# >= TRUNC (SYSDATE) - 6
AND ntimestamp# < TRUNC (SYSDATE)
AND logoff$time < TRUNC (SYSDATE)
对于想要避免重叠的日期,使用>=
和<
往往比使用between
更安全。
仔细观察,我不确定您在下限午夜前一分钟使用查询的意义是什么。这种事情通常在上限上完成。假设您实际上是出于某种原因这样做,您仍然可以使用以下任一方法来绕过转换为字符串:
WHERE ntimestamp# BETWEEN TRUNC (SYSDATE) - 6 - (1 / 24 / 60)
AND TRUNC (SYSDATE)
AND logoff$time < TRUNC (SYSDATE)
WHERE ntimestamp# BETWEEN TRUNC (SYSDATE)
- NUMTODSINTERVAL (6, 'DAY')
- NUMTODSINTERVAL (1, 'MINUTE')
AND TRUNC (SYSDATE)
AND logoff$time < TRUNC (SYSDATE)
所有这些实际上只是您的主要问题的一个旁白:您需要告诉解释器如何处理查询结果。这意味着您需要提供一个变量来放入结果,然后(大概)对结果做一些事情。一种方法是使用游标循环:
DECLARE
CURSOR cur_query IS
[your query goes here];
BEGIN
FOR r_query IN cur_query LOOP
DBMS_OUTPUT.put_line (r_query.app_type);
DBMS_OUTPUT.put_line (r_query.cpu_seconds);
DBMS_OUTPUT.put_line (r_query.pct_of_cpu);
END LOOP;
END;
当然,替代方法是将查询作为 SQL 而不是 PL/SQL 运行。消除变量后,这将更容易。
评论回复
PL/SQL 块并非旨在返回查询结果,就像您在 Toad 中直接运行 SQL 时那样。有一些方法可以通过返回用户定义类型或管道函数的函数来伪造它,但如果可以的话,最好编写 SQL(在这种情况下,你应该可以)。
我不确定您所说的“变量应该动态设置要查看的日期范围”是什么意思。提供的代码返回与sysdate
相关的数据,而不是获取外部数据。您可以像在 PL/SQL 块中一样轻松地在查询中执行此操作。
【讨论】:
在你的第二个代码块之后,你有我。这些变量应该动态设置要查看的日期范围(在获得更多时间处理数据之后,一个更好的例子是寻找日期之间的值 Sysdate - x & Sysdate - x + 6 [6 只是一个实例编号] 这样我就可以通过更改 X 的值来继续获取一周的数据集)。查询本身只是应该返回它的结果(在这种情况下,我可以使用 Toad 的导出到 CSV 来对数据进行一些分析),这不需要指定。 既然如此,我不确定我是否误解了那段,或者它只是不适用于这种情况。以上是关于在 PL/SQL 中使用变量和常量的困惑的主要内容,如果未能解决你的问题,请参考以下文章
如何在选择语句的“NOT IN”子句中使用逗号分隔的字符串列表作为 pl/sql 存储的函数参数
在使用 Entity Framework 数据库优先在表上插入行之前,如何从 PL/SQL 执行触发器?
PL/SQL ORA-01422 SELECT INTO 错误,Oracle 匿名块(NOVA 环境)
Windows 64位 安装Oracle instantclient 官方绿色版和PL/SQL Developer 总结
PL/SQL developer连接oracle出现“ORA-12154:TNS:could not resolve the connect identifier specified”问题的解决(代码