Иначе:
SELECT *
FROM TABLE A
WHERE EXISTS (
SELECT 1 FROM TABLE
WHERE COLUMN_NAME = A.COLUMN_NAME
AND ROWID < A.ROWID
)
Хорошо работает (достаточно быстрый), когда существует индекс на column_name
. И это - лучший способ удалить или обновить дублирующиеся строки.
Лучшее, что я смог сделать с sed, это сценарий:
s/[\s\t]*|[\s\t]*/|/g
s/[\s\t]*$//
s/^|/null|/
В моих тестах он работал примерно на 30% быстрее, чем ваш сценарий sed. Повышение производительности происходит за счет объединения первых двух регулярных выражений и исключения флага «g» там, где он не нужен.
Однако увеличение скорости на 30% - это лишь незначительное улучшение (все равно должно потребоваться около полутора часов, чтобы запустить выше скрипт в вашем файле данных размером 1 ГБ). Я хотел посмотреть, смогу ли я сделать что-нибудь лучше.
В конце концов, ни один другой метод, который я пробовал (awk, perl и другие подходы с sed), не показал себя лучше, за исключением, конечно, простого старого C реализация. Как и следовало ожидать от C, код является немного подробным для публикации здесь, но если вам нужна программа, которая скорее всего будет быстрее, чем любой другой метод, вы можете взглянуть на него .
В моих тестах реализация C завершается примерно за 20% времени, необходимого для ваш сценарий sed. Так что для запуска на вашем сервере Unix может потребоваться около 25 минут.
Я не тратил много времени на оптимизацию реализации C. Без сомнения, есть ряд мест, где алгоритм можно было бы улучшить, но, честно говоря, я не знаю, можно ли сэкономить значительное количество времени сверх того, что он уже дает. Во всяком случае, я думаю, что это определенно устанавливает верхний предел производительности, которую вы можете ожидать от других методов (sed, awk, perl, python и т. Д.).
Изменить: В исходной версии была небольшая ошибка, которая заставил его, возможно, напечатать неправильную вещь в конце вывода (например, мог напечатать "
Мое тестирование показало, что sed может довольно легко привязать к процессору что-то вроде этого . Если у вас многоядерный компьютер, вы можете попробовать запустить несколько процессов sed с помощью сценария, который выглядит примерно так:
#!/bin/sh
INFILE=data.txt
OUTFILE=fixed.txt
SEDSCRIPT=script.sed
SPLITLIMIT=`wc -l $INFILE | awk '{print $1 / 20}'`
split -d -l $SPLITLIMT $INFILE x_
for chunk in ls x_??
do
sed -f $SEDSCRIPT $chunk > $chunk.out &
done
wait
cat x_??.out >> output.txt
rm -f x_??
rm -f x_??.out
Из вашего примера мне кажется, что вы очищаете пустое пространство в начале и конце полей, разделенных вертикальной чертой (|), в текстовом файле. Если бы я сделал это, я бы изменил алгоритм на следующий:
for each line
split the line into an array of fields
remove the leading and trailing white space
join the fields back back together as a pipe delimited line handling the empty first field correctly.
Я бы также использовал для этого другой язык, такой как Perl или Ruby.
Преимущество этого подхода в том, что код, который очищает lines теперь обрабатывают меньше символов для каждого вызова и должны выполняться намного быстрее, даже если требуется больше вызовов.
Попробуйте изменить первые две строки на:
s/[ \t]*|[ \t]*/|/g
Этот сценарий Perl должен быть намного быстрее
s/\s*|\s*/|/go;
s/\s *$//o;
s/^|/null|/o;
В принципе, убедитесь, что ваши регулярные выражения скомпилированы один раз (флаг 'o'), и нет необходимости использовать 'g' в регулярных выражениях, которые применяется только к концу и началу строки.
Кроме того, [\ s \ t] * эквивалентно \ s *
Это может сработать. Я только немного его проверил.
awk 'BEGIN {FS="|"; OFS="|"} {for (i=1; i<=NF; i++) gsub("[ \t]", "", $i); $1=$1; if ( $1 == "" ) $1 = "null"; print}'
use gawk, not sed.
awk -vFS='|' '{for(i=1;i<=NF;i++) gsub(/ +|\t+/,"",$i)}1' OFS="|" file
How about Perl:
#!/usr/bin/perl
while(<>) {
s/\s*\|\s*/|/g;
s/^\s*//;
s/\s*$//;
s/^\|/null|/;
print;
}
EDIT: changed approach significantly. On my machine this is almost 3x faster than your sed script.
If you really need the best speed possible, write a specialized C program to do this task.
Попробуйте сделать это одной командой:
sed 's/[^|]*(|.*|).*/\1/'
Вы пробовали Perl? Это может быть быстрее.
#!/usr/local/bin/perl -p
s#[\t ]+\|#|#g;
s#\|[\t ]+#|#g;
s#[\t ]*$##;
s#^\|#null|#;
Редактировать: На самом деле, похоже, примерно в три раза медленнее, чем программа sed. Странно ...