J'ai 2 fichiers. Le premier fichier contient la liste des ID de ligne des tuples d'une table de la base de données . Et le second fichier contient des requêtes SQL avec ces ID de ligne dans la clause "Where" de la requête.
Par exemple:
Fichier 1
1610657303
1610658464
1610659169
1610668135
1610668350
1610670407
1610671066
Fichier 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;
Je dois lire le fichier 1 et rechercher dans le fichier 2 toutes les commandes SQL qui correspondent à l'ID de ligne du fichier 1 et vider ces requêtes SQL dans un troisième fichier.
Le fichier 1 contient 1,00,000 entrées et le fichier 2 contient 10 fois les entrées du fichier 1, soit 1,00,0000.
J'ai utilisé grep -f File_1 File_2 > File_3
. Mais cela est extrêmement lent et le taux est de 1000 entrées par heure.
Y at-il un moyen plus rapide de faire cela?
Une manière avec awk
:
awk -v FS="[ =]" 'NR==FNR{rows[$1]++;next}(substr($NF,1,length($NF)-1) in rows)' File1 File2
Cela devrait être assez rapide. Sur ma machine, il a fallu moins de 2 secondes pour créer une recherche de 1 million d'entrées et la comparer à 3 millions de lignes.
Spécifications de la machine:
Intel(R) Xeon(R) CPU E5-2670 0 @ 2.60GHz (8 cores)
98 GB RAM
Vous n'avez pas besoin d'expression régulière, donc grep -F -f file1 file2
Les solutions awk/grep mentionnées ci-dessus étaient lentes ou gourmandes en mémoire sur ma machine (fichier1 10 ^ 6 lignes, fichier2 10 ^ 7 lignes). Je suis donc venu avec une solution SQL utilisant sqlite3.
Transformez fichier2 en un fichier au format CSV où le premier champ est la valeur après ri=
cat file2.txt | gawk -F= '{ print $3","$0 }' | sed 's/;,/,/' > file2_with_ids.txt
Créez deux tables:
sqlite> CREATE TABLE file1(rowId char(10));
sqlite> CREATE TABLE file2(rowId char(10), statement varchar(200));
Importez les identifiants de ligne de fichier1:
sqlite> .import file1.txt file1
Importez les déclarations de file2 en utilisant la version "préparée":
sqlite> .separator ,
sqlite> .import file2_with_ids.txt file2
Sélectionnez toutes et seulement les instructions de la table file2
avec un ID de ligne correspondant dans la table file1
:
sqlite> SELECT statement FROM file2 WHERE file2.rowId IN (SELECT file1.rowId FROM file1);
Le fichier 3 peut être facilement créé en redirigeant la sortie vers un fichier avant d'émettre l'instruction select:
sqlite> .output file3.txt
Données de test:
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;
Sans créer d’index, l’instruction select a pris environ 15 secondes sur une machine AMD A8 1.8HGz 64bit Ubuntu 12.04.
Je suggère d'utiliser un langage de programmation tel que Perl, Ruby ou Python.
Dans Ruby, une solution lisant les deux fichiers (f1
et f2
) une seule fois pourrait être:
idxes = File.readlines('f1').map(&:chomp)
File.foreach('f2') do | line |
next unless line =~ /where ri=(\d+);$/
puts line if idxes.include? $1
end
ou avec 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;
Peut-être essayer AWK et utiliser le numéro du fichier 1 comme clé pour un script simple, par exemple
Le premier script produira le script awk:
awk -f script1.awk
{ print "\ $ 0 ~", $ 0, "{print\$ 0}"> script2.awk; }
puis invoquez script2.awk avec le fichier
Il se peut que je manque quelque chose, mais ne serait-il pas suffisant de simplement réitérer les ID dans file1
et pour chaque ID, grep file2
et de stocker les correspondances dans un troisième fichier? C'est à dire.
for ID in `cat file1`; do grep $ID file2; done > file3
Ce n’est pas très efficace (car le fichier 2 sera lu encore et encore), mais cela peut vous suffire. Si vous voulez plus de rapidité, je suggérerais d'utiliser un langage de script plus puissant qui vous permette de lire file2
sur une carte, ce qui permet d'identifier rapidement les lignes d'un identifiant donné.
Voici une version Python de cette idée:
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]