Могу ли я оптимизировать этот код? - PullRequest
0 голосов
/ 25 сентября 2010

Я пытаюсь извлечь данные из таблицы и преобразовать каждую строку в формат CSV, например

s12, james, 24, 1232, Salaried

Приведенный ниже код выполняет работу, нозанимает много времени, таблицы строк превышают 1 000 000.

Посоветуйте, пожалуйста, метод оптимизации:

 while(rset1.next()!=false) {
                         sr=sr+"\n";
                        for(int j=1;j<=rsMetaData.getColumnCount();j++)
                        {
                            if(j< 5)
                            {
                         sr=sr+rset1.getString(j).toString()+",";
                            }
                            else
                          sr=sr+rset1.getString(j).toString();
                        }

                       }

/ SR

Ответы [ 7 ]

15 голосов
/ 25 сентября 2010

Два подхода в порядке предпочтения:

Потоковый вывод

PrintWriter csvOut = ... // Construct a write from an outputstream, say to a file
while (rs.next())
    csvOut.println(...) // Write a single line

(обратите внимание, что вы должны убедиться, что ваш Writer / OutputStreamбуферизованные, хотя многие по умолчанию)

Использовать StringBuilder

StringBuilder sb = new StringBuilder();
while (rs.next())
    sb.append(...) // Write a single line

Идея в том, что добавление строк в цикле - плохая идея.Представьте, что у вас есть строка.В Java строки являются неизменяемыми.Это означает, что для добавления к строке необходимо скопировать всей строки и затем написать еще до конца.Поскольку вы добавляете вещи постепенно, у вас будет много копий строки, которые не очень полезны.

Если вы пишете в файл, наиболее эффективно просто писать напрямуюс потоком или писателем.В противном случае вы можете использовать StringBuilder, настроенный так, чтобы он был намного эффективнее для добавления множества маленьких строк.

5 голосов
/ 25 сентября 2010

Я не эксперт по Java, но я думаю, что всегда плохо использовать что-то вроде getColumnCount () в условной проверке. Это связано с тем, что после каждого цикла она запускает эту функцию, чтобы увидеть, каков счетчик столбцов, а не просто ссылается на статическое число. Вместо этого установите переменную, равную этому числу, и используйте ее для сравнения с j.

1 голос
/ 25 сентября 2010

Как говорят другие ответы, прекратите добавлять строку.В Java объекты String являются неизменяемыми, поэтому каждое добавление должно делать полную копию строки, превращая это в операцию O (n ^ 2).

Другое большое замедление - размер выборки.По умолчанию драйвер может извлекать по одной строке за раз.Даже если это занимает 1 мс, это ограничивает вас тысячами строк в секунду.Удаленная база данных, даже в той же сети, будет намного хуже.Попробуйте вызвать setFetchSize (1000) в операторе.Помните, что установка слишком большого размера выборки может вызвать ошибки нехватки памяти в некоторых драйверах базы данных.

1 голос
/ 25 сентября 2010

В качестве совершенно другой, но, несомненно, наиболее оптимальной альтернативы , используйте предоставляемые БД средства экспорта. Неясно, какую БД вы используете, но согласно вашей истории вопросов вы, похоже, много делаете с Oracle. В этом случае вы можете экспортировать таблицу в файл CSV, используя UTL_FILE.

Смотри также:

1 голос
/ 25 сентября 2010

Я не верю, что незначительные изменения кода будут иметь существенное значение.Однако я бы наверняка использовал StringBuffer.

Он будет читать миллион строк по проводам, предполагая, что его база данных находится на отдельной машине.Во-первых, если производительность неприемлема, я запускаю этот код на сервере базы данных и отключаю сеть от уравнения.Если это тот код, который запускается раз в неделю как пакетное задание, то это может быть нормально.

Теперь, что вы собираетесь делать с StringBuffer или String после полной загрузки из базы данных?Мы смотрим на строку длиной 50 Мбайт.


Это должно быть на 1 йоту быстрее, поскольку она удаляет ненужную (i <5) проверку. </p>

StringBuilder sr = new StringBuilder();
int columnCount =rsMetaData.getColumnCount();
while (rset1.next()) {
    for (int j = 1; j < columnCount; j++) {
        sr.append(rset1.getString(j)).append(",");
        }
    // I suspect the 'if (j<5)' really meant, "if we aren't on the last
    // column then tack on a comma." So we always tack it on above and
    // write the last column and a newline now.
    sr.append(rset1.getString(columnCount)).append("\n");
    }
}

Другой ответ - изменить выбор, чтобы он возвращал строку с запятой.Затем мы читаем результат с одним столбцом и добавляем его в StringBuffer.

Я забыл синтаксис сейчас, но что-то вроде:

select column1 || "," || column2 || "," ... from table;

Теперь нам не нужно ставить циклы и запятуюконкатенационное дело.

StringBuilder sr = new StringBuilder();
while (rset1.next()) {
    sr.append(rset1.getString(1)).append("\n");
    }
}
1 голос
/ 25 сентября 2010
StringBuilder sr = new StringBuilder();
int columnCount =rsMetaData.getColumnCount();
while (rset1.next()) {
    sr.append('\n');
    for (int j = 1; j <= columnCount; j++) {
        sr.append(rset1.getString(j));
        if (j < 5) {
        sr.append(',');
        }
    }
}
1 голос
/ 25 сентября 2010

Возможно, вы захотите использовать StringBuilder для построения строки, это намного эффективнее, когда вы делаете много конкатенации. Также, если у вас есть такой большой объем данных, вы можете рассмотреть возможность записи его непосредственно туда, куда вы собираетесь поместить его, вместо того, чтобы сначала создавать его в памяти, например, если это файл или сокет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...