Keine Ausgabe - komplizierte Abfrage
Bernd
- php
- sql
0 dedlfix0 Rolf B0 Bernd0 Tabellenkalk0 Rolf B
Hallo,
ich habe ein wenig Probleme mit meinem Script. Wenn ich dieses in phpMyAdmin ausführe erhalte ich keine Einträge, obwohl laut Datenbank welche vorhanden sein müssen und zwar für den heutigen Tag 5
SELECT
kp_id, kp_code, kp_userID, kp_status, kt_kalenderID, kt_datum, k_code, k_jobNr,
k_bezeichnung, ka_code, ka_titel, ka_farbe
FROM kalender_personal
LEFT JOIN
kalender_termine ON kalender_termine.kt_datum = kalender_personal.kp_code
LEFT JOIN
kalender ON kalender.k_code = kalender_personal.kp_code
LEFT JOIN
kalender_arten ON kalender_arten.ka_code = kalender_personal.kp_code
WHERE
kt_datum = '2019-05-26'
ORDER by test ASC
Tach!
ich habe ein wenig Probleme mit meinem Script. Wenn ich dieses in phpMyAdmin ausführe erhalte ich keine Einträge, obwohl laut Datenbank welche vorhanden sein müssen und zwar für den heutigen Tag 5
Ohne die Tabellenstrukturen zu kennen und ohne Testdaten kann ich nur Allgemeines zur Vorgehensweise sagen. Lass zuerst mal das WHERE weg und schau dir an, was überhaupt für eine Datenmenge erzeugt wird. Wenn das keine Erkenntnisse bringt, bau die Query schrittweise auf. Ein Join nach dem anderen hinzufügen und immer die erzeugte Ergebnismenge anschauen.
dedlfix.
Hallo,
danke für deine Antwort. Wenn ich das WHERE weg lasse erhalte ich knapp 1.300 Einträge. Kann stimmen. Was mir jetzt auffällt ist, im Feld kt_kalenderID, kt_datum, ka_code, ka_titel und ka_farbe steht NULL.
Zum Verständnis, ich möchte alle User auslesen, die für ein bestimmtes Datum eingetragen sind.
Die Tabelle sehen wie folgt aus
CREATE TABLE IF NOT EXISTS `kalender_personal` (
`kp_id` int(11) NOT NULL AUTO_INCREMENT,
`kp_code` varchar(100) NOT NULL,
`kp_userID` varchar(100) NOT NULL,
`kp_positionID` varchar(100) NOT NULL,
`kp_status` varchar(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`kp_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1805 ;
CREATE TABLE IF NOT EXISTS `kalender_termine` (
`kt_id` int(11) NOT NULL AUTO_INCREMENT,
`kt_kalenderID` varchar(200) NOT NULL,
`kt_datum` date NOT NULL,
`test` int(10) NOT NULL,
PRIMARY KEY (`kt_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=12261 ;
CREATE TABLE IF NOT EXISTS `kalender` (
`k_id` int(11) NOT NULL AUTO_INCREMENT,
`k_code` varchar(200) NOT NULL,
`k_art` varchar(200) NOT NULL,
`k_jobNr` varchar(100) NOT NULL,
`k_bezeichnung` varchar(200) NOT NULL,
`k_grund` text NOT NULL,
`k_farbe` varchar(200) NOT NULL,
`k_datum_von` date NOT NULL,
`k_datum_bis` date NOT NULL,
PRIMARY KEY (`k_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1136 ;
CREATE TABLE IF NOT EXISTS `kalender_arten` (
`ka_id` int(11) NOT NULL AUTO_INCREMENT,
`ka_code` varchar(200) NOT NULL,
`ka_titel` varchar(200) NOT NULL,
`ka_farbe` varchar(200) NOT NULL,
PRIMARY KEY (`ka_id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=10 ;
Tach!
danke für deine Antwort. Wenn ich das WHERE weg lasse erhalte ich knapp 1.300 Einträge. Kann stimmen. Was mir jetzt auffällt ist, im Feld kt_kalenderID, kt_datum, ka_code, ka_titel und ka_farbe steht NULL.
Warum bei einem Left Join NULL entstehen kann, wenn kein NULL in den Daten steht, ist Basiswissen.
dedlfix.
OK, ich habe die Abfrage etwas umgebaut
SELECT
kt_kalenderID, kt_datum, kp_id, kp_code, kp_userID, k_code, k_jobNr, k_bezeichnung,
per_anrede, per_name, per_vorname
FROM kalender_termine
LEFT JOIN
kalender ON kalender.k_code = kalender_termine.kt_kalenderID
LEFT JOIN
kalender_personal ON kalender_personal.kp_code = kalender_termine.kt_kalenderID
LEFT JOIN
personal ON personal.per_code = kalender_personal.kp_userID
WHERE
kt_datum = '2019-05-26'
AND kp_userID != ""
ORDER by test ASC
Jetzt erhalte ich auch eine Ausgabe
Zeige Datensätze 0 - 10 ( 11 insgesamt, Die Abfrage dauerte 0.0035 Sekunden)
Scheinbar muss ich eine ganz bestimmte Reihenfolge einhalten wenn ich mit einem Datum arbeite?
Tach!
Scheinbar muss ich eine ganz bestimmte Reihenfolge einhalten wenn ich mit einem Datum arbeite?
Das hat nichts mit bestimmten Datentypen zu tun, sondern mit Mengenlehre. Wenn du zwei Mengen miteinander verbindest und dabei entsteht kein Ergebnis, kann auch keins mehr entstehen, wenn du noch eine Menge hinzufügst, die keinen Anknüpfungspunkt in der leeren Menge finden kann.
Deswegen empfahl ich ja das schrittweise Vorgehen, damit du siehst, was da jeweils entsteht.
dedlfix.
Wenn Du NULL kriegst, kann das auch daran liegen daß Dein JOIN falschherumrum (seitenverkehrt) ist. Also mach entweder einen right Join oder nimm die Tabelle kalender_termine als Basistabelle, denn hier ist das feld kt_datum.
SELECT .. FROM kalender_termine
JOIN kalender_arten usw.
WHERE kt_datum = '..'
Und zur Optimierung lege einen Index auf kt_datum. Bei Deinen paar Daten ist er noch nicht nötig, aber das wird noch kommen. MFG
Und arbeite mit aussagekräftigen Alias'n bei Deinen Abfragen!
Hallo,
SELECT .. FROM kalender_termine JOIN kalender_arten usw. WHERE kt_datum = '..'
das habe ich gestern bereits gemacht:
https://forum.selfhtml.org/self/2019/may/26/keine-ausgabe-komplizierte-abfrage/1749400#m1749400
Ich dachte bis zu diesem Zeitpunkt immer, die Reihenfolge spielt keine Rolle.
Und arbeite mit aussagekräftigen Alias'n bei Deinen Abfragen!
Du meinst mit AS? Ich finde persönlich es sehr umständlich jede Spalte umzubenennen.
Hallo,
SELECT .. FROM kalender_termine JOIN kalender_arten usw. WHERE kt_datum = '..'
das habe ich gestern bereits gemacht:
https://forum.selfhtml.org/self/2019/may/26/keine-ausgabe-komplizierte-abfrage/1749400#m1749400Ich dachte bis zu diesem Zeitpunkt immer, die Reihenfolge spielt keine Rolle.
Die Reihenfolge bestimmt ja ob es ein LEFT oder RIGHT Join wird.
Und arbeite mit aussagekräftigen Alias'n bei Deinen Abfragen!
Du meinst mit AS? Ich finde persönlich es sehr umständlich jede Spalte umzubenennen.
Ja, AS bzw. Alias. Es geht ja nicht um das Umbebennen schlechthin sondern um die Weiterverarbeitung! So kann man z.B. die Aliase auf die Schlüssel einer Templating Engine anpassen und dies auch umgekehrt. Wenn das Template bereits steht und der Kollege sagt da muss Vorname
rein dann wird einfach nur ein Alias entsprechend gesetzt und nicht das ganze Template geändert. MFG
Tach!
SELECT .. FROM kalender_termine JOIN kalender_arten usw. WHERE kt_datum = '..'
das habe ich gestern bereits gemacht:
https://forum.selfhtml.org/self/2019/may/26/keine-ausgabe-komplizierte-abfrage/1749400#m1749400Ich dachte bis zu diesem Zeitpunkt immer, die Reihenfolge spielt keine Rolle.
Die Reihenfolge bestimmt ja ob es ein LEFT oder RIGHT Join wird.
Das ist nach wie vor nicht richtig. Ein Outer Join, ob Left oder Right, benötigt LEFT oder RIGHT als Schlüsselwort, sonst ist es ein Inner Join. Lediglich das Schlüsselwort OUTER kann weggelassen werden.
Dass es dann immer noch darauf ankommt, was links vom LEFT/RIGHT OUTER JOIN und was rechts steht, ist unbestritten. Das sollte ja auch anhand des Verhaltens klar sein, dass eine davon die Haupttabelle sein muss, deren Datensätze immer in die Ergebnismenge kommen, und die Felder der anderen bei Abwesenheit eines Datensatzes mit Verknüpfungsbedingung zu NULL werden können. Nur das Schlüsselwort LEFT/RIGHT kann nicht wegelassen werden.
Und arbeite mit aussagekräftigen Alias'n bei Deinen Abfragen!
Du meinst mit AS? Ich finde persönlich es sehr umständlich jede Spalte umzubenennen.
Das ist auch nicht notwendig. Wenn du mit deiner Benennung zufrieden bist und du keinen Grund für eine Alias siehst oder es keinen technischen Zwang gibt (gibt es in dem Fall nicht), kannst du das problemlos so lassen.
Ja, AS bzw. Alias. Es geht ja nicht um das Umbebennen schlechthin sondern um die Weiterverarbeitung! So kann man z.B. die Aliase auf die Schlüssel einer Templating Engine anpassen und dies auch umgekehrt.
Das kann man auch mit den derzeitigen Bezeichnern.
Wenn das Template bereits steht und der Kollege sagt da muss
Vorname
rein dann wird einfach nur ein Alias entsprechend gesetzt und nicht das ganze Template geändert.
Ja, wenn dann. Es gibt immer Gründe für irgendeine bestimmte Maßnahmen. Nur es als unabdingbar hinzustellen, ist nicht notwendig.
dedlfix.
PS: Der Alias ist ein wichtiges Instrument zur Trennung von Datenhaltung und Anwendung. Durch eine solche Abstrahierung wird der Datenhaltungslayer transparenter und alles zusammen wartungsfreundlicher. D.h. daß Du per Alias besser mit etwaigen Veränderungen/Skalierungen zurechtkommst, weil tabname.colname
eben nicht namentlich in die Anwendung verschleppt wird und wenn da eine Änderung eforderlich wird, bleibt der Alias davon unberührt.
Darüber hinaus liefern Funktionen z.B. so etwas
mysql> select count(url) from log;
+------------+
| count(url) |
+------------+
| 1997 |
+------------+
mysql> select count(url) as cnt from log;
+------+
| cnt |
+------+
| 1997 |
+------+
was die Zweckmäßigkeit eines ALias selbst erklärt. Insgesamt ist die Vergabe von Alias'n eine praktische Angelegenheit die sich in vielen Jahren meiner Programmiertätigkeit immer wieder als sehr nützlich erwiesen hat. MFG
Tach!
PS: Der Alias ist ein wichtiges Instrument zur Trennung von Datenhaltung und Anwendung. Durch eine solche Abstrahierung wird der Datenhaltungslayer transparenter und alles zusammen wartungsfreundlicher. D.h. daß Du per Alias besser mit etwaigen Veränderungen/Skalierungen zurechtkommst, weil
tabname.colname
eben nicht namentlich in die Anwendung verschleppt wird und wenn da eine Änderung eforderlich wird, bleibt der Alias davon unberührt.
Naja. Üblicherweise steht das SQL-Statement im Code direkt vor der Abfrageroutine. Ob man einen Namenswechsel - falls man ihn benötigt - nun per Alias oder im nachfolgenden Code vornimmt, nimmt sich vom Aufwand her nicht viel. Langname und gegebenenfalls Alias stehen also bereits in der Anwendung, weil das Statement darin notiert ist. Welchen Namen man im weiteren Verlauf nehmen möchte, ist eine Frage der Vorliebe. Wenn sich der Feldname in Zukunft ändert, kann man immer noch einen Alias hinzufügen oder ein Umschreiben nach der Abfrage vornehmen. Beides liegt nahe beieinander. Ich sehe die Sache mit dem Alias weniger dramatisch, als du sie hier darstellst. Man kann, aber man muss nicht.
Pseudocode:
sql = "SELECT p_name AS name FROM person"
query(sql)
records = []
while ...
records[] = fetch_array
end
return records
sql = "SELECT p_name FROM person"
query(sql)
while ...
record = fetch_array
records[] = ["name" => record["p_name"]]
end
return records
Ja, man spart sich das anschließende Umschreiben, vor allem wenn man es nur wegen der Namensänderung und nicht noch aus anderen Gründen macht. Auf der anderen Seite steht nur ein etwas längeres SQL-Statement. Aber wenn man mit seinem p_name völlig zufrieden ist, braucht man weder Alias noch eine Nachbehandlung, und hat trotzdem eine offene Zukunft mit einer der beiden Varianten.
dedlfix.
Ich frag mich immer wieder warum PHP es immer noch nicht geschafft hat, korrespondierende Klammern als Stringbegrenzer einzuführen die 1. ermöglichen SQL Statements mehrzeilig und übersichtlich zu schreiben und die 2. jeder gängige Editor als zusammengehörig erkennt.
Hallo pl,
Nr. 1 löst PHP mit der HEREDOC/NOWDOC Syntax. Sicher, es gibt schöneres. Aber das Feature "mehrzeilige Strings" ist grundsätzlich da.
Nr. 2 ist ein leichtes Problem, man muss dann schon einen EDITOR verwenden, der PHP-Syntax kennt. Das kann nicht jeder, aber viele.
Rolf
Tach!
Ich frag mich immer wieder warum PHP es immer noch nicht geschafft hat, korrespondierende Klammern als Stringbegrenzer einzuführen die 1. ermöglichen SQL Statements mehrzeilig und übersichtlich zu schreiben und die 2. jeder gängige Editor als zusammengehörig erkennt.
In welchen wichtigen Sprachen werden denn Klammern als Stringbegrenzer verwendet? PHP hat für diesen Zweck die Heredoc- und Nowdoc-Syntax, die auch anderenorts bekannt ist.
$text = <<<END
Zeile 1
Zeile 2
END;
was_anderes():
Kann Notepad++ genau wie Syntaxhervorhebung des Forums problemlos erkennen. Damit sollte auch jeder andere Editor zurechtkommen, der Bash-Unterstützung an Bord hat.
dedlfix.
In welchen wichtigen Sprachen werden denn Klammern als Stringbegrenzer verwendet?
in Perl:
my $q = q(
SELECT
a.name as Lastname,
a.vname as Firstname,
k.name as Konfession
FROM
address a
JOIN
konfession k USING(id)
WHERE
k.name = "Konfessionslos"
ORDER by
a.name
);
Und je nach Geschmack kann man auch [] oder {} als korrespondierende Klammern verwenden, die jeder Editor zuordnen kann, gerade bei längeren Geschichten.
MFG
Tach!
In welchen wichtigen Sprachen werden denn Klammern als Stringbegrenzer verwendet?
in Perl:
Ich frage nach wichtigen Sprachen. 😛
Und je nach Geschmack kann man auch [] oder {} als korrespondierende Klammern verwenden, die jeder Editor zuordnen kann, gerade bei längeren Geschichten.
Ach, und was ist, wenn nicht nur der Editor sondern auch Perl selbst bei kürzeren Geschichten schon aus dem Tritt kommen?
my $q = q(foo ) bar);
Jedenfalls hat PHP (und andere Sprachen) sowohl mit Anführungszeichen als auch mit Herdoc/Nowdoc die Möglichkeit, Strings übersichtlich und mehrzeilig zu erstellen.
dedlfix.
hi,
Ach, und was ist, wenn nicht nur der Editor sondern auch Perl selbst bei kürzeren Geschichten schon aus dem Tritt kommen?
my $q = q(foo ) bar);
Wenn hier jemand aus dem Tritt kommt ist das nicht Perl sondern der Programmierer der den Kontext nicht beachtet. my $q = q[foo ) bar];
wäre eine Variante dem gerecht zu werden. Der q-Operator erlaubt es schließlich, die Stringbegrenzerzeichen selbst festzulegen, so daß man auf zusätzliche Maskierungen verzichten kann.
MFG
Hallo pl,
in Perl:
my $q = q( SELECT a.name as Lastname, a.vname as Firstname, k.name as Konfession FROM address a JOIN konfession k USING(id) WHERE k.name = "Konfessionslos" ORDER by a.name );
in PHP:
$q = "
SELECT
a.name as Lastname,
a.vname as Firstname,
k.name as Konfession
FROM
address a
LEFT JOIN
konfession k USING(id)
WHERE
k.name = 'Konfessionslos'
ORDER by
a.name
";
Bis demnächst
Matthias
Hallo Matthias,
äh, nö? Zumindest bei mir nicht.
Update.
äh doch. Habe die " nicht in ' konvertiert 😳
Aber das zeigt den Vorteil von heredoc/nowdoc: Man kann mit Anführungszeichen um sich schmeißen wie man will.
Rolf
Hallo pl,
$this->q = <<<SQL
SELECT
a.name as Lastname,
a.vname as Firstname,
k.name as Konfession
FROM
address a
JOIN
konfession k USING(id)
WHERE
k.name = "Konfessionslos"
ORDER by
a.name
SQL;
geht doch. Ab PHP 7.3 muss das End-SQL auch nicht mehr zwingend auf Stelle 1 stehen. Rücke ich es 4 Stellen ein, werden im heredoc-String pro Zeile die ersten 4 Stellen entfernt.
$q = <<<SQL
SELECT *
FROM foo
ORDER BY bar
SQL;
echo $q;
ergibt
SELECT *
FROM foo
ORDER BY bar
Rolf
hi @Rolf B
Das kannste mit Perl auch alles so machen. Macht aber keiner. Das macht höchstens jemand, der den Sinn des q-Operators nicht verstanden hat. Wenn wir schon programmieren dann mit Stil, so muss das aussehen.
Und dann wäre noch ein nettes Feature was Perl bietet:
# hier steht Code
# Zugriff auf die Texte unterhalb des DATA-Token
read(DATA, my $text, -s DATA);
__DATA__
Hier stehen SQL Statments, Texte, Templates usw.
Womit man Texte/Templates auch hervorragend vom Code trennen kann. MFG
In welchen wichtigen Sprachen werden denn Klammern als Stringbegrenzer verwendet?
in Perl:
my $q = q( SELECT a.name as Lastname, a.vname as Firstname, k.name as Konfession FROM address a JOIN konfession k USING(id) WHERE k.name = "Konfessionslos" ORDER by a.name );
Und je nach Geschmack kann man auch [] oder {} als korrespondierende Klammern verwenden, die jeder Editor zuordnen kann, gerade bei längeren Geschichten.
MFG
PS: Das Template mit Mustache sähe dann so aus:
{{#names}}
<tr>
<td> {{Lastname}} </td>
<td> {{Firstname}} </td>
<td> {{Konfession}} </td>
</tr>
{{/names}}
(Table Body) Und die dazugehörige Datenstruktur mit der das gerendert wird, liefert eine einzige Anweisung
my $names = $dbh->selectall_arrayref($q, {Slice=>{}});
Template::Mustache->render( $tbody, { names => $names} );
Von soviel Komfort können PHP Entwickler gar nicht träumen weil sie sowas noch nie gesehen, geschweige denn angewandt haben 😉
MFG
my $names = $dbh->selectall_arrayref($q, {Slice=>{}}); Template::Mustache->render( $tbody, { names => $names} );
Von soviel Komfort können PHP Entwickler gar nicht träumen weil sie sowas noch nie gesehen, geschweige denn angewandt haben 😉
In PHP sähe das etwas kürzer aus:
$names = $statement->fetchAll();
echo (new Mustache_Engine)->render($tbody, ['names' => $names]);
Hallo Bernd,
ergänzend zu Dedlfix:
Welchen Datentyp hat die Spalte kt_datum? Vielleicht hast Du das früher mal geschrieben, aber das merken wir uns nicht unbedingt 😀
Die LEFT JOINs reduzieren nicht die Ergebnismenge und können darum nicht an den 0 Treffern schuld sein. Es muss der WHERE sein, und ich würde mal vermuten: kt_datum ist ein DATETIME-Wert. In dem Fall musst Du einen Bereich abfragen: kt_datum >= '2019-05-26' AND kt_datum < '2019-05-27'
Das ist natürlich blöd, wenn das abzufragende Datum aus einer Variablen kommen soll. Was Du aber keinesfalls tun solltest, ist dies:
SELECT kp_id, kp_code, kp_userID, kp_status, kt_kalenderID, kt_datum, ...
FROM kalender_personal
LEFT JOIN ...
LEFT JOIN ...
LEFT JOIN ...
WHERE DATE(kt_datum) = '2019-05-26'
Das wird zwar funktionieren, ist aber ineffizient, weil MYSQL dann einen Table Scan macht. Bestenfalls einen Index-Scan (wenn kt_datum im Index ist).
Besser ist es, in PHP das Datum des Folgetages zu bestimmen und damit die Bereichsabfrage zu bauen.
Rolf
Hallo,
kt_datum ist ein date Feld. Das Datum wird 2019-05-26 gespeichert. Die Strukturen meiner Tabellen habe ich hier gezeigt:
https://forum.selfhtml.org/self/2019/may/26/keine-ausgabe-komplizierte-abfrage/1749394#m1749394
Hallo,
kt_datum ist ein date Feld.
Die Frage die sich mir hier stellt: lässt sich ein date-Feld (kt_datum) mit einem varchar-Feld (kp_code) joinen? Vermutlich ist die interne Darstellung eine andere und daher unmöglich…
Gruß
Kalk
Tach!
Die Frage die sich mir hier stellt: lässt sich ein date-Feld (kt_datum) mit einem varchar-Feld (kp_code) joinen? Vermutlich ist die interne Darstellung eine andere und daher unmöglich…
Das ist ein simpler Vergleich, bei dem bei Bedarf eine Typkonvertierung stattfindet. Datumsangaben werden üblicherweise als Stringliteral geschrieben und da gibt es auch kein Problem, so ein Stringliteral mit einem Datum zu vergleichen, wenn das Stringliteral syntaktisch richtig wie ein Datum geschrieben ist.
dedlfix.
Hallo Bernd,
mir fällt auf, dass deine umgebaute Query anders joined als die Originalquery.
alt: kalender_termine.kt_datum = kalender_personal.kp_code
neu: kalender_personal.kp_code = kalender_termine.kt_kalenderID
Das wird wohl der Hauptgrund dafür sein, dass Du auf einmal Treffer bekommst.
Generell solltest Du noch einen EXPLAIN auf deine neue Query machen und Dir merken. Und dann solltest Du
Also so:
SELECT
kt_kalenderID, kt_datum, kp_id, kp_code, kp_userID, k_code, k_jobNr, k_bezeichnung,
per_anrede, per_name, per_vorname
FROM kalender_termine
JOIN
kalender_personal ON kalender_personal.kp_code = kalender_termine.kt_kalenderID
LEFT JOIN
kalender ON kalender.k_code = kalender_termine.kt_kalenderID
LEFT JOIN
personal ON personal.per_code = kalender_personal.kp_userID
WHERE
kt_datum = '2019-05-26'
AND kp_userID != ""
ORDER by test ASC
Du kannst Dich dann auch noch fragen, ob die Beziehung kalender_termine -> kalender tatsächlich optional ist (sprich: ob es kt_kalenderIDs geben kann, zu denen Du keinen k_code findest). Die gleiche Frage wäre für die Beziehung kalender_personal -> personal zu stellen. Wenn die Namen deiner Tabellen gut gewählt sind, würde ich annehmen, dass diese Beziehungen obligatorisch sind. Und dann kann das LEFT da weg.
Nachdem Du unnötige LEFT entfernt hast, solltest Du nochmal einen EXPLAIN machen und gucken ob der gewählte Zugriff besser geworden ist.
Ach ja, natürlich sollten die Spalten kp_code, k_code und per_code in einem Index zu finden sein, damit die JOINs nicht zu Table- oder Index-Scans führen. Aber das weißt Du sicherlich schon.
Rolf