Zivilisation

Diesmal durften wir schon früh auf der Reise japanische Hochkultur genießen – die Flugzeuge von Japan Airlines haben ernsthaft ein Washlet:

Muss man MySQL denn alles mehrmals sagen?

Unser täglich MySQL-Rant gib uns heute…

Ein geschätzter Kollege meldete mir heute erstmalig, dass sich seine Datenbank auf unserem MySQL-Server (in einer separaten Instanz, in der nur diese Datenbank läuft) über zu viele Verbindungen beschwert – soll heißen, sie verweigert die Annahme weiterer Datenbankverbindungen, da die maximale Anzahl an Verbindungen bereits ausgereizt wurde.

Nun, ein schneller Blick in das Manual – der entsprechende Schalter heißt max_connections. Diesen also ebenso schnell in der my.cnf auf 1000 gesetzt:

max_connections = 1000

MySQL neu gestartet und MySQL vermeldet stolz:

mysql> show variables like 'max_connections';
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| max_connections | 214 |
+-----------------+-------+
1 row in set (0,00 sec)

Grmph. Natürlich gibt es keinen Hinweis in den Logfiles, was MySQL gestört haben könnte.

Probieren wir doch mal, im Live-System die Anzahl hochzusetzen:

mysql> set global max_connections = 1000;
Query OK, 0 rows affected (0,00 sec)

mysql> show variables like 'max_connections';
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| max_connections | 1000 |
+-----------------+-------+
1 row in set (0,00 sec)

Okay, das funktioniert – wirft aber eher neue Fragen auf…

Nur eine etwas längere Websuche später erfahre ich endlich hier, dass die Anzahl gleichzeitig offener Dateien hochgesetzt werden muss.

Dies kann man wahlweise umständlich über sysctl machen oder aber sehr elegant in der my.cnf selbst:

open_files_limit = 8192
max_connections = 1000

Kurzer Check:

mysql> show variables like 'max_connections';
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| max_connections | 1000 |
+-----------------+-------+
1 row in set (0,01 sec)

Das sieht doch schon mal super aus – die Ursache war also open_files_limit, das wir ja auf 8192 gesetzt haben:

mysql> show variables like 'open_files_limit';
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| open_files_limit | 1024 |
+------------------+-------+
1 row in set (0,00 sec)

Aehm, moment … 1024? Wo kommt denn jetzt 1024 her?

Kurzer Blick ins Manual: Default-Wert ist 0.

Hmm, was zeigt er denn an, wenn man keinen Wert in der my.cnf setzt?

mysql> show variables like 'open_files_limit';
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| open_files_limit | 1024 |
+------------------+-------+
1 row in set (0,00 sec)

Okay … wir lernen also: bei open_files_limit wird der Default-Wert von 0 beim MySQL-Start auf 1024 aufgerundet während ein expliziter Wert von 8192 auf 1024 heruntergesetzt wird.

Was passiert denn, wenn man einfach die 1024 selbst setzt (denn ohne ein explizites Setzen wird ja max_connections ignoriert?

open_files_limit = 1024
max_connections = 1000

Nunja, ausnahmsweise mal das Erwartete:

mysql> show variables like 'max_connections';
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| max_connections | 1000 |
+-----------------+-------+
1 row in set (0,01 sec)

mysql> show variables like 'open_files_limit';
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| open_files_limit | 1024 |
+------------------+-------+
1 row in set (0,00 sec)

Keine Ahnung was der Blödsinn soll, aber bis hierher könnte man das Gelernte folgendermaßen zusammenfassen:

Möchte man bei MySQL die maximale Anzahl gleichzeitiger Verbindungen (max_connections) erhöhen, muss man zusätzlich explizit die Anzahl gleichzeitig offener Verbindungen (open_files_limit) auf den Wert setzen, den sie so eh schon haben.

Wenn man jetzt noch ein gutes Gefühl hätte, dass die 1024 Filedeskriptoren wirklich für 1000 Verbindungen ausreichen werden…

So, genug geranted. Das eigentliche Problem liegt in der im Betriebssystem vorgegebenen Einstellung, wie hoch ein Nutzer die Anzahl offener Files setzen darf. Bei Ubuntu liegt diese standardmäßig bei 1024 und MySQL hat das lustige Verhalten, diesen Wert blind zu übernehmen (egal, was in der my.cnf steht – ja, auch wenn dort ein niedrigerer Wert steht).

Um also MySQL zu mehr Filedeskriptoren zu verhelfen, müssen wir doch beim Betriebssystem vorbeigucken (der vollständige Weg (OS+MySQL) ist u.a. hier beschrieben).

Ich habe also in /etc/security/limits.conf die beiden folgenden Zeilen hinzugefügt

mysql soft nofile 8192
mysql hard nofile 8192

und boshafterweise in my.cnf open_files_limit auf 4096 gesetzt und das Ergebnis lautet:

mysql> show variables like 'open%';
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| open_files_limit | 8192 |
+------------------+-------+
1 row in set (0,00 sec)

Das entspricht zwar immer noch nicht dem, was laut Handbuch passieren sollte, aber wer will schon kleinlich sein…

Zur eventuellen Einschränkung sei gesagt, dass wir hier von MySQL 5.5 sprechen – keine Ahnung, ob sich spätere Versionen anders verhalten.

Antispaß mit der S-Bahn oder: Wie man effektiv Stammkunden vergrault

Ach wären wir doch einfach mit dem Auto gefahren…

Eigentlich war unsere Planung für den heutigen Tag ganz banal: mit der S-Bahn zum Fahrradladen, dort ein Fahrrad für Nessi kaufen, dann mit der S-Bahn zurück nach Hause und den restlichen Abend fröhlich radeln.

Im Prinzip haben wir diese Planung auch mehr oder weniger durchgezogen, nur das die Stimmung ab Punkt drei (Rückfahrt mit der S-Bahn) ziemlich im Eimer war – doch der Reihe nach…

Ursprünglich wollten wir ein Faltrad kaufen – u.a. um es leichter ins Auto zu bekommen, aber sicherlich auch, weil für die Dinger im VBB kein separates Ticket nötig ist.

Nur eine Stunde Probefahren auf etlichen Falt- und Nicht-Falt-Fahrrädern später entschieden wir uns dann aber ob des Komforts doch für ein (natürlich todschickes) Damenrad.

Ok – gekauft, zum S-Bahnhof geradelt, kurz gewartet und rein in die S-Bahn und…
…natürlich in eine Fahrkartenkontrolle geraten. Und da wir entgegen unserer eigentlichen Planung kein Faltrad gekauft hatten, fehlte uns nun ein Fahrradticket.

Ein Unternehmen, dem irgendetwas an seinen Stammkunden (wir haben beide eine Umweltkarte) liegt, hätte jetzt vermutlich ein Auge zugedrückt – der Fahrradladen (Stadler) ist von der S-Bahn aus nicht zu übersehen, das Fahrrad ist offensichtlich neu (im Zweifel hätten wir natürlich auch sofort die Rechnung parat gehabt) – selbst der größte Widerling müsste zugeben, dass hier offensichtlich ein Versehen vorlag.

Aber die S-Bahn wäre nicht sie selbst, wenn sie die Situation nicht noch unangenehmer machen könnte, denn um nicht ganz wie ein Arschloch dazustehen, wählte der Kontrolleur seine stärkste Waffe: die Falschinformation.

Wir mögen jetzt einfach noch nicht die 60 EUR bezahlen, sondern bis zu unserem Ziel weiterfahren, dort ein Fahrradticket nachkaufen und entwerten. Dann sollen wir am nächsten Morgen zum Kundenbüro in den Ostbahnhof fahren und müssten lediglich 7 EUR bezahlen. Auf unsere Nachfrage, ob er das nochmal wiederholen könnte (es war in der Bahn ziemlich laut), blaffte er uns an, es stehe alles auf dem Zettel und schwups war er weg…

Also auf den Zettel geguckt und dort steht mehr oder weniger deutlich, dass es in unserem Fall auf gar keinen Fall eine Reduzierung der Strafe gibt… Wollte uns der Kontrolletti jetzt ernsthaft am Folgetag nochmal durch halb Berlin schicken, damit wir uns auch nochmal im Kundenbüro anblaffen lassen können? Dass auf dem Feststellungsbeleg auch noch eine völlig falsche S-Bahn angegeben wurde, rundete das Gesamtbild ab.

Doch es wird besser: wir saßen ja nun in der S-Bahn und hatten immer noch kein gültiges Fahrradticket (das sollten wir uns ja erst am Fahrtende besorgen) sondern lediglich eine Zahlungsaufforderung, auf der nichts von Ersatzfahrschein o.ä. steht.

Völlig verunsichert riefen wir aus dem Zug das Kundentelefon der S-Bahn an … und nur 5 min Sprachcomputer später ging sogar jemand ran.

Als ich den Satz damit begann, dass wir gerade kontrolliert wurden, fiel mir die Service-Dame sofort ins Wort, dass sie mir dann jetzt eh noch nicht helfen könne, da die Daten erst am Folgetag vorliegen würden.

Nur der lautstarke Hinweis meinerseits, dass wir evtl. gerade auf Anraten eines Kontrolleurs Schwarzfahren, brachte sie dazu, mir gnädigerweise weiter zuzuhören.

Machen wir’s kurz: das mit der Reduzierung der Strafe war (leider erwartungsgemäß) Bullshit, die Weiterfahrt und die gesamte Story mit dem Fahrradticket am Ende lösen ebenso. Wir fuhren gerade schwarz und sie bat (zumindest verstand ich es so trotz ihres Tonfalls) darum, am nächsten Bahnhof auszusteigen und ein Fahrradticket zu lösen – was wir dann auch taten.

Auf meine Nachfrage, ob wir bei Befolgung des Kontrolletti-Vorschlages bei einer weiteren Kontrolle trotz expliziten Hinweises auf eben diesen ersten Kontrolleur erneut als Schwarzfahrer gelten würden, antwortete sie im besten Ausweichgeschwurbel, dass die Kontrolleure keinen Ermessensspielraum hätten.

Auf die direkte Nachfrage, ob der Kontrolleur dementsprechend völligen Blödsinn erzählt habe, antwortete sie nach einer weiteren Ausweichantwort schließlich: “Ja, hat er!”

Kurze Pause beiderseits, dann meine Nachfrage: “Möchten Sie sich evtl. notieren, dass auf dem Ostring gerade Kontrolleure unterwegs sind, die Fahrgäste unfreiwillig zu (erneuten) Schwarzfahrern machen?”

In diesem Moment schien auch der Service-Dame aufzufallen, dass das evtl. wirklich eine gute Idee sein könnte und sie fragte die Kontrolleursnummer (steht auf dem Feststellungsbeleg) ab.

Meine Hoffnung, dass das was bringt ist zwar begrenzt, aber gar nicht zu versuchen, freilaufende Inkompetenz einzudämmen ist auch keine Lösung.

Zusammengefasst hat uns die S-Bahn den heutigen, eigentlich schönen Tag wirklich verdorben. Und ich habe wieder ein Argument weniger, mit dem ich die (rein) autofahrenden Kollegen zum Umsteigen bewegen könnte.

Danke S-Bahn – für nix!

P.S.: Falls es aus dem obigen Beitrag nicht deutlich genug herauskommt: wir haben uns mangels Erfolgsaussicht die Stadtreise zum Kundenbüro erspart und am Abend die 60 EUR überwiesen…

ESC 2016 – Finale

Wie jedes Jahr hier noch die fehlenden Bewertungen der Länder, die erst im Finale zum ersten Mal erscheinen

Italien (3-2-3-3)
Paliettenlatzhose vor Teletubbie-Kulisse mit eingeblendeten Luftblasen und dahinplätscherndem Song.

Schweden (2-2-2-1)
Ziemlich dreiste Kopie eines aktuellen Charthits mit maximiertem Schwiegersöhnchencharme.

Deutschland (4-4-5-4)
Nach all den Jahren endlich mal ein Auftritt, für den sich Deutschland nicht schämen muss…

Frankreich (4-3-3-2)
Gute-Laune-Song mit etwas langweiliger Kulisse.

Spanien (3-3-3-2)
Erneut wirkt die O-Bein-Choreographie nicht wirklich ästhetisch.

United Kingdom (2-2-3-2)
Optisch Tears-for-fears als Kinder ode Jedward in dunkel und mit kurzen Haaren. Der Song plätschert vor sich hin.

Erklärung des Quadrupels (a-b-c-d):
a, b: Wertung von Nessi
c, d: Wertung von Kuni
a, c: akustische Wertung
b, d: optische Wertung
1 (schlecht) <= a, b, c, d <= 5 (gut)

ESC 2016 – Zweites Halbfinale

Lettland (4-4-4-3)
Optisch fehlt dem Sänger eigentlich nur noch eine Schmalztolle und der 50er-Jahre-Rock’n’Roller-Eindruck ist perfekt. Akustisch erfreulicher- und durchaus überraschenderweise wesentlich moderner.

Polen (3-2-3-2)
D’Artagnan in schmalzig. Vereinigt die Elemente so ziemlich jeder 80er-Jahre-Ballade. Inklusive Mini-Rückung am Ende.

Schweiz (2-2-3-2)
Das hatten wir auch noch nicht: eine dampfende Sängerin, die auf der Bühne Kniebeugen macht. Lenkt gut vom etwas eintönigen Lied ab.

Israel (4-3-4-3)
Beeindruckende Stimme, die durch das Klavier besonders gut zur Geltung kommt. Lenkt gut von der fragwürdigen Frisur ab 🙂 Funkensprühendes, fulminantes Finale.

Weißrussland (4-3-4-2)
Das war es, was dem ESC bisher noch gefehlt hatte: ein Sänger, der zum Liedbeginn nackt einen Wolf anschreit. Der Song danach ist wirklich gut, man darf nur dem Interpreten nicht beim Singen zusehen.

Serbien (3-2-3-2)
Optisch Vampirella mit schmachtendem und bisweilen grabbelndem Background-Tänzer. Akustisch solider ESC-Pop, natürlich mit großer Final-Rückung.

Irland (2-2-2-2)
Wie kann jemand, der aussieht wie Bryan Adams so eine dünne Stimme haben? Der Song ist ähnlich unspektakulär.

Mazedonien (2-2-3-3)
Stimmgewaltig ist noch untertrieben – akustisch wird man von der Sängerin regelrecht erschlagen. Optisch sehr puristisch angelegt.

Litauen (3-3-4-3)
Optisch und akustisch ein rundum gelungener Boygroup-Auftritt – nur das die restliche Band den Sänger versetzt hat.

Australien (5-4-5-4)
Keine Ahnung, wo die Sängerin die tolle Stimme herholt. Das Lied passt perfekt und auch die Optik wirkt mehr als stimmig.

Slowenien (4-3-4-3)
Superschneller Disco-Pop mit Jodeleinlagen … und einem Stangentarzan.

Bulgarien (4-2-4-3)
Wirklich nette Synthie-Pop-Nummer mit etwas fragwürdiger Choreographie (O-Beine sehen bei niemandem gut aus).

Dänemark (3-3-3-3)
Solider 90er-Boygroup-Pop. Zum stimmigen Gesamtbild fehlen eigentlich nur noch die Kuscheltiere, die am Ende auf die Bühne fliegen.

Ukraine (2-2-2-2)
Dunkler, etwas psychedelischer Eso-Drogen-Trip?

Norwegen (4-3-4-3)
Ziemlich gewöhnungsbedürftige Rhythmuswechsel, die das klatschwütige Publikum hörbar zur Verzweiflung brachten.

Georgien (2-1-1-1)
AFAIR der erste Britpop beim ESC – nicht das er vermisst worden wäre. Eine Epilepsie-Warnung wäre vor dem Auftritt nett gewesen…

Albanien (2-2-3-2)
Optisch und partiell auch akustisch Goldfinger.

Belgien (3-3-3-3)
Netter Retro-Auftritt. Wirkt wie eine krude Mischung aus 80er-Jahre-Pop, 70er-Jahre-Kulisse und ein klein bisschen Wadde-Hadde-Dudde-Da.

Erklärung des Quadrupels (a-b-c-d):
a, b: Wertung von Nessi
c, d: Wertung von Kuni
a, c: akustische Wertung
b, d: optische Wertung
1 (schlecht) <= a, b, c, d <= 5 (gut)

ESC 2016 – Erstes Halbfinale

Finnland (3-2-2-2)
Belangloser Spandex-Einstieg in den diesjährigen ESC mit deutlichen Schwächen bei den hohen Tönen.

Griechenland (2-2-1-2)
In den Strophen Ethno-Hip-Hop, im Refrain seichtes Restaurant-Gedudel. Mit Puppenspieltanzeinlage…

Moldawien (3-2-3-2)
Kosmonautenbreakdance zum Discofox. Song ist ganz nett, aber die Sängerin kommt nicht ansatzweise gegen die Synthesizer an.

Ungarn (3-3-3-3)
Mal was neues – Backgroundpfiffchor mit Taikobegleitung. Der Sänger klingt etwas unerwartet wie Bonnie Tyler in männlich.

Kroatien (4-3-4-3)
Die Stimme erinnert frappierend an Röyksopp, der Musikstil an Enigma. Das Kleid hingegen wirkt wie ein Frontalzusammenstoß von Stoff- und Alufolienrollen. Die erste Rückung des Abends.

Niederlande (3-3-3-3)
Das war also mal wieder der amerikanische Beitrag zum ESC – in einem Country-Club sicherlich einer der besseren Songs. Inklusive volkes- ähm, zuschauernähe heuchelndem Händeschütteln.

Armenien (3-3-4-4)
Endlich zeigt mal ein Teilnehmer, was die diesjährige Bühne alles an Tricktechnik hergibt. Die hektischen Kameraschnitte geben den Rest…

San Marino (2-2-3-2)
Retro-Song in bester 70er-Jahre-Boney-M-Manier.

Russland (2-4-2-4)
Optisch die logische Fortsetzung des letztjährigen Siegertitels. Lenkt gut vom eher durchschnittlichen Song ab.

Tschechien (3-2-3-2)
Sehr dreiecklastige Optik. Nette Ballade.

Zypern (2-2-2-1)
Ich würde gerne etwas zur Optik schreiben, aber die Schnitte waren so heftig häufig, dass kein Bild lang genug auf der Netzhaut liegen blieb. Immerhin gab es Käfige – wären sie nur sinnvoller benutzt worden…

Österreich (2-3-4-4)
Fröhlicher französischer Flower-Disco-Pop mit passendem Puppenkleid.

Estland (2-2-2-2)
Tolle Stimme, die bei dem Song leider nicht wirklich sehr zur Geltung kommt. Da hilft auch kein noch so toller Kartentrick…

Aserbaidschan (3-3-1-2)
Sehr psychedelischer Gesamteindruck – und ein Typ in goldfarbener American-Football-Unterwäsche.

Montenegro (2-2-1-2)
Laut, unübersichtlich, grell und mit grimmig dreinblickender Blondine – halt ein ganz normaler Rocksong.

Island (1-2-1-2)
Optisch deutliche Anleihen beim Vorjahressieger. Akustisch wurden etwas zu viele Ideen zusammengemischt.

Bosnien und Herzegowina (2-2-2-2)
Endlich erfahren wir, wozu die Aludecken aus dem Erste-Hilfe-Koffer wirklich da sind. Klassische osteuropäische Powerballade – das schrub ich und dann kam der bosnische Sido auf die Bühne.

Malta (2-2-2-2)
Sportlicher Pluspunkt für den dauerwegten Tanzninja.

Erklärung des Quadrupels (a-b-c-d):
a, b: Wertung von Nessi
c, d: Wertung von Kuni
a, c: akustische Wertung
b, d: optische Wertung
1 (schlecht) <= a, b, c, d <= 5 (gut)

SQL-Namenskonventionen

Es gibt wohl kaum ein Thema, dass im Datenbankumfeld so umstritten ist, wie ein konkretes Schema für die Benennung von Objekten in Datenbanken.

Es gibt nicht nur keinen Standard, selbst so etwas wie eine einfache Konvention scheint ob der völlig konträren Positionen der Diskussionsteilnehmer völlig unmöglich zu sein.

Aus diesem Grund soll die folgende Liste von Stichpunkten auch keinerlei Anspruch auf die objektive Wahrheit erheben, sondern lediglich das wiedergeben, was sich in zahlreichen Projekten von mir als sinnvoll erwiesen hat.

  • kein Abweichen von der ersten Normalform – d.h. niemals strukturierte Daten in einzelnen Datenbankfeldern
  • so normalisiert wie möglich (insbesondere niemals Daten redundant speichern) – nur in Ausnahmefällen denormalisiert lassen (z.B. Anrede “Herr/Frau” nicht in separate Tabelle auslagern)
  • grundsätzlich alle Bezeichner in Kleinbuchstaben; Unterstriche sind erlaubt (“person” statt “Person”)
  • grundsätzlich alle Bezeichner in der gleichen Sprache
  • Tabellen und Attributnamen im Singular (“person” statt “personen”)
  • Attributnamen (außer Fremdschlüssel) mit Präfix, der Kurzform des Tabellennamens wiederspiegelt (“pers_name” statt “name”)
  • Primärschlüssel ist immer eine separate Spalte namens Präfix + “_id” (“pers_id”) vom Typ Serial (PostgreSQL) bzw. Integer Auto Increment (MySQL)
  • Fremdschlüsselnamen werden inklusive Präfix von der referenzierten Tabelle übernommen (“pers_id” bleibt in jeder referenzierenden Tabelle “pers_id”)
  • keine Abkürzungen (“pers_geburtsjahr” statt “pers_geb”)
  • keine “Anreicherung” von Attributnamen (“pers_geburtsjahr” statt “pers_geburtsjahr_int”)
  • Spalten mit Booleschen Werte so benennen, dass sofort klar ist, wofür true/false stehen (“maennlich” statt “geschlecht”)

Diese Liste ist zwar offensichtlich unvollständig, aber selbst diese minimale Konvention hat mir schon sehr gute Dienste, vor allem bei der Erklärung der Datenbankschemata gegenüber Dritten, geleistet.

Der Monat fängt ja gut an

Unser täglich MySQL-Rant gib uns heute (ok, der erste Teil ist zugegebenermaßen nur ein Verständnisproblem)…

Ich wurde heute von der Frage eines Studenten überrascht, warum man, wenn man vom 19. April 19 Tage abzieht und dann noch einen weiteren Tag abzieht, als Ergebnis beim 1. April landet.

Genaugenommen ging es ihm um die folgende MySQL-Anweisung aus einem älteren Software-Projekt:

mysql> select CURRENT_DATE - INTERVAL DAYOFMONTH(CURRENT_DATE) - 1 DAY;
+----------------------------------------------------------+
| CURRENT_DATE - INTERVAL DAYOFMONTH(CURRENT_DATE) - 1 DAY |
+----------------------------------------------------------+
| 2016-04-01                                               |
+----------------------------------------------------------+
1 row in set (0.00 sec)

Hmm, was ist denn, wenn wir den Tag am Ende nicht abziehen?

mysql> select CURRENT_DATE - INTERVAL DAYOFMONTH(CURRENT_DATE) - 0 DAY;
+----------------------------------------------------------+
| CURRENT_DATE - INTERVAL DAYOFMONTH(CURRENT_DATE) - 0 DAY |
+----------------------------------------------------------+
| 2016-03-31                                               |
+----------------------------------------------------------+
1 row in set (0.00 sec)

Ok, …, auch interessant. Bei der Suche nach einer Erklärung irritierte mich erstmal, was das INTERVAL in der Anweisung sollte. Ist das ein Cast, oder was?

Die (nach wie vor grottige) MySQL-Doku kennt INTERVAL in der Übersicht der Datums- und Zeitfunktionen nicht als separate Anweisung oder Funktion, sondern lediglich als Schlüsselwort innerhalb von Datumsfunktionen wie z.B. DATE_ADD.

Erst bei deren Beschreibung findet man die nette Information, dass man mittels INTERVAL Datumsberechnungen durchführen kann und zwar mit der Syntax:

date + INTERVAL expr unit

Und damit erkennt man dann auch, wie die originale Anweisung interpretiert wird, nämlich so:

mysql> select CURRENT_DATE - INTERVAL (DAYOFMONTH(CURRENT_DATE) - 1) DAY;
+------------------------------------------------------------+
| CURRENT_DATE - INTERVAL (DAYOFMONTH(CURRENT_DATE) - 1) DAY |
+------------------------------------------------------------+
| 2016-04-01                                                 |
+------------------------------------------------------------+
1 row in set (0,00 sec)

Ok, Rätsel gelöst, ein paar Nebenerkenntnisse gibt es dann doch noch…

Spaßig wird die Sache z.B., wenn man mal ohne INTERVAL arbeitet:

mysql> select CURRENT_DATE - DAYOFMONTH(CURRENT_DATE);
+-----------------------------------------+
| CURRENT_DATE - DAYOFMONTH(CURRENT_DATE) |
+-----------------------------------------+
|                                20160400 |
+-----------------------------------------+
1 row in set (0.00 sec)

Und ja, derartig ungültige Daten darf man sogar in Tabellen abspeichern! Die Doku dazu liest sich MySQL-typisch unbesorgt – man darf halt einfach nicht zuviel “erwarten”, dann geht alles gut 🙂

MySQL permits you to store dates where the day or month and day are zero in a DATE or DATETIME column. […] In this case, you simply store the date as ‘2009-00-00’ or ‘2009-01-00’.

If you store dates such as these, you should not expect to get correct results for functions such as DATE_SUB() or DATE_ADD() that require complete dates.[…]

Die Behandlung des nichtexistenten Jahres 0 kann dann auch niemanden mehr überraschen:

MySQL permits you to store a “zero” value of ‘0000-00-00’ as a “dummy date.” This is in some cases more convenient than using NULL values, and uses less data and index space.

Dies bringt mich zu PostgreSQL, wo selbstverständlich jedwede ungültigen Daten verboten sind. Der zugehörige Abschnitt aus dem Manual gehört zu meinen Lieblingstellen:

The first century starts at 0001-01-01 00:00:00 AD, although they did not know it at the time. This definition applies to all Gregorian calendar countries.

There is no century number 0, you go from -1 century to 1 century. If you disagree with this, please write your complaint to: Pope, Cathedral Saint-Peter of Roma, Vatican.

Nur der Vollständig halber: in PostgreSQL kommt die Berechnung des Monatsersten ohne unschöne Subtraktionen aus – das Äquivalent zu ersten SQL-Anweisung in diesem Blogeintrag lautet:

#select date_trunc('month', current_date)::date;

 date_trunc 
------------
 2016-04-01
(1 row)

Augen auf beim Zwiebelkauf

Nach dem animalischen nun ein vegetarischer Beitrag aus der Kategorie “Das merken die nie!”

Die Lebensmittelmafia wird immer dreister und streckt teure Zwiebelmengen mit billigen Kartoffeln:

image

Zur Ehrenrettung des Supermarktes: es war nur eine Kartoffel im Netz und die daneben liegenden Netze schienen “Zwiebel pur” zu sein…

…nur echt ohne Deppenapostroph