如何在 PL/SQL 中使用 select 语句为 SQL*Plus 变量赋值?

Posted

技术标签:

【中文标题】如何在 PL/SQL 中使用 select 语句为 SQL*Plus 变量赋值?【英文标题】:How to assign a value to SQL*Plus variable with select statement in PL/SQL? 【发布时间】:2016-03-23 22:03:25 【问题描述】:

如何使用 select 语句为变量赋值并在 SQL*Plus 脚本中使用它?

VARIABLE FullCatCode VARCHAR2(7)

exec :FullCatCode := (SELECT CatCode from draw_catcodes where series = 123 and base = 158);

SELECT :FullCatCode || '-' || Other stuff... from table_name...

编辑:@AlexPoole,对于不精确的问题陈述感到抱歉。我正在从 280K 记录数据集中提取 50 列数据。我根据来自辅助表 (draw_catcodes) 的子字符串为每条记录分配序列 nextval。我在 SQL*Plus 中运行 SELECT 语句,假脱机到一个 CSV 文件。 SQL 现在看起来像这样(在下面测试您的第二个建议):

COLUMN CatCode NEW_VALUE FullCatCode;

SELECT
(CASE d.image WHEN 0 THEN 'NoImage.pdf'
 ELSE lower(d.prefix || lpad(d.series,3,0) || lpad(d.base,3,0) || lpad(d.suffix,2,0) || lpad(d.rev,2,0) || '.pdf') END) as "Filename",
(SELECT EngDiscipline from draw_catcodes c where d.series = c.series and d.base = c.base) as "EngDiscipline",

(SELECT CatCode from draw_catcodes c where d.series = c.series and d.base = c.base),

SELECT &FullCatCode || '-' ||
lpad((CASE substr(&FullCatCode,0,3)
WHEN 'AEG' THEN (SELECT AEG_seq.NEXTVAL FROM DUAL)
WHEN 'ARY' THEN (SELECT ARY_seq.NEXTVAL FROM DUAL)
WHEN 'BBR' THEN (SELECT BBR_seq.NEXTVAL FROM DUAL)
ELSE 0
END),6,0) as "ItemID",
upper(d.prefix || '-' || lpad(d.series,3,0) || '-' || lpad(d.base,3,0) || '-' || lpad(d.suffix,2,0)) as "LegacyID",
...
from tablename where ...

我从一个“更新插入”序列表的存储函数开始(无法从函数执行 DML),尝试了一个带有 OUT 变量的存储过程(无法从 SQL SELECT 中调用该过程),现在在 CASE 语句中尝试序列(无法弄清楚如何使用上述变量)...任何建议将不胜感激!

编辑:@AlexPoole,由于变量在这种情况下不起作用,我只是直接选择我想要的值,然后使用 CASE 语句来指定正确的顺序。语法不正确,因为我在下一行收到 ORA-00933: SQL command not properly end 错误:

SELECT ((SELECT CatCode from draw_catcodes c where d.series = c.series and d.base = c.base) || '-' ||
lpad((CASE substr((SELECT CatCode from draw_catcodes c where d.series = c.series and d.base = c.base),0,3)
WHEN 'AEG' THEN AEG_seq.NEXTVAL
WHEN 'ARY' THEN ARY_seq.NEXTVAL
WHEN 'BBR' THEN BBR_seq.NEXTVAL
...
WHEN 'SPR' THEN SPR_seq.NEXTVAL
WHEN 'SRL' THEN SRL_seq.NEXTVAL
ELSE 0
END),6,0))  as "ItemID" FROM DUAL,
upper(d.prefix || '-' || lpad(d.series,3,0) || '-' || lpad(d.base,3,0) || '-' || lpad(d.suffix,2,0)) as "LegacyID",

编辑:@AlexPoole,我添加了 JOIN 并清理了 SELECT,但现在得到 ORA-02287:此处不允许序列号

(c.CatCode || '-' || 
(SELECT lpad(
(CASE substr(c.CatCode,0,3)
WHEN 'AEG' THEN AEG_seq.NEXTVAL
WHEN 'ARY' THEN ARY_seq.NEXTVAL
WHEN 'BBR' THEN BBR_seq.NEXTVAL
WHEN 'BSY' THEN BSY_seq.NEXTVAL
...
WHEN 'SDR' THEN SDR_seq.NEXTVAL
WHEN 'SLC' THEN SLC_seq.NEXTVAL
WHEN 'SLD' THEN SLD_seq.NEXTVAL
WHEN 'SMS' THEN SMS_seq.NEXTVAL
WHEN 'SPP' THEN SPP_seq.NEXTVAL
WHEN 'SPR' THEN SPR_seq.NEXTVAL
WHEN 'SRL' THEN SRL_seq.NEXTVAL
ELSE 0 END ),6,0) FROM DUAL)) as "ItemID",
...
FROM md_draw d 
join draw_catcodes c on d.series = c.series and d.base = c.base
order by lpad(d.series,3,0), lpad(d.base,3,0), lpad(d.suffix,2,0);

有什么建议吗?

编辑:@AlexPoole,你说得对,我删除了包含序列调用的子查询,但仍然得到 ORA-02287: sequence number not allowed here 错误:

SELECT
(CASE d.image WHEN 0 THEN 'NoImage.pdf'
 ELSE lower(d.prefix || lpad(d.series,3,0) || lpad(d.base,3,0) || lpad(d.suffix,2,0) || lpad(d.rev,2,0) || '.pdf') END) as "Filename",
c.EngDiscipline as "EngDiscipline",
c.CatCode || '-' || lpad(CASE substr(c.CatCode,0,3)
WHEN 'AEG' THEN AEG_seq.NEXTVAL
WHEN 'ARY' THEN ARY_seq.NEXTVAL
WHEN 'BBR' THEN BBR_seq.NEXTVAL
...
WHEN 'SPP' THEN SPP_seq.NEXTVAL
WHEN 'SPR' THEN SPR_seq.NEXTVAL
WHEN 'SRL' THEN SRL_seq.NEXTVAL
ELSE 0 END,6,'0') as "ItemID",
upper(d.prefix || '-' || lpad(d.series,3,0) || '-' || lpad(d.base,3,0) || '-' || lpad(d.suffix,2,0)) as "LegacyID",
    ...
FROM md_draw SAMPLE (1) d 
join draw_catcodes c on d.series = c.series and d.base = c.base
order by c.ccProgram, lpad(d.series,3,0), lpad(d.base,3,0), lpad(d.suffix,2,0);

【问题讨论】:

您只有一个语句,并且您试图在稍后的同一语句中使用子查询中的 CatCode 的值? 您编辑问题时我没有收到通知,我只是碰巧回来了。为什么你还有一个子查询——那不在我的回答中?这就是导致新错误的原因。您是否看到有关使用分析函数而不是序列的编辑 - 如果您使用的比最初显示的更多,可能会更有用? @AlexPoole,删除子查询,仍然出现同样的错误... 还有所有额外的括号?我用自己的表运行了一个版本,删除了子查询没问题,但我也去掉了括号,我不能 100% 确定哪个清除了 ORA-02287。 【参考方案1】:

这相当容易。在 PL/SQL 中,查询要么需要是游标,要么必须选择 into 东西;在此,进入您的绑定变量:

VARIABLE FullCatCode VARCHAR2(7)

exec SELECT CatCode into :FullCatCode from draw_catcodes where series = 123 and base = 158;

SELECT :FullCatCode || '-' || Other stuff... from table_name...

如果您只打算在后面的 SQL 语句中使用它,您也可以使用替换变量,而不是绑定变量:

COLUMN CatCode NEW_VALUE FullCatCode

SELECT CatCode from draw_catcodes where series = 123 and base = 158;

SELECT &FullCatCode || '-' || Other stuff... from table_name...

对于您修改后的问题,这根本不是您想要做的。您试图在同一查询的另一部分引用子查询中的值,而不是稍后在脚本中的单独语句。并且不涉及 PL/SQL。

上述任何一种机制都无法做到这一点;第一个是因为没有 PL/SQL 块来执行 select ... into,第二个是因为在运行语句之前评估和替换了替换变量 - new_value 直到查询运行后才存在。

你不想要一个子查询,你应该使用一个连接,比如:

SELECT
  CASE d.image WHEN 0 THEN 'NoImage.pdf'
    ELSE lower(d.prefix || lpad(d.series,3,'0') || lpad(d.base,3,'0') || lpad(d.suffix,2,'0') || lpad(d.rev,2,'0') || '.pdf')
    END as "Filename",
  c.EngDiscipline as "EngDiscipline",
  c.CatCode,  -- not sure if you actually want this raw value?
  c.CatCode || '-' || lpad(CASE substr(c.CatCode,0,3)
    WHEN 'AEG' THEN AEG_seq.NEXTVAL
    WHEN 'ARY' THEN ARY_seq.NEXTVAL
    WHEN 'BBR' THEN BBR_seq.NEXTVAL
    ELSE 0
    END,6,'0') as "ItemID",
  upper(d.prefix || '-' || lpad(d.series,3,'0') || '-' || lpad(d.base,3,'0') || '-' || lpad(d.suffix,2,'0'))
    as "LegacyID",
  ...
from tablename d
join draw_catcodes c on d.series = c.series and d.base = c.base
where ...

您可以在尝试使用&FullCatCode的地方直接引用c.CatCode

如果序列只存在于此查询并且 ItemId 没有更广泛的意义,您可以使用分析函数来生成 ItemIds:

  c.CatCode || '-' ||
    lpad(row_number() over (partition by substr(c.CatCode,0,3) order by null),6,'0')
    as "ItemID",

ItemId 不会有确定性的顺序,但顺序方法不会;如果需要,您可以修改窗口子句以指定顺序。

【讨论】:

谢谢亚历克斯!更改 SELECT 后,出现以下错误:(EXEC SELECT CatCode into :FullCatCode from draw_catcodes c where d.series = c.series and d.base = c.base)* 第 20 行出现错误:ORA-00907:缺少右括号这个语句在一个更大的 Select 语句中,这个 'Into' 必须在最外面的语句中吗? @BarryPrentiss - 不太确定你的意思。 exec 是一个 SQL*Plus 命令,不是 SQL 语句的一部分,所以它不属于那里。 into 需要在最外面的语句中,是的。但目前尚不清楚该 sn-p/子查询在哪里适合更大的查询 - 它是选择列表表达式、内联视图还是连接条件?还是我没有想到的……? @BarryPrentiss - 根据我认为您正在尝试做的事情进行了更新。每次运行时,ItemID 是否应该从 1 开始,或者每次运行都应该从最后一次停止的地方开始?我试图弄清楚您是否真的需要这些序列,或者您是否只想要一个在查询中唯一的类似序列的后缀。 我根据唯一的序列/碱基对在选择时将 CatCodes 和顺序 ItemIDs 分配给记录,因此每个 CatCodes 都会有自己的序列号集。我会尝试加入,看看我是否可以让它工作......谢谢指针! 是的,但它们是否仅在此查询中相关?每行获取的序列值(即 ItemID)仅在文件中,而不是存储在您的数据库中。我想另一种看待它的方式是——你是否为这个查询创建了序列,如果你再次运行它,你会将序列重置为从 1 开始吗? (我想做的只是看看你是否能在没有序列的情况下得到你想要的结果)

以上是关于如何在 PL/SQL 中使用 select 语句为 SQL*Plus 变量赋值?的主要内容,如果未能解决你的问题,请参考以下文章

PL/SQL:, 如何将变量传递给 SELECT 语句并返回所有结果行

PL/SQL 触发器中的 SELECT 语句返回 null

在 ORACLE 的 select 语句中从 PL/SQL 调用函数

如何从 oracle pl sql 中的包主体内的 select 语句中捕获特定列

SQL 查询的 SELECT 子句中 Oracle PL/SQL 语句的延迟评估

如何在 SQL SELECT 语句中使用包常量?