#jdbc #prepared-statement
#jdbc #подготовленный оператор
Вопрос:
Я подключаюсь к базе данных Microsoft sql Server через java jdbc и сталкиваюсь с очень странной проблемой. Всякий раз, когда я использую параметры держателя места (?) в своем запросе в предложении where, а затем выполняю метод PreparedStatement.setString(..), выполнение моего простого запроса занимает от 4800 до 5800 миллисекунд. Когда я жестко кодирую предложение where внутри самого запроса, для выполнения требуется от 1 до 36 миллисекунд. Для меня это не имеет смысла, потому что я думал, что использование заполнителей должно быть быстрее…
В таблице действительно много строк (8 миллионов или около того), однако параметры, которые я передаю, составляют всего несколько символов, я передаю только 2 параметра, оператор всегда возвращает 1 (или 0) строк, а возвращаемые им данные невелики. Индексы в таблице не помогают. Почему такая ОГРОМНАЯ разница во времени? 5-6 секунд — это действительно долгое время для такого запроса. Оба столбца в предложении where имеют строковый тип (varchar(20)), поэтому на стороне базы данных нет неявного преобразования типов (о котором я знаю).
Я пробовал другую версию sqljdbc4.jar водитель, но он делает то же самое.
package testdbconnection;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class Main {
public static void main(String[] args) throws SQLException
{
Connection con = getConnection();
PreparedStatement statement = con.prepareStatement("select col1 from tableName where username = ? and password = ?");
statement.setString(1, "UName");
statement.setString(2, "PWord");
long start = System.currentTimeMillis();
ResultSet rs = statement.executeQuery();
long stop = System.currentTimeMillis();
System.out.println("took: " (stop - start));
rs.close();
con.close();
}// end main
private static Connection getConnection()
{
Connection connection = null;
try {
// Load the JDBC driver
String driverName = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
Class.forName(driverName);
// Create a connection to the database
String url = "jdbc:sqlserver://DBSERVERNAME;databaseName=DBNAME;";
String username = "dbUname";
String password = "dbPword";
connection = DriverManager.getConnection(url, username, password);
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
} catch (SQLException e)
{
e.printStackTrace();
}
return connection;
}// end getConnection()
}
Вывод:
выполнить:
заняло: 4891
СБОРКА ВЫПОЛНЕНА УСПЕШНО (общее время: 5 секунд)
Теперь, если я сделаю это:
package testdbconnection;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class Main {
public static void main(String[] args) throws SQLException
{
Connection con = getConnection();
PreparedStatement statement = con.prepareStatement("select col1 from tableName where username = 'UName' and password = 'PWord'");
long start = System.currentTimeMillis();
ResultSet rs = statement.executeQuery();
long stop = System.currentTimeMillis();
System.out.println("took: " (stop - start));
rs.close();
con.close();
}// end main
private static Connection getConnection()
{
Connection connection = null;
try {
// Load the JDBC driver
String driverName = "com.microsoft.sqlserver.jdbc.SQLServerDriver";
Class.forName(driverName);
// Create a connection to the database
String url = "jdbc:sqlserver://DBSERVERNAME;databaseName=DBNAME;";
String username = "dbUname";
String password = "dbPword";
connection = DriverManager.getConnection(url, username, password);
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
} catch (SQLException e)
{
e.printStackTrace();
}
return connection;
}// end getConnection()
}
Вывод:
выполнить:
заняло: 32
СБОРКА ВЫПОЛНЕНА УСПЕШНО (общее время: 2 секунды)
Ответ №1:
Вообще говоря, выполнение полностью жестко запрограммированного оператора будет быстрее, чем эквивалентного параметризованного оператора. Причина этого связана с планированием выполнения базы данных. Когда вы предоставляете всю информацию с самого начала, база данных может выполнять оптимизацию и выбирать более короткие пути, специфичные для точных предоставленных вами данных. Когда вы параметризуете оператор, он может выполнять только те оптимизации, которые были бы полезны для любого значения, которое может быть вставлено.
Параметризованные операторы могут быть полезны, когда вам нужно запускать много похожих запросов и вы хотите избежать накладных расходов на подготовку оператора каждый раз, но для одного запроса к большому набору данных (как в вашем случае использования) жестко запрограммированный запрос будет лучше.
Комментарии:
1. Что бы вы предложили в этом случае, чтобы сократить время и по-прежнему использовать параметризованный запрос? Я неохотно использую жестко запрограммированный запрос, потому что этот материал в предложении where поступает из пользовательского ввода и не использует параметры, что делает приложение более уязвимым для sql-инъекций. 5-6 секунд — это огромное время для такого небольшого запроса. Даже нет никаких объединений. Есть ли что-нибудь еще, что можно было бы сделать на стороне Java для повышения производительности?
2. Одним из возможных решений было бы использовать жестко запрограммированный запрос, если вы можете легко убедиться, что ввод безопасен (скажем, только буквенно-цифровые символы и пробелы), и сохранить параметризованный запрос для использования с более сомнительным вводом. Это имело бы смысл только в том случае, если ваш ожидаемый «обычный» ввод легко идентифицировать, конечно.