Solución:
Tuve un problema similar, con una solicitud muy simple (SELECT. FROM. WHERE =.) Que tarda hasta 10 segundos en devolver una sola fila cuando se usa una conexión jdbc en Java, mientras que solo toma 0.01s en sqlshell. El problema era el mismo si estaba usando el controlador oficial de MS SQL o el controlador JTDS.
La solución fue configurar esta propiedad en la url jdbc:
sendStringParametersAsUnicode = falso
Ejemplo completo si está utilizando el controlador oficial de MS SQL: jdbc: sqlserver: // yourserver; instanceName = yourInstance; databaseName = yourDBName; sendStringParametersAsUnicode = false;
Instrucciones si utiliza diferentes controladores jdbc e información más detallada sobre el problema aquí: http://emransharif.blogspot.fr/2011/07/performance-issues-with-jdbc-drivers.html
SQL Server diferencia los tipos de datos que admiten Unicode de los que solo admiten ASCII. Por ejemplo, los tipos de datos de caracteres que admiten Unicode son nchar, nvarchar, longnvarchar, mientras que sus contrapartes ASCII son char, varchar y longvarchar respectivamente. De forma predeterminada, todos los controladores JDBC de Microsoft envían las cadenas en formato Unicode a SQL Server, independientemente de si el tipo de datos de la columna correspondiente definida en SQL Server es compatible con Unicode o no. En el caso de que los tipos de datos de las columnas admitan Unicode, todo es fluido. Pero, en los casos en los que los tipos de datos de las columnas no son compatibles con Unicode, surgen problemas de rendimiento graves, especialmente durante la obtención de datos. SQL Server intenta convertir tipos de datos no Unicode en la tabla a tipos de datos Unicode antes de realizar la comparación. Además, si existe un índice en la columna no Unicode, se ignorará. En última instancia, esto conduciría a un escaneo de toda la tabla durante la búsqueda de datos, lo que ralentizaría drásticamente las consultas de búsqueda.
En mi caso, tenía más de 30 millones de registros en la tabla desde la que estaba buscando. La duración para completar la solicitud pasó de más de 10 segundos a aproximadamente 0.01 s después de aplicar la propiedad.
Espero que esto ayude a alguien !
Parece que esto puede no aplicarse a su situación particular, pero quería proporcionar otra posible explicación para alguien que busque este problema.
Acabo de tener un problema similar en el que una consulta ejecutada directamente en SQL Server tomó 1 minuto, mientras que la misma consulta tomó 5 minutos a través de un estado preparado en Java. Lo rastreé hasta el hecho de que se hizo como una declaración preparada.
Cuando ejecuta una consulta directamente en SQL Server, le está proporcionando una consulta no parametrizada, en la que conoce todos los criterios de búsqueda en el momento de la optimización. En mi caso, mis criterios de búsqueda incluían un rango de fechas, y el servidor SQL pudo verlo, decidir “ese rango de fechas es enorme, no usemos el índice de fechas” y luego eligió algo mucho mejor.
Cuando ejecuto la misma consulta a través de una declaración preparada en Java, en el momento en que SQL Server está optimizando la consulta, aún no le ha proporcionado ninguno de los valores de parámetro, por lo que tiene que adivinar qué índice usar. En el caso de mi rango de fechas, si se optimiza para un rango pequeño y le doy un rango grande, funcionará más lento de lo que podría. Del mismo modo, si se optimiza para un rango grande y le doy uno pequeño, nuevamente funcionará más lento de lo que podría.
Demostrar que este era de hecho el problema, como experimento intenté darle pistas sobre qué optimizar para usar la opción “OPTIMIZAR PARA” de SQL Server. Cuando le dije que usara un rango de fechas pequeño, mi consulta de Java (que en realidad tenía un rango de fechas amplio) en realidad tomó el doble de tiempo que antes (10 minutos, en lugar de 5 minutos antes, y en lugar de 1 minuto en SQL Server ). Cuando le dije mis fechas exactas para optimizar, el tiempo de ejecución fue idéntico entre la declaración preparada de Java.
Entonces, mi solución fue codificar las fechas exactas en la consulta. Esto funcionó para mí porque fue solo una declaración única. El PreparedStatement no estaba destinado a ser reutilizado, sino simplemente para parametrizar los valores para evitar la inyección de SQL. Dado que estas fechas provenían de un objeto java.sql.Date, no tuve que preocuparme por mis valores de fecha que contienen código de inyección.
Sin embargo, para una declaración de que SÍ necesita ser reutilizada, la codificación rígida de las fechas no funcionaría. Quizás una mejor opción para eso sería crear múltiples declaraciones preparadas optimizadas para diferentes rangos de fechas (una para un día, una para una semana, una para un mes, una para un año y una para una década … o tal vez usted solo necesito 2 o 3 opciones … no lo sé) y luego, para cada consulta, ejecute la declaración preparada cuyo rango de tiempo coincida mejor con el rango en la consulta real.
Por supuesto, esto solo funciona bien si sus rangos de fechas están distribuidos de manera uniforme. Si el 80% de sus registros se encontraban en el último año, y el 20% por ciento se distribuyó en los 10 años anteriores, entonces hacer las “consultas múltiples basadas en el tamaño del rango” podría no ser lo mejor. Tendría que optimizar sus consultas en función de rangos específicos o algo así. Tendría que averiguarlo mediante prueba y error.
Asegúrese de que su controlador JDBC esté configurado para usar una conexión directa y no una conexión basada en cusror. Puede publicar su URL de conexión JDBC si no está seguro.
Asegúrese de estar usando un conjunto de resultados de solo lectura y solo reenvío (este es el valor predeterminado si no lo está configurando).
Y asegúrese de estar utilizando controladores JDBC actualizados.
Si todo esto no funciona, entonces debería mirar el generador de perfiles sql e intentar capturar la consulta sql mientras el controlador jdbc ejecuta la declaración, y ejecutar esa declaración en el estudio de administración y ver si hay una diferencia.
Además, dado que está extrayendo tantos datos, debe intentar asegurarse de no tener ninguna ralentización de la recolección de memoria / basura en la JVM (aunque en este caso eso no explica realmente la discrepancia de tiempo).