Insertion automatisée dans une base SQL [BASH]

Salut tout le monde,

Je viens auprès de vous prendre un peu d’aide sur un script qui me fait buter
J’ai fait un script qui me permet à partir de fichier CSV de remplir ma base de donnée MYSQL, car j’ai clairement pas envie de faire ça à la manouche.


Je vous mets ci-dessous le code PS: Il n'est pas encore factorisé
#!/usr/bin/env bash
## Permet de stopper en cas d'erreur
set -e 
## DEBUG 
set -x
## Declaration des variables
tmpfile=$(mktemp)
source_file="/awk_bdd/importbdd"
MYSQL_USER="geoffrey"
MYSQL_BDD="BDDMYSQL"
MYSQL_TABLE="$1"
COLUMN1="$2"
COLUMN2="$3"
COLUMN3="$4"
COLUMN4="$5"
EMPTY_TABLE="DELETE from $MYSQL_TABLE;"
### Declare fonctions
function awk_func(){

awk -F',' 'BEGIN{OFS=";"} NR > 1{gsub("\"","");print $1, $2, $4}' $source_file > $tmpfile 
}
awk_func


ARG=$#
ARG=$((ARG - 1))
ARGS=("$@")
if (( $ARG <= 0 ));then
   echo "USAGE : $0 Nom_du tableau Nom_colonne1 Nom_colonne2 ...."
   exit
fi
## Declare les arrays 
## Ingredients nom
declare -a array
   while read -r line
      do array+=("$line")
   done < <(awk -F';' '{print $1}' $tmpfile)
## Ingredients marque
declare -a array2
   while read -r line
      do array2+=("$line")
   done < <(awk -F';' '{print $3}' $tmpfile)
##Ingredients categorie
declare -a array3
   while read -r line
      do array3+=("$line")
   done < <(awk -F';' '{print $2}' $tmpfile)
## Quatrieme colonne pour test
declare -a array4
   while read -r line
      do array4+=("$line")
   done < <(awk -F';' '{print $4}' $tmpfile)

function query_insert(){
if (($ARG == 1));then
query="INSERT INTO $MYSQL_TABLE ($COLUMN1) VALUES ('${array[$insert]}');"
   export query
elif (($ARG == 2));then
query="INSERT INTO '$MYSQL_TABLE' ('$COLUMN1','$COLUMN2') VALUES ('${array[$insert]}','${array2[$insert]}');"
   export query
elif (($ARG == 3));then
query='INSERT INTO '$MYSQL_TABLE' ('$COLUMN1','$COLUMN2','$COLUMN3') VALUES ('${array[$insert]}','${array2[$insert]}','${array3[$insert]}');'
   export query
elif (($ARG == 4));then
query="INSERT INTO '$MYSQL_TABLE' ('$COLUMN1','$COLUMN2','$COLUMN3','$COLUMN4') VALUES ('${array[$insert]}','${array2[$insert]}','${array3[$insert]}','${array4[$insert]}');"
   export query
fi
}
query_insert

function looping(){
sudo mysql -u $MYSQL_USER -D $MYSQL_BDD -e "$EMPTY_TABLE" && echo "Purge ... success"
for ((insert=0;insert<${#array[@]};insert++))
     do
      sudo mysql -u $MYSQL_USER -D $MYSQL_BDD -e "$query"
done
}

looping
   

Le souci est que qu’il me dit à chaque fois que l’entrée existe déjà à la première insertion
hors qu’au début de la boucle je vide bien la table pour être sur
Je comprend pas pourquoi il y a déjà une entrée et de ce fait il bloque l’éxécution

ci-dessous le debug de ce qui merde

+ query='INSERT INTO ingredients (ingredients_nom) VALUES ('\''Batônnet d’amande'\'');'
+ export query
+ looping
+ sudo mysql -u geoffrey -D trouvetarecette -e 'DELETE from ingredients;'
Password:
+ echo 'Purge ... success'
Purge ... success
+ (( insert=0 ))
+ (( insert<183 ))
+ sudo mysql -u geoffrey -D trouvetarecette -e 'INSERT INTO ingredients (ingredients_nom) VALUES ('\''Batônnet d’amande'\'');'
+ (( insert++ ))
+ (( insert<183 ))
+ sudo mysql -u geoffrey -D trouvetarecette -e 'INSERT INTO ingredients (ingredients_nom) VALUES ('\''Batônnet d’amande'\'');'
ERROR 1062 (23000) at line 1: Duplicate entry 'Batônnet d’amande' for key 'ingredients_nom'

Merci

Bonjour,

Déjà, l’erreur provient du fait que tu importes 2 fois de suite 'Batônnet d’amande' et que tu as une clé primaire ou un index unique sur le champ ingredients_nom non ?

Je ne réponds pas directement à ta question mais pourquoi réinventer l’eau chaude alors qu’il y a plus simple et plus efficace ?

Je n’utilise pas souvent MySQL, mais j’ai par exemple déjà fait cette manip directement dans son shell :
mysql> load data local infile 'monfichier.csv' into table matable
-> fields terminated by ','
-> enclosed by '"'
-> lines terminated by '\n'
-> (champ1, champ2)
-> ;
Ça serait simple à adapter à ton cas non ?

Il y a aussi ogr2ogr qui est vraiment performant, et qui pourrait être utilisé ainsi :
ogr2ogr -f MySQL MYSQL:"trouvetarecette, user=*, password=*, host=*, port=*, tables=ingredients" -lco OVERWRITE=YES monfichier.csv

À moins qu’il s’agisse d’une volonté de ta part de travailler exclusivement avec un script Bash, ce qui est tout à ton honneur, mais là je serais moins compétent.

Et ensuite tu construis une nouvelle table ou une vue avec tes opérations sur les champs dans MySQL avec une requête SQL qui sera certainement plus simple ?

1 J'aime

Ceci dit, on peut exactement faire la même chose en mettant ta commande load data dans une commande CLI.

si la seule raison est de ne pas le faire à la manouche, passe par mysql et un import ( ou un load comme proposé par isam ) car il y aura des cas particuliers où ta fonction sera inutilisable ( pas assez de colonnes,

en tout cas,
j’ia appliqué au fichier essai.csv :

"dateuh","visites"
"1","Batônnet d’amande"
"20200201","5"
"20200227","11"
"20200228","21"
"20200229","21"

ton programme et ça ne boucle pas :
5 fois la ligne

mysql -u geoffrey -D BDDMYSQL -e 'INSERT INTO essai (visites) VALUES ('\''1'\'');'

avec
./a.sh essai visites

Salut les gars
Bon tout d’abord merci pour vos réponses, en effet le coup du load data , je ne connaissais pas du tout.
Donc oui du coup ca me parait plus approprié.


Seulement, je suis de nature très curieuse, et je cherche à comprendre pourquoi est-ce qu'il double car je ne vois pas la raison Je dois buger qqpart, car j'ai réussi à le faire fonctionner, mais la j'arrive plus
Je vais partir sur les autres propositions faites ,mais seulement comme je n'aime pas partir sur une défaite et me sentir vaincu.

@dindoun
Je ne comptais bien sur pas laisser ça comme cela, ça doit encore être factoriser et un peu plus flexible, mais je fais d’abord qqch de fonctionnel et puis l’améliorer, c’est comme cela que je procède.
Il faut que tu modifies plein de chose si tu veux faire un test chez toi, car il va chez moi mais n’est pas portable du tout car tout est en dur !

@lsam
En effet y a bien des contraintes mise sur la table
Et pour répondre à ta question, je vois le souci mais je ne comprend pas pourquoi
Mais ta solution reste à étudier car elle me parait simple et facile à mettre en place.
Donc un grand merci à toi

Quelle est le schéma de ta base de données? Ca nous donnerait tes contraintes.

Re bonjour
pour @Zargos
Je te file ici mon fichier de creation SQL, la structure est bonne ce qu’il y a dedans n’est que du test

-- Création de la base de donnée (trouvetarecette)

CREATE DATABASE IF NOT EXISTS `trouvetarecette`;
USE `trouvetarecette`;

DROP TABLE IF EXISTS `bind_ir`;
DROP TABLE IF EXISTS `bind_rf`;
DROP TABLE IF EXISTS `recettes`;
DROP TABLE IF EXISTS `categorie`;
DROP TABLE IF EXISTS `ingredients`;
DROP TABLE IF EXISTS `fiches`;

-- Création de la table Catégorie

CREATE TABLE IF NOT EXISTS `categorie` (
    categorie_id SMALLINT(6) PRIMARY KEY AUTO_INCREMENT NOT NULL,
    categorie_nom ENUM('Crèmes de base','Pâtes de base','Glaçages','Biscuits','Entremets','Gâteaux','Cakes','Mousses','Glaces et Sorbets','Compotés','Inserts','Tartes','Pâte à tartiner','Meringues','Mignardises','Macarons','Décorations')
)
ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- Creation ingredients

CREATE TABLE IF NOT EXISTS `ingredients` (
    ingredients_id SMALLINT(6) PRIMARY KEY AUTO_INCREMENT NOT NULL,
    ingredients_nom VARCHAR(50) NOT NULL UNIQUE,
    ingredients_marque VARCHAR(50),
    ingredients_categorie VARCHAR(50)
) 
ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;


-- Création de la table principale recettes

CREATE TABLE IF NOT EXISTS `recettes` (
    recettes_id SMALLINT(6) PRIMARY KEY AUTO_INCREMENT NOT NULL,
    recettes_nom VARCHAR(100) NOT NULL UNIQUE,
    recettes_categorie SMALLINT(6) NOT NULL,
    recettes_frequence VARCHAR(50),
    recettes_date DATE,
    recettes_approuve BOOLEAN,
    recettes_etapes TEXT NOT NULL,
    FOREIGN KEY (recettes_categorie) REFERENCES categorie(categorie_id)
)
ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- Création de la table Fiches (pour les fiches technique complètes)

CREATE TABLE IF NOT EXISTS `fiches` (
    fiches_id SMALLINT(6) PRIMARY KEY AUTO_INCREMENT NOT NULL,
    fiches_nom VARCHAR(100) NOT NULL
)
ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- Creation table liaison
CREATE TABLE IF NOT EXISTS `bind_ir` (
    bind_recettes_id SMALLINT(6) NOT NULL,
    bind_ingredients_id SMALLINT(6) NOT NULL,
    bind_quantite SMALLINT(6) NOT NULL,
    bind_unite VARCHAR(4) DEFAULT 'Gr',
    FOREIGN KEY (bind_ingredients_id) REFERENCES ingredients (ingredients_id) ON DELETE CASCADE,
    FOREIGN KEY (bind_recettes_id) REFERENCES recettes (recettes_id) ON DELETE CASCADE,
    PRIMARY KEY (bind_recettes_id,bind_ingredients_id),
    UNIQUE (bind_recettes_id,bind_ingredients_id)
)
ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS `bind_rf` (
    bind_rf_fiches SMALLINT(6) NOT NULL,
    bind_rf_recettes SMALLINT(6) NOT NULL,
    bind_rf_saisons ENUM('Printemps','ETE','Automne','Hiver') NOT NULL,
    bind_rf_approuve BOOLEAN,
    bind_rf_process TEXT,
    FOREIGN KEY (bind_rf_recettes) REFERENCES recettes (recettes_id) ON DELETE CASCADE,
    FOREIGN KEY (bind_rf_fiches) REFERENCES fiches (fiches_id) ON DELETE CASCADE,
    PRIMARY KEY (bind_rf_fiches,bind_rf_recettes),
    UNIQUE (bind_rf_fiches,bind_rf_recettes)
);

-- Ajout de contenu à la tables ingredients
-- Ajout par script bash dans Privscripts

-- Ajout des catégories

INSERT INTO categorie (categorie_nom)
VALUES
('Gâteaux'),
('Cakes'),
('Crèmes de base'),
('Pâtes de base'),
('Tartes'),
('Compotés'),
('Glaces et Sorbets'),
('Entremets');

-- -- Ajout de recettes 

-- INSERT INTO recettes (recettes_nom,recettes_categorie,recettes_frequence,recettes_date,recettes_approuve,recettes_etapes)
-- VALUES
-- ('Javanais',1,'Souvent','2022-04-28',1,'Il faut faire'),
-- ('Merveilleux',2,'Jamais','2020-12-28',0,'La meringue '),
-- ('Cake chocolat',2,'Très souvent','2020-10-01',1,'Il ne faut pas');

-- INSERT INTO `bind_ir` (bind_recettes_id,bind_ingredients_id,bind_quantite,bind_unite)
-- VALUES
--     (1,1,287,DEFAULT),
--     (1,4,654,DEFAULT),
--     (1,3,465,DEFAULT),
--     (2,3,400,DEFAULT),
--     (3,4,500,DEFAULT),
--     (1,2,187,DEFAULT);

-- INSERT INTO `fiches` (`fiches_id`, `fiches_nom`)
-- VALUES
-- (1, 'Super dessert de la mort qui tue'),
-- (2, 'paris brest presque parfait'),
-- (3, 'montellimar nougat');

-- INSERT INTO `bind_rf`(bind_rf_fiches,bind_rf_recettes,bind_rf_saisons,bind_rf_approuve,bind_rf_process)
-- VALUES
--     (1,2,'ETE',1,'Trop fort ton truc'),
--     (3,2,'HIVER',0,'Pas encore fait comme dessert');

Pour le probleme du script j’ai trouvé d’ou vient l’erreur mais je n’ai pas encore trouver comment la résoudre
En fait le soucis je vous remet le code qui pose probleme

sudo mysql -u $MYSQL_USER -D $MYSQL_BDD -e "$EMPTY_TABLE" && echo "Purge ... success"
   
# if (($ARG == 1));then
# query="INSERT INTO $MYSQL_TABLE ($COLUMN1) VALUES ('${array[$insert]^}');"
# elif (($ARG == 2));then
# query="INSERT INTO $MYSQL_TABLE ($COLUMN1,$COLUMN2) VALUES ('${array[$insert]^}','${array2[$insert]^}');"
#    export query
# elif (($ARG == 3));then
# query="INSERT INTO $MYSQL_TABLE ($COLUMN1,$COLUMN2,$COLUMN3) VALUES ('${array[$insert]^}','${array2[$insert]^}','${array3[$insert]^}');"
   
# elif (($ARG == 4));then
# query="INSERT INTO $MYSQL_TABLE '($COLUMN1,$COLUMN2,$COLUMN3,$COLUMN4)' VALUES '('${array[$insert]^}','${array2[$insert]^}','${array3[$insert]^}','${array4[$insert]^}');'"
   
# fi

En fait au moment de l’affectation à la variable query , le tableau est évalué ce qui fait qu’ensuite
seul le premier index est évalué , et vu qu’il ne sait plus incrémenter ça bloque.
J’ai essayé un peu toute syntaxe mais je n’y arrive pas encore

les quote sdans ta query, soit elles sont inutile, soit elles sont au mauvais endroit.

Je reviens avec ma propre correction.

#!/usr/bin/env bash
## Permet de stopper en cas d'erreur
set -e 
## DEBUG 
#set -x
. colors_new.sh

## Declaration des variables
tmpfile=$(mktemp)
source_file=""
MYSQL_USER=""
MYSQL_BDD=""
MYSQL_TABLE="$1"
ARG1="$2"
ARG2="$3"
ARG3="$4"
ARG4="$5"

read -r -p "Entrer le nom de la colonne de l'id (Utile uniquement si vous avez plusieurs colonnes à remplir) : " id

function test_table(){
   result="$(sudo mysql -u $MYSQL_USER $MYSQL_BDD -e "SELECT COUNT(*) from ingredients;" |grep -E '[0-9]+')"
   if [[ $result -eq 0 ]];then
       echo ""
       echo "La table semble vide on peut continuer"
       read -p "Appuyer sur Entrée pour confirmer l'insertion CTRL+C dans le cas contraire"
       else
       echo ""
         echo "Risque d'écrasement de donnée veillez vider vos table ou faire une sauvegarde"
         echo "Il y a ${redtext}$result${to_normal} enregistrement déjà présent dans la table"
         exit   
   fi
}
function tozero_table(){

      sudo mysql -u $MYSQL_USER $MYSQL_BDD -e "TRUNCATE TABLE $MYSQL_TABLE;"

}

### Declare fonctions convert CSV with good separator

function awk_func(){
awk -F',' 'BEGIN{OFS=";"} NR > 1{gsub("\"","");print $0}' $source_file > $tmpfile 
}

function read_file(){
   
   count=1
   field=$2
   if [[ -s $tmpfile ]];then
      if (( $ARG == 1));then
      while read -r line
      do
         echo "${redtext}$line ${to_normal} àjouté à la table"
         sudo mysql -u $MYSQL_USER $MYSQL_BDD -e "INSERT INTO $MYSQL_TABLE ($1) VALUES ('$line');"
      done < <(awk -F';' -v var=$field '{print $var}' $tmpfile)
      elif (($ARG > 1));then
         while read -r line2
            do  
               sudo mysql -u $MYSQL_USER $MYSQL_BDD -e "UPDATE $MYSQL_TABLE SET $1 = '$line2' WHERE $id = $count;"
               ((count++))
            done < <(awk -F';' -v var=$field '{print $var}' $tmpfile)
      fi
   fi
}

ARG=$#
ARG=$((ARG-1))
if (( $ARG <= 0 || $ARG > 8 ));then
   echo ""
   echo "USAGE : $0 Tables_MYSQL Column_name ... ... ...  Un maximum de (8 colonnes)"
   echo ""
   echo "Attention l'ordre des tables à de l'importance, Assurer vous que le fichier dans lequel les données sont,
Vous faites correspondre la COL1 avec la VAL1 Exemple : 
COL1 = Ingredients , VAL1 = la colonne des ingrédients dans le fichier CSV !"
   exit 0
   else
      awk_func
      NBR_OCCUR=$(awk 'END {print NR}' "$tmpfile")
      case $ARG in
         
         1) test_table
            echo "${text_orange}Le nombre d'elements qui seront ajoutés est de ${to_normal} => $NBR_OCCUR"
            read -p "Confirmer par Entrée"
            read_file "$ARG1" 1
            ;;
         2) read_file "$ARG2" 2 
            ;;
         3) read_file "$ARG3" 3 
            ;;
         4) read_file "$ARG4" 4 
            ;;
         5)read_file "$ARG5" 5 
            ;;
         *)
            echo "Un probleme est survenu, veuillez relancé le script et vérifier si les arguments sont bons"
            ;;
   esac

fi


   



bravo
peux-tu me dire ce qu’est cette commande :

done < < ...

je ne connais pas le < <

Bonjour

Dans le script de linuxpit
ça permet d’envoyer le flux de sortie du sous-shell dans lequel s’exécute la commande awk
vers l’entrée de la commande while


while read -r line
do
    echo "${redtext}$line ${to_normal} àjouté à la table"
    sudo mysql -u $MYSQL_USER $MYSQL_BDD -e "INSERT INTO $MYSQL_TABLE ($1) VALUES ('$line');"
done < <(awk -F';' -v var=$field '{print $var}' $tmpfile)

est l’équivalent de :

awk -F';' -v var=$field '{print $var}' $tmpfile | while read -r line
do
    echo "${redtext}$line ${to_normal} àjouté à la table"
    sudo mysql -u $MYSQL_USER $MYSQL_BDD -e "INSERT INTO $MYSQL_TABLE ($1) VALUES ('$line');"
done
1 J'aime

Salut Micp,
Personnelement, j’ai plus de mal avec la deuxième syntaxe.
Mais c’est ça qui est super y a toujours plusieurs moyen d’arriver à ce que l’on veut, selon nos préférences, c’est pour ça qu’un code n’est jamais identique :slight_smile:

merci, c’est clair.