[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Bash Mysql Tutorial


On Sun, Dec 11, 2005 at 11:07:07PM +0100, Michael Lestinsky wrote:
> Am 11.12.2005 schrieb Christian Eichert:
> > Hat jemand ein Tutorial zum Thema Mysql und Bash?
> 
> Aeh, wozu?
> 
>   echo "select * from Blubtable" | mysql -uuser -ppassword spinatdb

Das ist schon ein guter Anfang, aber man will ja meistens nicht nur
Daten aus einer DB rausholen, sondern auch damit sinnvoll arbyten. Und
dazu will ich kurz vorstellen, wie ich das in der Praxis handhabe:

Bash und Arrays sind so ein Thema fuer sich, deswegen empfiehlt es sich,
alle Array-Operationen sequentiell abzuarbeiten, d.h. Zeile fuer Zeile.

Richtg ist: 
        * mysql nimmt SQL auf stdin entgegen
        * mysql erzeugt auf stdout eine tab-separierte Liste mit den
          Datensaetzen.


Das Kommando arbeitet nach dem EVA-Prinzip: Ein/Ausgaben auf
stdin/stdout, dazwischen der Zugriff auf die Datenbank, von dem das
drumherumliegende Shellscript nichts weiss. Vermutlich geht das auch mit
PGSQL oder auch anderen Datenbank-Shells auf Oracle, Informix, ... Das
script muss nichts ueber die darunterliegende Datenbank wissen, solange
das verwendete "Frontend" nur stdin/stdout bedient.

---> [mysql.stdin][mysql.db][mysql.stdout] -->
                    |   î
                    |   |
                    v   |
             [mysqld (Server)]




Will man damit sinnvoll arbyten, ist das ein guter Ansatz:

--------------------------------------------------

echo "select id,bla,fasel,blubber 
      from foo 
        [weitere joins]
      where bar=1
        [weitere Bedingungen]
      [weiteres SQL]
" | 
mysql [parameter fuer den Zugriff] |   # man mysql
tail -n +2 |                          # Die Kopfzeile mit den Ueberschriften wegwerfen
tr '\t' '|' |                         # alle Tabs durch "|" setzen, siehe IFS unten
while IFS='|' read ID BLA FASEL BLUBBER junk; do # Zeilenweise einlesen
  # in dieser Schleife kann mit den Werten $ID, $BLA, $FASEL und
  # $BLUBBER gearbytet werden, zB um damit irgendwelche befehle zu
  # fuettern oder aber auch, um daraus wieder mit echo SQL auszugeben
  echo "update foo set xyz='abc mit $BLA und $FASEL', date=NOW(), blubber='${BLUBBER}__'  where id='$ID';"
done |
sort |          # Dieses sort ist nur unter bestimmten Bedingungen notwendig
mysql [parameter fuer Zugriff]

--------------------------------------------------

Oder stark verkuerzt:

echo $SELECT | mysql | tail -n +2 | tr '\t' '|' | 
while IFS='|' read VARIABLEN junk; do [Arbyten mit VARIABLEN]; done


Warum das sort als vorletztes Element in der Pipe?

Wenn in der Schleife SQL-Statements erzeugt werden, die
Schreiboperationen auf der Tabelle ausfuehren, von der der select oben
liest, dann kann es zu einer deadlock-Situation kommen, gerade wenn es
sich um hunderte Datensaetze handelt.

Alle Update-Befehle werden in eine Warteschlange gestellt und
ausgefuehrt, wenn der select beendet ist. Wenn zu viele update-Befehle
warten, dann nimmt der schreibende mysql-Prozess irgendwann keine Daten
mehr an und blockiert damit auch die Verarbytung der while-Schleife, die
wiederum u.U. den lesenden mysql-Prozess blockiert, der dann seinen
select nicht zu Ende bringen kann. In der Praxis habe ich so ein
Verhalten bisher nicht erlebt, weil die gesamte Pipe ziemlich elastisch
ist, d.h an vielen Stellen grosse Puffer beteiligt sind.

Der sort-Befehl bewirkt folgendes: er liest alle Daten aus stdin bis zum
Dateiende, d.h. solange, bis keine Daten mehr kommen, sortiert das ganze
dann um und gibt es auf stdout weiter. Damit ist als Seiteneffekt
gewaehrleistet, dass die update-Statements erst dann ausgefuehrt werden,
wenn der select zu Ende ist. Die tatsaechliche Reihenfolge der
update-Statements ist idR egal.

Neuere Versionen von MySQL haben ein etwas kooperativeres Verhalten, 
was konkurrierende Zugriffe von select und update angeht. Sicher ist
sicher. 

Wenn der schreibende MySQL-Prozess in eine andere Tabelle schreibt als
die im join angegeben, kann man das auch parallel laufen lassen, und
damit potenziell auch schneller.

Das ganze Shellkonstrukt ansich ist ziemlich performant.

Weitere Beispiele:

Aus einem Vortrag:
  http://rabe.uugrn.org/scripts/vortrag/mysql.html

Stammtisch-Termine berechnen mit SQL-Ausgabe (insert-Statements):
  http://rabe.uugrn.org/scripts/uugrn_stammtischkalender.sh

FreeBSD Ports in MySQL abbilden:
  http://rabe.uugrn.org/scripts/FreeBSD/port-bla/import-urls.sh
  http://rabe.uugrn.org/scripts/FreeBSD/port-bla/load-descr.sh


Vielleicht solltest Du uns mitteilen, was genau Du Dir davon
versprichst, MySQL in Shellscripten zu verwenden, also welches Ziel du
damit verfolgst.

MfG
-- 
Raphael Becker                                    http://rabe.uugrn.org/
                      http://schnitzelmitkartoffelsalat.und.rahmspin.at/
.........|.........|.........|.........|.........|.........|.........|..