【问题标题】:How to determine if a column name must be referred in quotes in a SQL statement?如何确定是否必须在 SQL 语句中用引号引用列名?
【发布时间】:2014-02-22 01:57:55
【问题描述】:

我正在编写一个 Web 服务,该服务本质上允许用户针对广告列向各种 SQL 数据库中的预先存在的表提交查询。

我有一个这样定义的 PostgreSQL 表:

CREATE TABLE stpg.test (
    test integer,
    "Test" integer,
    "TEST" integer
);
insert into stpg.test values (1,2,3);

为了确定可用列的名称,我运行以下 Java 代码:

ResultSet rs = dbmd.getColumns(null, "stpg", "test", null);
 while (rs.next()) {
     System.out.println(rs.getString("COLUMN_NAME"));
 }

我明白了:

测试 测试 测试

如果用户提交查询,在返回时引用列,例如 select test, Test, TEST from stpg.test 他会得到 1 1 1 而不是预期的 1 2 3。 这是一个错误吗?

我知道执行select test, "Test", "TEST" from stpg.testreturn 会正确返回结果。但是我的用户不知道要获取在引号中定义的“大写”列的值,他们需要在查询中使用引号。

有没有一种方法可以确定列名区分大小写,以便我可以用引号报告它的名称?我需要针对不同的数据库进行一般性的操作,因此 JDBC api 方法更可取。我尝试使用 ResultSetMetaData 并调用 getColumnNamegetColumnLabel 但它们返回的名称不带引号。调用 isCaseSensitive 总是返回 false。

【问题讨论】:

  • 为什么不总是引用列名?
  • 这是记录在案的行为:postgresql.org/docs/current/static/… 与其他人相反,我强烈建议您从不引用列名,除非您真正了解其中的含义。
  • 如果可以避免的话,我建议使用区分大小写的列名是个坏主意。

标签: java postgresql jdbc


【解决方案1】:

有没有一种方法可以确定列名区分大小写,以便我可以用引号报告它的名称?

您似乎在说如果列名包含任何大写字母,则需要引用它。在这种情况下:

    if (!name.equals(name.toLowercase())) {
        // needs quoting.
    }

但这没有实际意义:

  • 如果您只引用所有列名,或者
  • 如果您用户提供的列名视为不区分大小写。

(关于后一点,在区分大小写的情况下使用列名可能是一个糟糕的设计。区分大小写当然不是您希望网站用户不得不担心的事情...... .)


我尝试使用 ResultSetMetaData 并调用 getColumnName 和 getColumnLabel 但它们返回的名称不带引号。

他们应该这样做!引号不是列名的一部分!它们是标识符(通常)的(Postgres)SQL 语法的一部分。名称是引号内的内容。

调用 isCaseSensitive 总是返回 false。

说实话,这种方法的结果意味着什么并不完全清楚(来自javadoc)。但听起来您可能在您使用的 JDBC 驱动程序中发现了一个错误。 (或者您可能只是弄错了。在当前 Postgres 中实现该方法的代码确实参考了列类型信息...)

【讨论】:

  • 我不太懂 if (name.equals(name.toUppercase())) 测试。如果我没看错的话,只有当列名 完全 大写时,这才是正确的。但如果名称中的 any 字符为大写,则 PostgreSQL 需要引用。所以那个测试不起作用......
  • 这里的JDBC API也有不足; ConnectionDatabaseMetaData 应该公开 quoteIdentifier 方法,但事实并非如此。所以它比它应该的更难引用。
  • @CraigRinger - 是的,也许。但另一方面是模式中区分大小写的名称是一个坏主意,支持“基于坏主意”的编程不是一个好主意......
  • @StephenC 我同意第二部分 - 但这并不是那么简单。 JDBC 也不公开保留字/关键字列表,并且由于您必须引用关键字,IMO JDBC 应该更容易引用在数据库元数据中找到的任何标识符。它暴露了“标识符引用字符”,但它比无用更糟糕,尤其是在 MS SQL Server 等使用非标准 [ident] 完成标识符引用的事情上。
  • @CraigRinger - 不幸的是,在 JDBC 级别处理不同 SQL 方言之间的语法差异是一个失败的原因。仅仅修复这个(晦涩的)案例并没有帮助......在大局中。这就是为什么我们在 JDBC API 之上有(各种)数据库独立层。
【解决方案2】:

我建议总是引用列名。您没有真正的理由要删除引号。而且,更重要的是,决定是否引用的代码肯定会跨越 10-15 行,没有附加价值。这大约是 15 行代码,可能会引入新的错误、拼写错误、概念错误。

只引用每一列是直截了当的,而且总是正确的!

另外,关于select test, Test, TEST from stpg.test 的结果是否是错误的问题:不是。这是 PostgreSQL 的默认行为。所有列名(或 db-object 名称)总是降低,除非它们被引号括起来。这也将我们引向isCaseSensitive。它总是错误的,因为它不区分大小写。

更重要的一点:如果您让用户输入 SQL 查询,您可能会遇到其他奇怪的问题。您永远不会知道您的用户输入了什么样的恶作剧。无论是设计还是偶然;)

如果这是您第一次允许用户输入 SQL 查询,请仔细考虑您的行动计划!用户输入错误、错误(5 个数百万行的表上的全笛卡尔积?只有 然后 应用过滤器?...有趣的时间...),或者甚至可能尝试使用您的数据库.如果您决定真正这样做,请系好安全带! :) 这完全取决于您的用户群的技术知识。

另外,在 Postgres 中,我发现将所有内容保持小写和用户下划线分隔单词很有用。喜欢user_account 而不是UserAccount

【讨论】:

    猜你喜欢
    • 2018-02-26
    • 1970-01-01
    • 2015-12-31
    • 1970-01-01
    • 2012-01-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多