Java中绑定变量的SQL查询需要很长时间

时间:2016-11-10 17:11:35

标签: java oracle jdbc

我试图用一些参数执行查询。 如果我在pl / sql developer(oracle)中使用文字参数执行此sql它在1秒内运行。 如果我使用java,jdbc驱动程序和文字参数执行此sql它也运行得非常快。例如,使用此代码:

    String query = "select ID, (a lot of other columns)"
            + " from VCP_TIT_LIQDC"
            + " WHERE"
            + " ((123 is not null and id_tit = 123 and exists"
            + " (select 1 from sd_dual"
            + " where ((exists"
            + " (select 1"
            + " from fn_config_usr cfg"
            + " where cfg.nome_usr_bd = 'NAME' and cfg.ind_acesso_qualquer_site = 'S') or exists"
            + " (select 1"
            + " from fn_rel_usr_site rel"
            + " where rel.nome_usr_bd = 'NAME' and rel.cod_site = decode(vcp_tit_liqdc.id_empresa_lider, null, vcp_tit_liqdc.cod_site, vcp_tit_liqdc.cod_site_empresa_lider)))))) OR"
            + " (123 is null and (28 is null or"
            + " ((id_empresa = nvl(28 ,id_empresa) and id_empresa_lider is null) OR"
            + " id_empresa_lider = 28 )) AND decode (id_empresa_lider, null, cod_site, cod_site_empresa_lider) in"
            + " (select nvl (null , site.cod_site)"
            + " from fn_site site, fn_rel_usr_site rus, fn_config_usr cfg"
            + " where cfg.nome_usr_bd = 'NAME' and rus.nome_usr_bd (+) = cfg.nome_usr_bd and site.cod_site = decode (cfg.ind_acesso_qualquer_site, 'S', site.cod_site, rus.cod_site) group by site.cod_site))) AND (usr_cadastro = 'NAME' or ( id_tit in"
            + " (select ac.id_tit"
            + " from cp_titulo_acesso ac, cp_usr_acesso_janela jan"
            + " where ac.id_acesso = jan.id_acesso and 'WINDOW' = nvl(jan.nome_janela_sis, 'WINDOW' ) and jan.nome_usr_bd = 'NAME' ))) ORDER BY ID_TIT DESC";

    PreparedStatement p = connection.prepareStatement(query);
    ResultSet rs = p.executeQuery();

但如果我使用"?"设置参数? (绑定)运行需要10分钟或更长时间。例如在代码中:

    String query = "select ID, (a lot of other columns)"
            + " from VCP_TIT_LIQDC"
            + " WHERE"
            + " ((? is not null and id_tit = ? and exists"
            + " (select 1 from sd_dual"
            + " where ((exists"
            + " (select 1"
            + " from fn_config_usr cfg"
            + " where cfg.nome_usr_bd = ? and cfg.ind_acesso_qualquer_site = 'S') or exists"
            + " (select 1"
            + " from fn_rel_usr_site rel"
            + " where rel.nome_usr_bd = ? and rel.cod_site = decode(vcp_tit_liqdc.id_empresa_lider, null, vcp_tit_liqdc.cod_site, vcp_tit_liqdc.cod_site_empresa_lider)))))) OR"
            + " (? is null and (? is null or"
            + " ((id_empresa = nvl(? ,id_empresa) and id_empresa_lider is null) OR"
            + " id_empresa_lider = ? )) AND decode (id_empresa_lider, null, cod_site, cod_site_empresa_lider) in"
            + " (select nvl (? , site.cod_site)"
            + " from fn_site site, fn_rel_usr_site rus, fn_config_usr cfg"
            + " where cfg.nome_usr_bd = ? and rus.nome_usr_bd (+) = cfg.nome_usr_bd and site.cod_site = decode (cfg.ind_acesso_qualquer_site, 'S', site.cod_site, rus.cod_site) group by site.cod_site))) AND (usr_cadastro = ? or ( id_tit in"
            + " (select ac.id_tit"
            + " from cp_titulo_acesso ac, cp_usr_acesso_janela jan"
            + " where ac.id_acesso = jan.id_acesso and ? = nvl(jan.nome_janela_sis, ? ) and jan.nome_usr_bd = ? ))) ORDER BY ID_TIT DESC";

    PreparedStatement p = connection.prepareStatement(query);

    p.setInt(1, 123);
    p.setInt(2, 123);
    p.setString(3, "NAME");
    p.setString(4, "NAME");
    p.setInt(5, 123);
    p.setInt(6, 28);
    p.setInt(7, 28);
    p.setInt(8, 28);
    p.setString(9, null);
    p.setString(10, "NAME");
    p.setString(11, "NAME");
    p.setString(12, "WINDOW");
    p.setString(13, "WINDOW");
    p.setString(14, "NAME");

    ResultSet rs = p.executeQuery();

有什么方法可以解决这个问题吗?因为sql在每种情况下都是相同的。

我正在使用10g数据库并测试了这个版本的jdbc:10.2.0.1.0,11.2.0.2.0和12.1.0.2.0。

2 个答案:

答案 0 :(得分:1)

包含静态值的查询具有谓词,例如"123 is null and (28 is null"对于静态值,优化器知道这些条件永远不会为真,因此它可以简单地忽略它们。作为一个类比,如果你被告知去商店购买一罐条纹油漆,你可以立即看到没有任何意义,因为没有这样的东西。

当它们是实际变量时,它们可能是空的,因此需要做大量的额外工作。

最好在Java代码中测试其中一些null值并构造一个更简单的查询来执行。

答案 1 :(得分:0)

当您使用x = NVL(?, x)而不是? is null or x = ?时,Oracle中有一个有趣的优化,请参见Connor McDonald的这篇文章: https://connor-mcdonald.com/2018/02/13/nvl-vs-coalesce

在许多情况下,这种区分可能不是一个真正的问题,但是在您的情况下,查询非常复杂,因此优化器可能根本无法“正确”地优化此条件。

我建议您编写一个动态SQL查询,其中SQL字符串和绑定变量根据执行用例的不同而不同。使用SQL builders like jOOQ(免责声明:我为供应商工作)或other SQL builders可以很容易地做到这一点。

如果您更喜欢使用静态SQL,您仍然可以设计一些不依赖于绑定值null的独特SQL查询来解决此问题。