2013-06-21 4 views
18

У меня 2 файла. Первый файл содержит список идентификаторов строк кортежей таблицы в базе данных. И второй файл содержит SQL-запросы с этими идентификаторами строк в предложении «где» запроса.Найти содержимое одного файла из другого файла в UNIX

Например:

Файл 1

1610657303 
1610658464 
1610659169 
1610668135 
1610668350 
1610670407 
1610671066 

Файл 2

update TABLE_X set ATTRIBUTE_A=87 where ri=1610668350; 
update TABLE_X set ATTRIBUTE_A=87 where ri=1610672154; 
update TABLE_X set ATTRIBUTE_A=87 where ri=1610668135; 
update TABLE_X set ATTRIBUTE_A=87 where ri=1610672153; 

Я должен прочитать файл 1 и поиск в файле 2 для всех команд SQL, который соответствует строки идентификаторов из файла 1 и сбрасывать эти SQL-запросы в третьем файле.

Файл 1 имеет 1,00,000 записей, а файл 2 содержит 10 записей в файле 1, то есть 1,00,0000.

Я использовал grep -f File_1 File_2 > File_3. Но это очень медленно, и скорость составляет 1000 записей в час.

Есть ли более быстрый способ сделать это?

+0

ли все запросы, которые используют идентификаторы строк, упомянутые в файле 1, попадают в один и тот же выходной файл или вы хотите иметь отдельный файл для каждого идентификатора строки в файле 1? –

+0

Разрешено ли сортировать файлы по идентификаторам? Или порядок должен быть сохранен? – fizzer

+0

Только один выходной файл. Каждый раз, когда запись из файла 1 соответствует записи в файле 2, эта запись файла 2 должна быть сброшена в выходной файл. И записи в файлах 1 и 2 уникальны. Нет дубликатов строк в любом из файлов. – Tirthankar

ответ

13

Один из способов с awk:

awk -v FS="[ =]" 'NR==FNR{rows[$1]++;next}(substr($NF,1,length($NF)-1) in rows)' File1 File2 

Это должно быть довольно быстро.На моей машине потребовалось менее 2 секунд, чтобы создать поиск по 1 миллиону записей и сравнить его с 3 миллионами строк.

характеристики машины:

Intel(R) Xeon(R) CPU E5-2670 0 @ 2.60GHz (8 cores) 
98 GB RAM 
+1

+1. awk для победы. –

+1

Этот лайнер блестящий :) –

+1

Если вы укажете время, необходимое для запуска команды, может быть уместно включить спецификации машины. – doubleDown

0

Может попробовать AWK и использовать номер из файла 1 в качестве ключа для примера простого скрипта

Первого сценарий будет производить AWK сценария:
AWK -f script1.awk

 
{ 
    print "\$0 ~ ",$0,"{ print \$0 }" > script2.awk; 
} 

и затем вызвать Скрипт2 .awk с файлом

+0

Но это также приведет к нескольким чтениям файлов как в файле 1, так и в файле 2. Не считаете ли вы, что это также будет медленнее. – Tirthankar

+0

Да, но если вы читаете свой файл1 один раз и создаете одно большое регулярное выражение, а затем проверяете все строки в файле2 снова в этом регулярном выражении, он будет читать каждый файл только один раз. – Damian0o

1

Возможно, мне что-то не хватает, но не хватило бы просто повторить идентификаторы в file1 и для каждого ID, grep file2 и сохранить совпадения в третий файл? То есть

for ID in `cat file1`; do grep $ID file2; done > file3 

Это не очень эффективно (так как file2 будет читать снова и снова), но это может быть достаточно хорошо для вас. Если вы хотите увеличить скорость, я бы предложил использовать более мощный язык сценариев, который позволяет читать file2 на карте, которая позволяет быстро идентифицировать строки для данного идентификатора.

Вот версия Python этой идеи:

queryByID = {} 

for line in file('file2'): 
    lastEquals = line.rfind('=') 
    semicolon = line.find(';', lastEquals) 
    id = line[lastEquals + 1:semicolon] 
    queryByID[id] = line.rstrip() 

for line in file('file1'): 
    id = line.rstrip() 
    if id in queryByID: 
    print queryByID[id] 
+0

Я тоже пользовался perl, но это не помогло: my $ input_ri_file = "input_RI.csv"; my $ input_sql_file = "SQL_file.sql"; my $ output_sql_file = "Out_SQL_file.sql"; open (SQLFILE, "<$ input_sql_file"); foreach () { push @ lin, $ _ except ($ _ eq "\ n"); } закрыть $ SQLFILE; open (RILIST, "<$ input_ri_file"); foreach () { push @ ri_list, $ _ except ($ _ eq "\ n"); } закрыть $ RILIST; open (FINALSQLFILE, "> $ output_sql_file"); foreach $ ri (@ ri_list) { chomp $ ri; для (@ lin) { if ($ _ = ~/$ ri /) { print FINALSQLFILE $ _; } } } закрыть FINALSQLFILE; – Tirthankar

+0

@Tirthankar: Я просто добавил версию Python, это помогает? Сценарий ожидает два файла 'file1' и' file2'. Он печатает совпадения со стандартным выходом, поэтому вы можете перенаправить это в файл. –

+1

@Tirthankar вы можете использовать пастебин вместо демпинга кода в комментарий ... :-) –

22

Вам не нужно регэкспы, так grep -F -f file1 file2

+0

OP говорит, что он уже пробовал этот, но он был слишком медленным. –

+7

, но без атрибута -F – Damian0o

+0

Мне очень интересно, как это относится к [awk-решению JS 웃] (http://stackoverflow.com/a/17236091/1743811), но я слишком ленив, чтобы воспроизвести условие OP. – doubleDown

1

Я предлагаю использовать язык программирования, таких как Perl, Ruby или Python.

В Ruby, решение чтения обоих файлов (f1 и f2) только один раз может быть:

idxes = File.readlines('f1').map(&:chomp) 

File.foreach('f2') do | line | 
    next unless line =~ /where ri=(\d+);$/ 
    puts line if idxes.include? $1 
end 

или с Perl

open $file, '<', 'f1'; 
while (<$file>) { chomp; $idxs{$_} = 1; } 
close($file); 

open $file, '<', 'f2'; 
while (<$file>) { 
    next unless $_ =~ /where ri=(\d+);$/; 
    print $_ if $idxs{$1}; 
} 
close $file; 
1

В/GREP решения AWK, упомянутые выше, были медленными или памяти голодным на моей машине (file1 10^6 строк, file2 10^7 строк). Поэтому я придумал SQL-решение, используя sqlite3.

Turn file2 в CSV-файла в формате, где первое поле значение после ri=

cat file2.txt | gawk -F= '{ print $3","$0 }' | sed 's/;,/,/' > file2_with_ids.txt 

Создайте две таблицы:

sqlite> CREATE TABLE file1(rowId char(10)); 
sqlite> CREATE TABLE file2(rowId char(10), statement varchar(200)); 

Импорт идентификаторы строк из file1:

sqlite> .import file1.txt file1 

Импортировать инструкции из файла2, используя «подготовленную» версию:

sqlite> .separator , 
sqlite> .import file2_with_ids.txt file2 

Выбрать все и оны утверждения в таблице file2 с согласующим RowId в таблице file1:

sqlite> SELECT statement FROM file2 WHERE file2.rowId IN (SELECT file1.rowId FROM file1); 

Файл 3 могут быть легко созданы путем перенаправления вывода в файл перед выполнением оператора выбора:

sqlite> .output file3.txt 

Тестовые данные:

sqlite> select count(*) from file1; 
1000000 
sqlite> select count(*) from file2; 
10000000 
sqlite> select * from file1 limit 4; 
1610666927 
1610661782 
1610659837 
1610664855 
sqlite> select * from file2 limit 4; 
1610665680|update TABLE_X set ATTRIBUTE_A=87 where ri=1610665680; 
1610661907|update TABLE_X set ATTRIBUTE_A=87 where ri=1610661907; 
1610659801|update TABLE_X set ATTRIBUTE_A=87 where ri=1610659801; 
1610670610|update TABLE_X set ATTRIBUTE_A=87 where ri=1610670610; 

Без создания каких-либо индексов оператор select занял около 15 секунд на 64-разрядной машине AMD A8 1.8HGz с 64-битным Ubuntu 12.04.

-1

## сообщает любые строки, содержащиеся в файле < 1> отсутствует в файле < 2>

IFS=$(echo -en "\n\b") && for a in $(cat < file 1>); 
do ((\!$(grep -F -c -- "$a" < file 2>))) && echo $a; 
done && unset IFS 

или делать то, что спрашивающий хочет, снять отрицанием и перенаправлять

(IFS=$(echo -en "\n\b") && for a in $(cat < file 1>); 
do (($(grep -F -c -- "$a" < file 2>))) && echo $a; 
done && unset IFS) >> < file 3> 
+0

Вы можете увидеть сценарий оболочки, который я недавно сделал, что делает это в моем блоге: http://scriptsandoneliners.blogspot.com/2014/08/blog-post.html –