代码之家  ›  专栏  ›  技术社区  ›  Zombies

JavaPraveReDATA中的%Script

  •  1
  • Zombies  · 技术社区  · 16 年前
    PreparedStatement ps = con.createStatement("select * from table1 where last_name like ?");
    ps.setString(1, "'%"+lastName+"'");
    

    这和……一样有效吗?

    Statement s = con.createStatement("select * from table1 where last_name like %"+ lastName);
    

    或者PreparedStatement是否去掉了%符号?

    6 回复  |  直到 12 年前
        1
  •  3
  •   cagcowboy    16 年前

    %是一个通配符(至少在Oracle中),因此理论上两者应该工作相同(假设您添加缺少的单引号)

    但是,第一种方法被认为是更好的实践,因为它可能使数据库优化程序不能够重新解析语句。第一种方法还应该保护您不受SQL注入的影响,而第二种方法可能不受影响。

        2
  •  3
  •   Janco    16 年前

    第二个不起作用,因为您忘记了字符串周围的引号! 接下来,您需要进行转义,并注意SQL注入。

    假设SQL

    lastName = "and a quote' or a bracket()";
    Statement s = con.createStatement("select * from table1 where last_name like '%"+ lastName + "'");
    

    产生的SQL是:

    select * from table1 where last_name like '%and a quote' or a bracket()'
    

    将失败

    绑定变量使使用它总是更安全。

        3
  •  3
  •   Jay    16 年前

    简短的回答是:是的,假设你修正了报价,两个应该给出相同的结果。百分号不会从准备好的语句中“去掉”,这比任何其他字符都多。

    更长的答案:准备好的语句和一次性语句的问题可能很复杂。如果只执行一次,则准备好的语句将花费更长的时间,因为数据库引擎必须为准备好的语句执行所有设置,然后插入值,然后让它在缓存中浮动,直到引擎决定刷新它。此外,优化器通常无法有效地处理准备好的语句。准备好的语句的全部要点是,优化器解析查询并设计一次查询计划。假设您说“从客户中选择客户名称”,其中客户类型为?和客户“zip=?”。您在类型和zip上都有索引。使用一条单独的use语句(当然,使用实数而不是问号),许多数据库引擎中的查询优化器可以查看两个字段值分布的统计信息,并选择将给出较小记录集的索引,然后按顺序读取所有这些记录,并消除那些失败的记录。二次试验。对于准备好的语句,它必须在知道将提供哪些值之前选择索引,这样它可能会选择效率较低的索引。

    你永远不应该因为死亡而痛苦,永远不应该写那些只在未知值周围加引号并将其放入SQL语句的代码。要么使用准备好的语句,要么编写一个函数来正确地转义任何嵌入的引号。这样的函数编写起来很简单。我不明白为什么JDBC不包含一个,所以您必须自己编写它,并将它包含在每个应用程序中。(考虑到某些SQL方言除了应该转义的单引号之外还有其他字符,这一点尤其正确。)

    下面是Java中这样一个函数的例子:

    public static String q(String s)
    {
      if (s==null)
        return "null";
      if (s.indexOf('\'')<0)
        return "'"+s+"'";
      int sl=s.length();
      char[] c2=new char[sl*2+2];
      c2[0]='\''; 
      int p2=1;
      for (int p=0;p<sl;++p)
      {
        char c=s.charAt(p);
        if (c=='\'')
          c2[p2++]=c;
        c2[p2++]=c;
      }
      c2[p2++]='\'';
      return new String(c2,0,p2);
    }
    

    (注意:我刚刚从我从代码中提取的版本中编辑了这个函数,以消除一些与这里不相关的特殊情况——很抱歉,在执行此操作时引入了一些小错误。)

    我通常给它起一个很短的名字,比如“Q”,这样我就可以写:

    String sql="select customer_name from customer where customer_type="+q(custType)
      +" and customer_zip="+q(custZip);
    

    或者类似的简单快捷的东西。这违反了“赋予函数完整且有意义的名称”,但我认为在这里值得一提,我可以在一个语句中使用相同的函数十次。

    然后我超载它,以采取日期和数字和其他特殊类型,并妥善处理它们。

        4
  •  1
  •   tuinstoel    16 年前

    将准备好的语句与绑定变量一起使用要快得多,因为这意味着Oracle不必反复分析(编译)SQL语句。Oracle将所有执行的语句以及执行计划存储在一个共享哈希表中以供重用。然而,Oracle将只重用带有绑定变量的已准备语句的执行计划。当你这样做的时候:

    “从表1中选择*,其中姓氏如%”+姓氏

    甲骨文公司 重用执行计划。

    (Oracle会散列每个SQL语句,当您使用select时…其中last_name如%“+last name每个SQL语句都有不同的哈希值,因为变量last name几乎总是有不同的值,所以Oracle在哈希表中找不到SQL语句,Oracle无法重用执行计划。)

    在多并发情况下,影响更大,因为Oracle锁定了这个共享哈希表。这些锁不会持续很长时间,但在多并发情况下,锁定确实开始受到伤害。当使用带有绑定变量的准备好的语句时,几乎不需要锁定。顺便说一句,Oracle称这些自旋锁为闩锁。

    只有当您拥有一个数据仓库并且您的查询需要几分钟(报告)而不是几秒钟时,您才能使用未准备好的语句。

        5
  •  0
  •   northpole    16 年前

    我们经常使用第一种方法,没有问题。例如:

    String sql = "SELECT * FROM LETTER_BIN WHERE LTR_XML Like ' (?) ' AND LTR_BIN_BARCODE_ID = (?)";
    try
    {
        // Cast a prepared statement into an OralcePreparedStatement
        opstmt = (OraclePreparedStatement) conn.prepareStatement(sql);
        // Set the clob using a string
        opstmt.setString(1,fX.toString());
        // for this barcode
        opstmt.setLong(2,lbbi);
        // Execute the OraclePreparedStatement
        opstmt.execute();
    } catch(java.sql.SQLException e)
    {
        System.err.println(e.toString());
    } finally
    {
        if(opstmt != null)
        {
            try
            {
                opstmt.close();
            } catch(java.sql.SQLException ignore)
            {
                System.err.println("PREPARED STMT ERROR: "+ignore.toString());
            }
        }
    
    }
    
        6
  •  0
  •   Jay    16 年前

    好吧,我相信你在甲骨文上的话。毫不奇怪,这依赖于数据库引擎。Postgres的行为和我描述的一样。当从JDBC使用MySQL时——至少在几年前我最后一次研究这个问题时——准备好的语句和单用语句之间几乎没有什么区别,因为MySQLJDBC驱动程序在客户端保存准备好的语句,当您执行准备好的语句时,它以文本的形式填充值并发送给我。转到数据库引擎。就发动机而言,实际上没有准备好的声明。当我得知其他引擎的行为完全不同时,我一点也不惊讶。