【问题标题】:Validate Oracle Column Names验证 Oracle 列名
【发布时间】:2018-01-04 07:11:56
【问题描述】:

在一种情况下,我们动态地为create 临时表创建sql。 table_name 没有问题,因为它是由我们决定的,但是列名是由不受我们控制的来源提供的。

通常我们会使用以下查询检查列名:

select  ..
where NOT REGEXP_LIKE (Column_Name_String,'^([a-zA-Z])[a-zA-Z0-9_]*$') 
OR Column_Name_String is NULL
OR Length(Column_Name_String) > 30

但是,是否有任何内置功能可以进行更广泛的检查。也欢迎对上述查询提出任何意见。

提前致谢。


基于以下答案的最终查询:

select  ..
where NOT REGEXP_LIKE (Column_Name_String,'^([a-zA-Z])[a-zA-Z0-9_]{0,29}$') 
OR Column_Name_String is NULL
OR Upper(Column_Name_String) in (select Upper(RESERVED_WORDS.Keyword) from V$RESERVED_WORDS RESERVED_WORDS)

对列名中的 $ 等字符特别不满意,因此也不会使用..

dbms_assert.simple_sql_name('VALID_NAME')

我可以使用正则表达式来决定我自己的字符集是否允许。

【问题讨论】:

  • @TimBiegeleisen 只是试图选择所有无效的列。下面的检查句柄就是它。如果no data found 则向前移动否则error
  • 为什么要动态创建表?这在 Oracle 世界中并不常见(但据我所知,它在 MS SQL Server 中)。所以,如果你的主要背景是 SQL Server,也许你想解释一下你到底在做什么以及为什么;有人可能会为您指出正确(即更好)的方向。
  • @Littlefoot 好吧,我无法改变这个过程,但让我分享一点背景知识,以便我对未来有一些更好的想法(使用最佳实践)。目前我们正在从外部源(文件)获取数据,程序/脚本的工作是将这些数据推送到 oracle 表。尽管存在第三方应提供数据的某些规则,但他们有时会违反这些规则。例如,列是数字数据类型,但如果提供的数据是字符串,那么我的 data-push(sqlldr) 将失败....
  • @Littlefoot 为了验证数据,我们决定使用第三方提供的列和数据创建动态表并对其进行验证。通过这种方式,我们可以立即验证整组数据并提供错误。如果我们只依赖oracle,那么错误可能会随着异常的抛出而一个一个地抛出,并且会使错误修复任务变得非常繁琐。
  • @APC 请在上方查看。欣赏一些提示和建议。

标签: oracle oracle11g


【解决方案1】:

此答案不一定提供性能或逻辑改进,但您实际上可以使用单个正则表达式验证列名:

SELECT ...
WHERE NOT
    REGEXP_LIKE (COALESCE(Column_Name_String, ''), '^([a-zA-Z])[a-zA-Z0-9_]{0,29}$')

之所以有效,是因为:

  • 它使用相同的模式来匹配列,即以字母开头,然后仅使用字母数字字符和下划线
  • NULL 列名被映射为空字符串,正则表达式失败
  • 我们使用长度量词{0,29} 直接在正则表达式中检查列长度

【讨论】:

    【解决方案2】:

    " 是否有任何内置函数可以进行更广泛的检查。"

    Oracle 具有DBMS_ASSERT.SIMPLE_SQL_NAME() 函数。如果符合 Oracle 命名规则,则返回传递的名称...

     select dbms_assert.simple_sql_name('VALID_NAME') from dual;
    

    ...如果名称无效,则发送ORA-44003

    如果名称被双引号括起来,则有效名称允许任何字符(糟糕,但创建“即时临时表”也是如此)。此外,该功能不会检查名称的长度,因此您仍然需要自己验证。

    Find out more in the docs.

    这里还有a SQL Fiddle


    “无法创建带有注释列的表,因为它的标识符无效”

    公平点。 DBMS_ASSERT 主要用于防止 SQL 注入。因此它验证一个值是否符合 Oracle 的命名规则,而不是该值是一个有效的 Oracle 名称。要捕获comment 之类的内容,您还需要检查V$RESERVED_WORDS 的值,可能是where reserved != 'Y'。由于这是一个 V$ 视图选择,默认情况下不授予它;如果您无权访问,则需要请友好的 DBA 提供帮助。

    “为了验证列名,我认为我应该检查整个列表”

    由你决定。区别在于某些关键字可以合法地用作标识符。例如,当他们引入对象关系的东西时,TYPE 只是在 Oracle 版本 8 中成为保留字。但是现有系统中有很多表和视图使用'TYPE' 作为列名(尤其是Oracle 数据字典)。如果 Oracle 将 TYPE 设置为适当的保留字,它将破坏所有这些系统。因此,不能用作标识符的保留字列表是所有 Oracle 关键字的子集。


    对一般任务的意见:

    “我们从外部来源(文件)获取数据,程序/脚本的工作是将这些数据推送到 oracle 表中。”

    这个任务有两个部分。

    首先,您应该与第三方就这些文件的标准格式达成一致。不需要发现文件的结构或内容。 (或者如果有这样的需要,因为文件是从第三方轮播中随机获取的,您可能不应该使用关系数据库,而是使用其他东西:Endeca?Python Pandas 库?)

    第二个是动态创建表。如果您有约定的文件结构,那么您应该根据您的情况使用SQL*Loaderexternal tables 加载到标准表中。如果你在 12c 上,SQL*Loader Express Mode 可能会感兴趣。

    【讨论】:

    • 我个人也讨厌双引号。这使得编写 SQL 变得非常困难。无论如何,这个函数并没有多大帮助,因为select dbms_assert.simple_sql_name('comment') from dual 仍然返回成功,但是创建一个带有comment 列的表是不可能的,因为它是一个无效的标识符。
    • 对于整个任务,我们已经与第三方商定了一个标准格式,但是第三方太多了,中间总会出现问题。最好先做好准备,然后再受苦。由于该应用程序是最近推出的,预计会发生错误,但随着时间的推移,事情将得到简化,并且不再需要这些检查。
    • 我们仍在使用 oracle 11g。我还想知道我无法找到的 sqlldr 中的 commit all or none option。即使流程被简化并且我们直接加载到标准表中,我们仍然需要考虑得到错误并处理它们。由于sqlldr中没有commit all records or nothing,我们不可能实现direct load into standard tables,因为它不符合业务需求。
    • 我同意并且我从这次经历中学到了同样的东西。已注明。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-13
    • 2018-07-26
    相关资源
    最近更新 更多