<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Groschengedanken zu Software &#187; sql</title>
	<atom:link href="http://aysx.de/eingroschen/?feed=rss2&#038;tag=sql" rel="self" type="application/rss+xml" />
	<link>http://aysx.de/eingroschen</link>
	<description>... und vielleicht auch dem ganzen Rest</description>
	<lastBuildDate>Tue, 18 Jun 2019 17:59:37 +0000</lastBuildDate>
	<language>de-DE</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.2.38</generator>
	<item>
		<title>SQuirreL &#8211; ein sql-Client</title>
		<link>http://aysx.de/eingroschen/?p=206</link>
		<comments>http://aysx.de/eingroschen/?p=206#comments</comments>
		<pubDate>Wed, 20 Nov 2013 17:59:25 +0000</pubDate>
		<dc:creator><![CDATA[matthias]]></dc:creator>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[asymmetrie]]></category>
		<category><![CDATA[sql]]></category>
		<category><![CDATA[squirrel]]></category>
		<category><![CDATA[Symmetrie]]></category>

		<guid isPermaLink="false">http://aysx.de/eingroschen/?p=206</guid>
		<description><![CDATA[Ich nutze seit Jahren SQuirreL als SQL-Client. Nicht besonders hübsch, aber funktioniert im Alltag tadellos. Und weil Funktion bei Software wichtiger ist als Style, arbeite ich gerne damit. Die Spitze des Non-Styles bilden die innig geliebten Unix-Komandos, denn man kann &#8230; <a href="http://aysx.de/eingroschen/?p=206">Weiterlesen <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Ich nutze seit Jahren <a title="squirrel Homepage" href="http://squirrelsql.org/" target="_blank">SQuirreL</a> als SQL-Client. Nicht besonders hübsch, aber funktioniert im Alltag tadellos. Und weil Funktion bei Software wichtiger ist als Style, arbeite ich gerne damit. Die Spitze des Non-Styles bilden die innig geliebten Unix-Komandos, denn man kann sie nutzen, ohne sie zu sehen. Gleich danach kommt &#8220;vim&#8221; &#8211; optisch _sehr_ sparsam. Und XPoint. Erinnert sich jemand an CrossPoint von Peter Mandrella? &#8230; Neulich bin ich in SQuirreL auf ein Feature gestoßen, das den Gebrauchswert dieser Anwendung für mich steigert: Bookmarks. Dahinter verbirgt sich die Möglichkeit, wiederkehrende Abfragen so abzulegen, dass sie mit wenigen Tastendrücken erreichbar sind. Ich bin Tastatur-Nutzer und deshalb liebe ich dieses Feature.</p>
<p>Beispiel</p>
<p>&#8220;select * from adresse&#8221; im Editor-Fenster markieren, dann Menü &#8220;Session &#8211; Bookmark &#8211; Add&#8221;, Name sei &#8220;adr*&#8221;, bei Beschreibung gebe ich ein &#8220;Alle Adressen&#8221;. Dann kann ich im Editor-Fenster Strg-j drücken und bekomme die Liste aller Bookmarks präsentiert. Mit jedem Tastendruck reduziert sich die Anzahl der Elemente in der Auswahl. Wenn ich &#8220;a&#8221; und &#8220;d&#8221; eingetippt habe, sehe ich nur noch &#8220;adr*&#8221; und kann auf &#8220;Return&#8221; hämmern, um den Befehl in den Editor einzufügen und Strg-Return, um den Befehl auszuführen. Sehr bequem!<br />
Für &#8220;select * from adresse&#8221; wird man kein Bookmark anlegen, aber für das <a title="Weihnachtsgeschäft" href="http://aysx.de/eingroschen/?p=186">WeihnachtsgeschäftSQL</a> schon.</p>
<p>Asymmetrie</p>
<p>Ich reite nicht gerne auf Dingen herum. Einiges ist eben omnipräsent ;) Das Bookmark-Plugin von SQuirreL weist eine Asymmetrie auf: Man kann Bookmarks nicht auf dem Weg löschen, auf dem man sie erstellt hat. Aber man ist dem nicht ausgeliegert: um ein Bookmark zu bearbeiten gehe ins Menü &#8220;File &#8211; Global Preferences&#8221;, dort gibt es einen Reiter &#8220;Bookmark&#8221; und darin entsprechende Buttons für das Erstellen, Bearbeiten und Löschen.</p>
<p>Alternative: Das Plugin speichert seine Einstellungen in ~/.squirrel-sql/plugins/sqlbookmark/bookmarks.xml. Darin enthalten sind <code>&lt;Bean&gt;</code>-Tags. Finde das passende Tag und editiere oder lösche es. Oder füge ein Tag hinzu, das neue Bookmark wird nach dem Neustart von SQuirreL zur Verfügung stehen.</p>
<p>Schmankerl: Parameter</p>
<p>Ein Bookmark ist erstmal nur ein kurzer Weg über die Tastatur zum Befehl. Im Alltag kommt es oft vor, dass ein Befehl einen Parameter braucht, um zum Ergebnis zu kommen.Beispiel: In &#8220;select * from adresse where id = 42&#8243; ist &#8220;42&#8221; der Wert des Parameters. Und oft kommt mehr als ein Parameter vor. Beispiel:<br />
<code><br />
select * from adresse where Name = 'Kopp' and Vorname='Matthias'<br />
</code><br />
Die Werte unterscheiden sich natürlich bei jeder Abfrage, und es ist ziemlich nervig, immer wieder durch den SQL-Text zu laufen und die Werte zu verändern. Bookmarks in SQuirreL können Dir diese Lauferei abnehmen. Erstelle das Bookmark in der Form<br />
<code><br />
select * from adresse where Name = '${Name-Eingabe}' and Vorname='${Vorname-Eingabe}'<br />
</code><br />
Dann fragt SQuirreL die beiden Werte ab, wenn Du das Bookmark aufrufst, und setzt die beiden Werte ein, wenn es den Text in den Editor gibt. Wenn derselbe Variablenname mehrfach vorkommt, wird er nur einmal abgefragt und bei jedem Vorkommen passend ersetzt. </p>
<p>Wunderbar. Danke, SQuirreL.</p>
]]></content:encoded>
			<wfw:commentRss>http://aysx.de/eingroschen/?feed=rss2&#038;p=206</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Views als Schnittstelle</title>
		<link>http://aysx.de/eingroschen/?p=197</link>
		<comments>http://aysx.de/eingroschen/?p=197#comments</comments>
		<pubDate>Wed, 20 Nov 2013 16:34:19 +0000</pubDate>
		<dc:creator><![CDATA[matthias]]></dc:creator>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[CleanCode]]></category>
		<category><![CDATA[Schnittstelle]]></category>
		<category><![CDATA[sql]]></category>
		<category><![CDATA[view]]></category>

		<guid isPermaLink="false">http://aysx.de/eingroschen/?p=197</guid>
		<description><![CDATA[Wenn man mittel Java oder PHP oder einer Programmiersprache deiner Wahl auf ein RDBMS zugreift, dann kann man Code erstellen, der sich ohne Umwege der Tabellen bedient. Man kennt den Namen der Tabellen und ihre Spaltenbezeichnungen. Dann ist fix eine &#8230; <a href="http://aysx.de/eingroschen/?p=197">Weiterlesen <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Wenn man mittel Java oder PHP oder einer Programmiersprache deiner Wahl auf ein RDBMS zugreift, dann kann man Code erstellen, der sich ohne Umwege der Tabellen bedient. Man kennt den Namen der Tabellen und ihre Spaltenbezeichnungen. Dann ist fix eine Abfrage gecodet und &#8211; schwupps &#8211; erscheint das Ergebnis in der Anwendung. So weit, so gut, so einfach.</p>
<p>Kompliziert wird es, wenn Tabellen sich ändern &#8211; beispielsweise wegen Kundenanpassungen. Für den bereits bestehenden Code ist das nicht gut, denn der &#8220;wettet&#8221; ja auf eine ganz bestimmte Struktur. Beim Ändern der Tabelle müßte man also darauf achten, bestehenden Code nicht zu zerbrechen &#8211; was für eine Qual! Ich müßte den gesamten Code abklappern &#8230; gar nicht gut, das dauert viel zu lange und Chef sitzt mir im Nacken. Was tun?</p>
<p>Eine Option ist, den Quelltext so zu organisieren, dass mir das Abklappern leicht fällt. Aber das ist ein anderes Thema. Hier und jetzt möchte ich die Seite des RDBMS beleuchten. Kann mir das helfen? Natürlich kann es das.</p>
<p>Die mir bekannten RDBMS erlauben es, Views zu erstellen. Ein View kann man sich als benannte und im RDBMS gespeicherte Abfrage vorstellen. Das ist eine hilfreiche Option, dem Kind einen Namen geben zu können &#8211; hilfreich für das Verständnis, das Debuggen. Und man kann die Daten so bekommen, wie man sie braucht: Man kann weglassen, Spalten umbenennen, mit <code>join</code> arbeiten usw. &#8211; eben all die Techniken nutzen, die ein <code>select</code> bietet. Der View &#8220;kennt&#8221; die benötigten Tabellennamen, aber der Nutzer des Views braucht die beteiligten Tabellennamen und -strukturen nicht zu kennen. Der Nutzer des Views kennt &#8211; nur den View. Wie der View zu seinem Ergebnis kommt, ist dem View-Abfrager gleichgültig. In der Programmierung nennen wir dieses Prinzip &#8220;Kapselung&#8221;.</p>
<p>Auf der Seite der Client-Programmierung macht es keinen Unterschied, ob ich eine Tabelle abfrage oder einen View. Deswegen bietet das Erstellen von Views die Möglichkeit, eine stabile Schnittstelle aufzubauen. Feine Sache, weil der zugreifende Code nicht zerbrechen wird, wenn ich eine Tabelle ändere, die jenseits der Schnittstelle liegt.</p>
<p>Eitel Sonnenschein? Fast. Woher weiss ich, dass eine Tabellenänderung nicht Views kaputt macht? Hier hilft mir eine Automatisierung: Ich habe ein Script, das alle Views löscht. Und ich habe ein Script, das alle Views aufbaut. Wenn die beiden Scripte erfolgreich abgearbeitet werden können, dann ist&#8217;s in Ordnung. Wenn nicht, dann kann ich anhand der Fehlermeldung beim Anlegen genau sehen, was und wo es schiefgeht.</p>
<p>Offene Frage: Ist einfügen in und löschen aus Views möglich? Mit Oracle&#8217;s RDBMS schwant mir, dass es möglich ist, wenn der View &#8220;key preserved&#8221; ist. Mssql löscht einfach &#8211; und unter Umständen ist man überrascht, was da gelöscht wird und was nicht :-/</p>
<p>Offene Frage: Gibt es Views mit Parametern? Das wäre praktisch &#8230;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://aysx.de/eingroschen/?feed=rss2&#038;p=197</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Weihnachtsgeschäft</title>
		<link>http://aysx.de/eingroschen/?p=186</link>
		<comments>http://aysx.de/eingroschen/?p=186#comments</comments>
		<pubDate>Tue, 19 Nov 2013 20:28:58 +0000</pubDate>
		<dc:creator><![CDATA[matthias]]></dc:creator>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[crm]]></category>
		<category><![CDATA[Kundenbetreuung]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[sql]]></category>
		<category><![CDATA[Wirtschaftsinformatik]]></category>

		<guid isPermaLink="false">http://aysx.de/eingroschen/?p=186</guid>
		<description><![CDATA[Es ist November, das Weihnachtsgeschäft soll&#8217;s &#8216;rausreissen. Also schicken wir denjenigen Kunden eine Ansichtskarte an die letzte Lieferadresse, die seit zwei Jahren nichts gekauft haben, aber davor wenigstens dreimal etwas geordert haben. Jetzt Du, Programmierer &#8230; Hier ist ein Ausschnitt &#8230; <a href="http://aysx.de/eingroschen/?p=186">Weiterlesen <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Es ist November, das Weihnachtsgeschäft soll&#8217;s &#8216;rausreissen. Also schicken wir denjenigen Kunden eine Ansichtskarte an die letzte Lieferadresse, die seit zwei Jahren nichts gekauft haben, aber davor wenigstens dreimal etwas geordert haben. Jetzt Du, Programmierer &#8230;</p>
<p>Hier ist ein Ausschnitt der (My)Sql-Tabellen-Struktur, gefüllt mit Fantasie-Daten. Ich habe es für MySQL entwickelt, es sollte mit keinen oder wenigen Anpassungen auf andere RDBMS übertragbar sein.</p><pre class="crayon-plain-tag">CREATE TABLE adresse
(
   id int PRIMARY KEY NOT NULL,
   vorname varchar(45),
   name varchar(45),
   strasse varchar(80),
   plz varchar(10),
   ort varchar(20)
);

insert into adresse (id,vorname,name,strasse,plz,ort) values (1,'Peter','Mustermann','Musterstr.42','12345','Musterhausen');
insert into adresse (id,vorname,name,strasse,plz,ort) values (2,'Paul','Mustermann','Musterstr.43','12345','Musterhausen');
insert into adresse (id,vorname,name,strasse,plz,ort) values (3,'Erika','Musterfrau','Musterstr.44','12345','Musterhausen');
insert into adresse (id,vorname,name,strasse,plz,ort) values (4,'Erna','Normalverbraucher','Musterstr.45','12345','Musterhausen');
insert into adresse (id,vorname,name,strasse,plz,ort) values (5,'Erna','Normalverbraucher','Ottostr.45','12345','Musterhausen');

CREATE TABLE bestellung
(
   id int PRIMARY KEY NOT NULL,
   rechnungsadresse int DEFAULT 0 NOT NULL,
   lieferadresse int DEFAULT 0 NOT NULL,
   id_person int DEFAULT 0 NOT NULL,
   datum timestamp NOT NULL
);

insert into bestellung (id,rechnungsadresse,lieferadresse,id_person,datum) values (1,1,1,1,'2010.12.01');
insert into bestellung (id,rechnungsadresse,lieferadresse,id_person,datum) values (2,1,1,1,'2011.01.01');
insert into bestellung (id,rechnungsadresse,lieferadresse,id_person,datum) values (3,1,1,1,'2011.02.01');
insert into bestellung (id,rechnungsadresse,lieferadresse,id_person,datum) values (4,1,1,1,'2011.12.01');
insert into bestellung (id,rechnungsadresse,lieferadresse,id_person,datum) values (5,1,1,1,'2012.01.01');
insert into bestellung (id,rechnungsadresse,lieferadresse,id_person,datum) values (6,1,1,1,'2012.12.01');
insert into bestellung (id,rechnungsadresse,lieferadresse,id_person,datum) values (7,2,2,2,'2010.12.02');
insert into bestellung (id,rechnungsadresse,lieferadresse,id_person,datum) values (8,2,2,2,'2011.02.01');
insert into bestellung (id,rechnungsadresse,lieferadresse,id_person,datum) values (9,3,3,3,'2011.02.03');
insert into bestellung (id,rechnungsadresse,lieferadresse,id_person,datum) values (10,3,3,3,'2011.12.03');
insert into bestellung (id,rechnungsadresse,lieferadresse,id_person,datum) values (11,3,3,3,'2012.03.03');
insert into bestellung (id,rechnungsadresse,lieferadresse,id_person,datum) values (12,3,3,3,'2012.12.03');
insert into bestellung (id,rechnungsadresse,lieferadresse,id_person,datum) values (13,4,4,4,'2011.05.04');
insert into bestellung (id,rechnungsadresse,lieferadresse,id_person,datum) values (14,4,4,4,'2011.07.04');
insert into bestellung (id,rechnungsadresse,lieferadresse,id_person,datum) values (15,5,4,4,'2011.09.04');</pre><p>Zuerst finde heraus, wer wieoft bestellt hat und wann die letzte Bestellung stattfand &#8211; unter Bachtung der oben gegebenen Einschränkungen:</p><pre class="crayon-plain-tag">-- INTERVAL 22 MONTH
-- weil es erst November ist und wir den Januar auch &quot;mitnehmen&quot; m&ouml;chten.

SELECT id_person, max(datum) as letzteBestellung, count(*) as anzahlBestellungen
FROM (
	SELECT id_person, datum
	FROM bestellung
) t1
group by id_person
having count(*) &amp;gt;= 3 -- wenigstens 3 Bestellungen 
and max(datum) &amp;lt; DATE_SUB(now(), INTERVAL 22 MONTH) -- und seit 22 Monaten ohne Umsatz</pre><p>Aufgrund dieser Daten suche ich die Rechnungsadresse &#8211; mit einem self-join:</p><pre class="crayon-plain-tag">SELECT distinct taba.id_person, tabb.letzteBestellung, tabb.anzahlBestellungen, taba.rechnungsadresse
FROM bestellung taba inner join
(
	SELECT id_person, max(datum) as letzteBestellung, count(*) as anzahlBestellungen
	FROM (
		SELECT id_person, datum
		FROM bestellung
	) t1
	group by id_person
	having count(*) &amp;gt;= 3
	and max(datum) &amp;lt; DATE_SUB(now(), INTERVAL 22 MONTH)
) tabb
on (taba.id_person = tabb.id_person
and tabb.letzteBestellung = taba.datum)</pre><p>Und dann brauche ich das nur noch mit den Adressen zu verbinden und habe das Ergebnis:</p><pre class="crayon-plain-tag">SELECT *
FROM (
	SELECT distinct taba.id_person, tabb.letzteBestellung, tabb.anzahlBestellungen, taba.rechnungsadresse
	FROM bestellung taba inner join
	(
		SELECT id_person, max(datum) as letzteBestellung, count(*) as anzahlBestellungen
		FROM (
			SELECT id_person, datum
			FROM bestellung
		) t1
		group by id_person
		having count(*) &amp;gt;= 3
		and max(datum) &amp;lt; DATE_SUB(now(), INTERVAL 22 MONTH)
	) tabb
	on (taba.id_person = tabb.id_person
	and tabb.letzteBestellung = taba.datum)
) ago inner join adresse
on ago.rechnungsadresse = adresse.id</pre><p>Die Bestelldaten sind auch für Menschen überschaubar. Peter bekommt keine Karte, weil er ein treuer Kunde ist und wir hoffen, dass er in diesem Jahr auch so wieder etwas bestellt. Paul bekommt keine Karte, weil er &#8211; nach unseren Masstäben &#8211; kein Interesse hat. Erika ergeht es wie Peter. Aber eine Karte in Erna zu investieren lohnt bestimmt!</p>
<p>Aufräumen nicht vergessen, weil das nur ein Bespiel ist.</p><pre class="crayon-plain-tag">drop table bestellung;
drop table adresse;</pre><p>Und das SQL ist so, dass wir es im kommenden Jahr wieder verwenden können. Der Serienbrief-Druck kann beginnen.</p>
<p>Über den Sinn und Unsinn einer solchen Weihnachtskarten-Aktion kann man trefflich streiten. Erfolgskontrolle wird die Marketing-Abteilung freuen: Wer von den angeschriebenen hat wieder etwas bei uns gekauft? Und warum? War die Karte der Auslöser oder die Trennung vom Partner oder &#8230; was?</p>
]]></content:encoded>
			<wfw:commentRss>http://aysx.de/eingroschen/?feed=rss2&#038;p=186</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>join vs subselect</title>
		<link>http://aysx.de/eingroschen/?p=175</link>
		<comments>http://aysx.de/eingroschen/?p=175#comments</comments>
		<pubDate>Tue, 19 Nov 2013 19:12:58 +0000</pubDate>
		<dc:creator><![CDATA[matthias]]></dc:creator>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[forschung]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[sql]]></category>

		<guid isPermaLink="false">http://aysx.de/eingroschen/?p=175</guid>
		<description><![CDATA[Vor einigen Jahren habe ich für den Mssql-Server herausgefunden, dass Abfragen mit &#8220;join&#8221; mehr als 100 mal schneller sein können als die gleich Abfrage in Form eines Subselects. Stimmt das auch heute für MySQL? Folgendes zeigt, dass es nicht zwingend &#8230; <a href="http://aysx.de/eingroschen/?p=175">Weiterlesen <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Vor einigen Jahren habe ich für den Mssql-Server herausgefunden, dass Abfragen mit &#8220;join&#8221; mehr als 100 mal schneller sein können als die gleich Abfrage in Form eines Subselects. Stimmt das auch heute für MySQL? Folgendes zeigt, dass es nicht zwingend so ist.</p>
<p>Frage: Welche Postleitzahlen gibt es sowohl in Deutschland als auch in den USA?</p>
<p>Stationen auf dem Weg zur Antwort:</p>
<li> Quelle von PLZ-dumps<br />
http://www.tutorials.de/relationale-datenbanksysteme/229059-postleitzahlen-mysql-dump.html<br />
=> Einspielen in die Tabelle &#8216;orte&#8217;.</p>
<li> Quelle US Zip-Codes<br />
http://federalgovernmentzipcodes.us/free-zipcode-database.csv<br />
aufbereiten mit<br />
cut -d &#8220;,&#8221; -f2,4 free-zipcode-database.csv &gt; free-zipcode-database-zip-city-only.csv<br />
Ergänzung für einen brauchbaren MySQL-Dump überlasse ich dem geneigten Publikum.<br />
=> Einspielen in die Tabelle &#8216;orteus&#8217;.</p>
<li> SQL-Befehle übersichtlich strukturiert. Sie werden unten als 1-Zeiler benutzt:
<p></p><pre class="crayon-plain-tag">-- join
--
SELECT count(*)
FROM (
&nbsp;&nbsp; &nbsp;SELECT distinct orte.PLZ FROM 
&nbsp;&nbsp; &nbsp;orte, orteus
&nbsp;&nbsp; &nbsp;where orte.PLZ = orteus.PLZ
) t1;

-- subselect
--
SELECT count(*)
FROM (
&nbsp;&nbsp; &nbsp;SELECT distinct orte.PLZ
&nbsp;&nbsp; &nbsp;FROM orte
&nbsp;&nbsp; &nbsp;where orte.PLZ in (
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;select orteus.PLZ FROM orteus
&nbsp;&nbsp; &nbsp;)
) t2;</pre><p></p>
<p>Copy&#038;Paste aus dem mysql-Client:</p>
<p></p><pre class="crayon-plain-tag">mysql&amp;gt; select count(*) from orte;
 +----------+
 | count(*) |
 +----------+
 |&nbsp;&nbsp;&nbsp; 44188 |
 +----------+
 1 row in set (0.00 sec)

mysql&amp;gt; select count(*) from orteus;
+----------+
| count(*) |
+----------+
|&nbsp;&nbsp;&nbsp; 81831 |
+----------+
1 row in set (0.00 sec)

mysql&amp;gt; SELECT count(*) FROM (SELECT distinct orte.PLZ FROM orte, orteus where orte.PLZ = orteus.PLZ) t1;
+----------+
| count(*) |
+----------+
|&nbsp;&nbsp;&nbsp; 13365 |
+----------+
1 row in set (0.16 sec)

mysql&amp;gt; SELECT count(*) FROM (SELECT distinct orte.PLZ FROM orte where orte.PLZ in (select orteus.PLZ FROM orteus)) t2;
+----------+
| count(*) |
+----------+
|&nbsp;&nbsp;&nbsp; 13365 |
+----------+
1 row in set (0.18 sec)</pre><p></p>
<p>Ergebnis: Für ca. 44000&#215;81000 ist fast kein Unterschied auszumachen.<br />
Ich bin angenehm überrascht. Einschränkung der Gültigkeit der Erkenntnis: Die Datenmenge im obigen Beispiel ist nicht gross. Damals bei Mssql ging es um ungefähr 12000&#215;625000, also Faktor 0,5 aber immerhin diesselbe Grössenordnung.</p>
]]></content:encoded>
			<wfw:commentRss>http://aysx.de/eingroschen/?feed=rss2&#038;p=175</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Beispiel Warenkorbanalyse in sql</title>
		<link>http://aysx.de/eingroschen/?p=83</link>
		<comments>http://aysx.de/eingroschen/?p=83#comments</comments>
		<pubDate>Sun, 13 Oct 2013 18:40:33 +0000</pubDate>
		<dc:creator><![CDATA[matthias]]></dc:creator>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[sql]]></category>

		<guid isPermaLink="false">http://aysx.de/eingroschen/?p=83</guid>
		<description><![CDATA[[crayon-69fca9cfbc35b661252794/]]]></description>
				<content:encoded><![CDATA[<p></p><pre class="crayon-plain-tag">use test

/*
	Warenkorb-Analyse
	=================
	
	Bei youtube fand ich ein Science-Slam-Video zu Wirtschafts-Informatik,
	bei dem das Stichwort &quot;Warenkorbanalyse&quot; fiel. Interessante Sache 
	das, also habe ich das Beispiel aus dem Video in MySQL nachgebaut.
*/

create table einkauf
(
	e_id int,
	prod_id int
);

create table produkt
(
	prod_id int,
	name varchar (255)
);

insert into produkt ( prod_id, name ) values ( 1 , 'Cola');
insert into produkt ( prod_id, name ) values ( 2 , 'Milch');
insert into produkt ( prod_id, name ) values ( 3 , 'Bier');
insert into produkt ( prod_id, name ) values ( 4 , 'Pizza');
insert into produkt ( prod_id, name ) values ( 5 , 'Pampers');

insert into einkauf ( e_id, prod_id ) values ( 1, 1 );
insert into einkauf ( e_id, prod_id ) values ( 1, 2 );
insert into einkauf ( e_id, prod_id ) values ( 1, 4 );
insert into einkauf ( e_id, prod_id ) values ( 2, 2 );
insert into einkauf ( e_id, prod_id ) values ( 2, 5 );
insert into einkauf ( e_id, prod_id ) values ( 3, 1 );
insert into einkauf ( e_id, prod_id ) values ( 3, 4 );
insert into einkauf ( e_id, prod_id ) values ( 4, 2 );
insert into einkauf ( e_id, prod_id ) values ( 4, 3 );
insert into einkauf ( e_id, prod_id ) values ( 5, 1 );
insert into einkauf ( e_id, prod_id ) values ( 5, 3 );

SELECT * FROM produkt;
SELECT * FROM einkauf;

/*
	Alle Produkt-Paare aus den Eink&auml;ufen herausfinden, d.h.
	f&uuml;r jeden Einkauf jedes Produkt mit allen anderen
	Produkten dieses Einkaufs kombinieren.
	NB: Wenn ein Produkt innerhalb eines Einkaufs mehrfach
	vorkommt, dann muss das vorher gruppiert werden. Ich gehe
	hier oBdA davon aus, dass jedes Produkt nur einmal in jedem
	Einkauf vorkommt.
*/

SELECT ek1.e_id, ek1.prod_id, ek2.prod_id
FROM einkauf ek1, einkauf ek2
where ek1.e_id = ek2.e_id;

/*
	An dieser Stelle spielt die Einkaufsnummer keine Rolle.
	Hauptsache, ich kenne alle Paarungen.
	Das sind die Paarungen, die vorkommen.
	Was ist mit denen, die nicht vorkommen?
*/

SELECT ek1.prod_id, ek2.prod_id
FROM einkauf ek1, einkauf ek2
where ek1.e_id = ek2.e_id;

/*
	So gruppieren, dass ich sehe, welches Paar wie oft
	vorkommt.
	Die Zeilen, bei denen ek1.prod_id = ek2.prod_id ist,
	geben an, wie oft das Produkt mit der ek1.prod_id
	insgesamt gekauft wurde. 
*/

SELECT ekl.prod_id as prodA, ekr.prod_id as prodB, count(ekl.prod_id) as anzahl
FROM einkauf ekl, einkauf ekr
where ekl.e_id = ekr.e_id
group by prodA, prodB;

/*
	Welche Kombinationen fehlen f&uuml;r das jeweilige Produkt?
	Nun, vergleiche die obige Tabelle mit dem kartesischen Produkt
	der Waren.
*/

SELECT * 
FROM (
	SELECT ekl.prod_id as prodA, ekr.prod_id as prodB, count(ekl.prod_id) as anzahl
	FROM einkauf ekl, einkauf ekr
	where ekl.e_id = ekr.e_id
	group by prodA, prodB
) tupels 
right outer join 
(
	select prl.prod_id as prodR, prr.prod_id as prodL
	from produkt prl, produkt prr
) alleWarenTupel
on tupels.prodA = alleWarenTupel.prodR
and tupels.prodB = alleWarenTupel.prodL;

/*
	Von obiger Tabelle zeige ich prodA und prodB nicht an,
	denn prodR und prodL sind bedeutungsgleich. Wichtig ist,
	dass ich f&uuml;r die jeweilige Paarung die Anzahl kenne.
*/

SELECT alleWarenTupel.prodL, alleWarenTupel.prodR, tupels.anzahl
FROM (
	SELECT ekl.prod_id as prodA, ekr.prod_id as prodB, count(ekl.prod_id) as anzahl
	FROM einkauf ekl, einkauf ekr
	where ekl.e_id = ekr.e_id
	group by prodA, prodB
) tupels 
right outer join 
(
	select prl.prod_id as prodR, prr.prod_id as prodL
	from produkt prl, produkt prr
) alleWarenTupel
on tupels.prodA = alleWarenTupel.prodR
and tupels.prodB = alleWarenTupel.prodL;

/*
	Wie bekomme ich eine h&uuml;bsche Tabelle?
	Ich h&auml;tte gerne die Spalte &quot;anzahl&quot; in F&uuml;nfer-Pakete aufgeteilt, so
	dass die ersten f&uuml;nf Werte hintereinander stehen, in der zweiten
	Zeile die zweiten f&uuml;nf Werte usw.
	F&uuml;r MSSQL scheint es unkompliziert, siehe
	http://www.brighthub.com/internet/web-development/articles/91895.aspx
	F&uuml;r MySQL ist es umst&auml;ndlich(er)
	http://www.onlamp.com/pub/a/onlamp/2003/12/04/crosstabs.html

	Schlie&szlig;lich ein Dank an
	http://en.wikibooks.org/wiki/MySQL/Pivot_table

	Wermutstropfen: Die Spaltennamen sind &quot;hardcodiert&quot;. Bei gro&szlig;em
	Produktumfang wird das eine Qual :/, also doch besser in eine 
	Tabellenkalkulation bringen.
*/

select prodL, ProdName,
sum(anzahl*(1-abs(sign(prodR-1)))) as 'Cola',
sum(anzahl*(1-abs(sign(prodR-2)))) as 'Milch',
sum(anzahl*(1-abs(sign(prodR-3)))) as 'Bier',
sum(anzahl*(1-abs(sign(prodR-4)))) as 'Pizza',
sum(anzahl*(1-abs(sign(prodR-5)))) as 'Pampers'
from (
	SELECT alleWarenTupel.prodL, alleWarenTupel.ProdName, alleWarenTupel.prodR, tupels.anzahl
		FROM (
		SELECT ekl.prod_id as prodA, ekr.prod_id as prodB, count(ekl.prod_id) as anzahl
		FROM einkauf ekl, einkauf ekr
		where ekl.e_id = ekr.e_id
		group by prodA, prodB
	) tupels 
	right outer join 
	(
		select prl.prod_id as prodR, prr.prod_id as prodL, prr.name as ProdName
		from produkt prl, produkt prr
	) alleWarenTupel
	on tupels.prodA = alleWarenTupel.prodR
	and tupels.prodB = alleWarenTupel.prodL
) pivot
group by prodL, ProdName;

select ProdName, Cola, Milch, Bier, Pizza, Pampers
from 
(
	select prodL, ProdName,
	sum(anzahl*(1-abs(sign(prodR-1)))) as 'Cola',
	sum(anzahl*(1-abs(sign(prodR-2)))) as 'Milch',
	sum(anzahl*(1-abs(sign(prodR-3)))) as 'Bier',
	sum(anzahl*(1-abs(sign(prodR-4)))) as 'Pizza',
	sum(anzahl*(1-abs(sign(prodR-5)))) as 'Pampers'
	from (
		SELECT alleWarenTupel.prodL, alleWarenTupel.ProdName, alleWarenTupel.prodR, tupels.anzahl
			FROM (
			SELECT ekl.prod_id as prodA, ekr.prod_id as prodB, count(ekl.prod_id) as anzahl
			FROM einkauf ekl, einkauf ekr
			where ekl.e_id = ekr.e_id
			group by prodA, prodB
		) tupels 
		right outer join 
		(
			select prl.prod_id as prodR, prr.prod_id as prodL, prr.name as ProdName
			from produkt prl, produkt prr
		) alleWarenTupel
		on tupels.prodA = alleWarenTupel.prodR
		and tupels.prodB = alleWarenTupel.prodL
	) pivot
	group by prodL, ProdName
) nice;

-- Aufr&auml;umen;
--
drop table einkauf;
drop table produkt;

-- Ende.</pre><p></p>
]]></content:encoded>
			<wfw:commentRss>http://aysx.de/eingroschen/?feed=rss2&#038;p=83</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Rang in sql</title>
		<link>http://aysx.de/eingroschen/?p=53</link>
		<comments>http://aysx.de/eingroschen/?p=53#comments</comments>
		<pubDate>Sun, 06 Oct 2013 18:44:53 +0000</pubDate>
		<dc:creator><![CDATA[matthias]]></dc:creator>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[rang]]></category>
		<category><![CDATA[sql]]></category>

		<guid isPermaLink="false">http://aysx.de/eingroschen/?p=53</guid>
		<description><![CDATA[[crayon-69fca9cfbc87e696824857/] Natürlich ist das obige Beispiel so in der Praxis nicht vorgekommen. Das Beispiel zeigt eine nützliche Struktur. Im Laufe der Zeit kam immer wieder die Anforderung der Durchnummerierung. Man braucht dazu keinen PK-Constraint, aber wenigstens irgendein Tupel, das unique &#8230; <a href="http://aysx.de/eingroschen/?p=53">Weiterlesen <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p></p><pre class="crayon-plain-tag">/*
	Nummeriere die Datens&auml;tze
	*************************

	Gegeben sei eine Tabelle aysx mit einem Primary Key. 
        Die Werte f&uuml;r den PK m&uuml;ssen nicht fortlaufend sein. 

        Aufgabe: Nummeriere die Datens&auml;tze 
	vom kleinsten bis zum gr&ouml;&szlig;sten PK-Wert.

	Das folgende MySQL-Script zeigt exemplarisch, wie 
        das machbar ist. Bedenke, dass die Bestimmung des 
        Rangs (gidf) nicht auf eine &quot;echte&quot; Tabelle beschr&auml;nkt 
        ist, sondern auch mit einem View und dem Ergebnis
	einer Abfrage durchgef&uuml;hrt werden kann.

	Beispieltext: aysx Matthias Kopp 2013
*/

create table aysx ( 
	id int PRIMARY KEY, 
	remark varchar ( 64 ));

/*
	Tabelle mit Beispieldaten f&uuml;llen.
*/

insert into aysx ( id, remark ) values ( 1,	'Eins' );
insert into aysx ( id, remark ) values ( 512,	'FiveOneTwo' );
insert into aysx ( id, remark ) values ( 21,	'Antv' );
insert into aysx ( id, remark ) values ( 42,	'Antwort' );
insert into aysx ( id, remark ) values ( 23,	'whatever' );

/*
	Wesentlich ist der self-join. Der PK-Constraint garantiert,
	dass jede aysx.id nur einmal vorkommt. Eine id ist immer
	gleich ihrer selbst und alle anderen sind entweder gr&ouml;&szlig;er
	oder kleiner. Die where-Bedingung sucht diejenigen Datens&auml;tze
	zusammen, die kleiner-gleich einer id sind - und das f&uuml;r
	alle ids. Schlie&szlig;lich wird f&uuml;r jede id eine Gruppe gebildet
	und die Anzahl der Elemente in der Gruppe ist eben der Rang.
*/

select count(*) as rang, t2.id
from aysx t1, aysx t2
where t1.id &amp;lt;= t2.id
group by t2.id;

/*
	Ohne Gruppierung und Z&auml;hlen - die &quot;expandierte&quot; Form.
	Hier kann man nachvollziehen, welche Elemente vorhanden sind.
	Z&auml;hle und gruppiere selbst!
*/

select t1.id, t2.id
from aysx t1, aysx t2
where t1.id &amp;lt;= t2.id
order by t2.id

/* Aur&auml;umen. */
drop table aysx;</pre><p>Natürlich ist das obige Beispiel so in der Praxis nicht vorgekommen. Das Beispiel zeigt eine nützliche Struktur. Im Laufe der Zeit kam immer wieder die Anforderung der Durchnummerierung. Man braucht dazu keinen PK-Constraint, aber wenigstens irgendein Tupel, das unique ist &#8211; und das findet sich. Jedenfalls konnte ich immer irgendwo eins auftreiben :)</p>
<p>Das Script oben habe ich mit Copy&amp;Paste aus <a title="SQuirrel SQL" href="http://www.squirrelsql.org/" target="_blank">SQuirreL SQL</a> übernommen, während ich mit einer<a title="MySQL" href="http://www.mysql.com/" target="_blank"> MySQL-Datenbank</a> verbunden war. Kann sein, dass andere RDBMS eine leicht abweichende Syntax haben. Aber im Prinzip sollte dieses Script auf gängigen SQL-Systemen lauffähig sein.</p>
]]></content:encoded>
			<wfw:commentRss>http://aysx.de/eingroschen/?feed=rss2&#038;p=53</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Daten konsistent halten</title>
		<link>http://aysx.de/eingroschen/?p=49</link>
		<comments>http://aysx.de/eingroschen/?p=49#comments</comments>
		<pubDate>Sun, 06 Oct 2013 16:06:55 +0000</pubDate>
		<dc:creator><![CDATA[matthias]]></dc:creator>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[foreign key]]></category>
		<category><![CDATA[Konsistenz]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[sql]]></category>

		<guid isPermaLink="false">http://aysx.de/eingroschen/?p=49</guid>
		<description><![CDATA[MySQL und andere RDBMS bieten die Möglichkeit, Foreign-Key-Constraints auszuschalten. Ich halte das für eine schlechte Idee. Ich kenne aus der Praxis ein Szenario, bei dem FK-Constraints ausgeschaltet wurden, um den Import von Daten zu ermöglichen. Die Daten lagen in unterschiedlichen &#8230; <a href="http://aysx.de/eingroschen/?p=49">Weiterlesen <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>MySQL und andere RDBMS bieten die Möglichkeit, Foreign-Key-Constraints auszuschalten. Ich halte das für eine schlechte Idee.</p>
<p>Ich kenne aus der Praxis ein Szenario, bei dem FK-Constraints ausgeschaltet wurden, um den Import von Daten zu ermöglichen. Die Daten lagen in unterschiedlichen Tabellen, verknüpft durch Foreign Keys. Man kriegt die einen Daten nicht &#8216;rein, wenn die anderen Daten nicht vorhanden sind. Bei einem System mit vielen Tabellen und Verbindungen wird das schnell zu einer &#8220;Plage&#8221;, weil man sich über die Reihenfolge bei Export und Import Gedanken machen muss. Vielleicht sind in dem exportierten System bestimmte Datenstrukturen gar nicht vorhanden, weil das einen anderen Versionsstand hat als das System, in das hinein importiert wird. Es ist verlockend einfach, die Contraints auszuschalten, die Daten reinzupumpen und zu hoffen, dass alles gut geht. RDBMS mit einer strikten Vorgehensweise, Beispiel Oracle, erlauben es nicht, Constraints einzuschalten, solange inkonsistente Daten vorliegen. Und dann hat man zwei Probleme, nämlich inkonsistente Daten und fehlende Contraints, die wiederum zu weiteren Inkonsistenzen führen können. Wohl dem Kunden, der einen Supportvertrag hat :(</p>
<p>Foreign-Key-Contraints auszuschalten heisst, schnell und dreckig zu arbeiten. Manchmal wird einen der Arbeitsdruck dazu verleiten &#8211; es muss ein Weg gefunden werden und zwar flott. Ich kenne das. Und ich weiss, dass das Aufräumen inkonsistenter Daten mächtig viel Zeit in Anspruch nehmen kann. Foreign-Key-Contraints-ausschalten als Notnagel: Ja. Als Dauerlösung im Code: Nein!</p>
]]></content:encoded>
			<wfw:commentRss>http://aysx.de/eingroschen/?feed=rss2&#038;p=49</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
