Wie löse ich mehrdeutige Spaltennamen beim Abrufen von Ergebnissen auf?

74

Ich habe zwei Tabellen in meiner Datenbank:

NEWS-Tabelle mit Spalten:

  • id - die Nachrichten-ID
  • user - die Benutzer-ID des Autors)

USERS-Tabelle mit Spalten:

  • id - die Benutzer-ID

Ich möchte diese SQL ausführen:

SELECT * FROM news JOIN users ON news.user = user.id 

Wenn ich die Ergebnisse in PHP erhalte, möchte ich ein assoziatives Array und Spaltennamen von erhalten $row['column-name']. Wie erhalte ich die Nachrichten-ID und die Benutzer-ID mit demselben Spaltennamen?

Dan Burzo
quelle

Antworten:

117

Sie können Aliase für die ausgewählten Spalten festlegen:

$query = 'SELECT news.id AS newsId, user.id AS userId, [OTHER FIELDS HERE] FROM news JOIN users ON news.user = user.id'
Christian C. Salvadó
quelle
1
Diese Antwort hat die Antworten auf stackoverflow.com/questions/9233387/sql-should-i-use-a-join für mich funktionieren lassen, danke!
AVProgrammer
3
Der Hauptnachteil dieses Ansatzes besteht darin, dass SELECT *die Abfrage im Gegensatz dazu eng an die Tabellenstruktur gekoppelt ist. Das heißt, wenn Sie Spalten im Schema hinzufügen / löschen / umbenennen, müssen Sie Ihre Abfrage entsprechend ändern.
Ron Inbar
4
@ RonInbar. SELECT *ist ein Wartungsalptraum in großen Systemen. Sie schlagen vor, dass das Hinzufügen einer Spalte einfach ist und bei Verwendung keine Änderung an SQL erfordert. SELECT *Dies ist jedoch nicht der Fall, wie bei dem hier identifizierten Aliasing-Problem. Es kann auch unnötige Auswirkungen auf die Leistung haben, wenn das neue Feld groß ist. Es kann auch clientseitige Steuerelemente beschädigen, die eine bestimmte Anzahl oder Felder erwarten. Ich kann weitermachen, aber die Vorteile SELECT *verschwinden sehr schnell, wenn Sie die Auswirkungen verstehen, die sie auf die Wartbarkeit und Leistung haben.
simo.3792
1
@ simo.379209 Ich würde eher sagen, dass select * CAN ein Albtraum sein kann. Es war in unserem letzten. In unserem derzeitigen System ist alles von Grund auf so aufgebaut, dass erweiterbare Datenstrukturen verwendet werden. Schecks haben die Form "Sind diese Daten vorhanden?" Nicht "Sind diese Daten vorhanden und nicht mehr vorhanden?". Wir sind dabei, einen Chaosaffen einzusetzen, der Spaltenreihenfolgen zufällig sortiert und neue Spalten hinzufügt. Wir haben viel davon profitiert, dass wir Funktionen schneller und mit weniger Brüchen entwickeln konnten. Ob select * schlecht ist, hängt auch in sehr großen Systemen vollständig vom Kontext ab.
Max Murphy
Es scheint, als ob es eine Option geben select table.idsollte, bei der die Spalte zurückgegeben werden sollte table.id. Scheint eine einfache Funktion zu sein, die Aliasing verhindern würde. In PHP wird es ein bisschen mühsam sein, die select-Anweisung zu aliasen.
Kolob Canyon
42

Sie können entweder die numerischen Indizes ( $row[0]) verwenden oder besser ASin MySQL verwenden:

SELECT *, user.id AS user_id FROM ...

Greg
quelle
13
Beachten Sie auch, dass wenn Sie wildwarcd verwenden *, es das erste in der select-Klausel sein muss. NICHT:SELECT user_id,* ...
Anders
2
Stimmt, aber Sie können es immer tun, SELECT user.id AS user_id, user.* FROM ...wenn die Reihenfolge wichtig ist.
Cyrille
23

Ich habe das gerade herausgefunden. Es ist wahrscheinlich eine schlechte Praxis, aber in diesem Fall hat es bei mir funktioniert.

Ich bin einer der faulen Leute, die nicht jeden Spaltennamen mit einem Tabellenpräfix aliasen oder ausschreiben wollen.

Sie können alle Spalten aus einer bestimmten Tabelle table_name.*auswählen, indem Sie sie in Ihrer select-Anweisung verwenden.

Wenn Sie Spaltennamen dupliziert haben, wird MySQL vom ersten bis zum letzten überschrieben. Die Daten aus dem ersten duplizierten Spaltennamen werden überschrieben, wenn dieser Spaltenname erneut gefunden wird. Der doppelte Spaltenname, der zuletzt eingeht, gewinnt also.

Wenn ich 3 Tabellen verbinde, die jeweils einen doppelten Spaltennamen enthalten, bestimmt die Reihenfolge der Tabellen in der select-Anweisung, welche Daten ich für die doppelte Spalte erhalte.

Beispiel:

SELECT table1.* , table2.* , table3.* FROM table1 LEFT JOIN table2 ON table1.dup = table2.dup LEFT JOIN table3 ON table2.dup = table3.dup;

Im obigen Beispiel wird der Wert von dupI von sein table3.

Was ist, wenn ich dupder Wert von sein möchte table1?

Dann muss ich das machen:

SELECT table3.* , table2.* , table1.* FROM table1 LEFT JOIN table2 ON table1.dup = table2.dup LEFT JOIN table3 ON table2.dup = table3.dup;

Jetzt table1kommt der letzte, also ist der Wert von dupder Wert aus Tabelle1.

Ich habe den Wert bekommen, den ich wollte, dupohne jede einzelne verdammte Spalte aufschreiben zu müssen, und ich bekomme immer noch alle Spalten, mit denen ich arbeiten kann. Yay!

Ich weiß, dass der Wert von dupin allen 3 Tabellen gleich sein sollte, aber was ist, wenn table3es keinen passenden Wert für gibt dup? Dann dupwäre im ersten Beispiel leer, und das wäre ein Mist.

Eab
quelle
3
Es ist eine schreckliche Idee, sich auf eine undokumentierte Bestellung zu verlassen, die im Moment für Sie funktioniert und keine Fehler verursacht, wenn sich die Datenbankimplementierung ändert.
simo.3792
2
Der Bestell-Hack ist beängstigend, aber +1 für die Auswahl, table.*damit ich es tun kann SELECT table1.*, table2.this_one_column_need FROM table1 INNER JOIN table2 ON....
tobek
10

@ Jason. Sie haben Recht, außer dass PHP der Schuldige ist und nicht MySQL. Wenn Sie Ihre JOINin MySQL Workbench einfügen, erhalten Sie drei Spalten mit genau demselben Namen (eine für jede Tabelle), jedoch nicht mit denselben Daten (einige davon, nullwenn diese Tabelle nicht mit der übereinstimmt JOIN).

Wenn Sie MYSQL_NUMin PHP verwenden, erhalten mysql_fetch_array()Sie alle Spalten. Das Problem ist, wenn Sie mysql_fetch_array()mit verwenden MYSQL_ASSOC. Dann baut PHP innerhalb dieser Funktion den Rückgabewert wie folgt auf:

$row['dup'] = [value from table1]

und später ...

$row['dup'] = [value from table2]

...

$row['dup'] = [value from table3]

Sie erhalten also nur den Wert aus Tabelle3. Das Problem ist, dass eine Ergebnismenge aus MySQL Spalten mit demselben Namen enthalten kann, assoziative Arrays in PHP jedoch keine doppelten Schlüssel in Arrays zulassen. Wenn die Daten in assoziativen Arrays in PHP gespeichert werden, gehen einige Informationen stillschweigend verloren ...

Gehe zu
quelle
9

Sie können so etwas tun

SELECT news.id as news_id, user.id as user_id ....

Und dann $row['news_id']wird die Nachrichten-ID und $row['user_id']die Benutzer-ID sein

Paolo Bergantino
quelle
9

Ein weiterer Tipp: Wenn Sie saubereren PHP-Code wünschen, können Sie eine ANSICHT in der Datenbank erstellen, z

Zum Beispiel:

CREATE VIEW view_news AS
SELECT
  news.id news_id,
  user.id user_id,
  user.name user_name,
  [ OTHER FIELDS ]
FROM news, users
WHERE news.user_id = user.id;

In PHP:

$sql = "SELECT * FROM view_news";
Dolch
quelle
3

Ich hatte das gleiche Problem mit dynamischen Tabellen. (Tabellen, von denen angenommen wird, dass sie eine ID haben, um verbunden werden zu können, jedoch ohne Annahme für den Rest der Felder.) In diesem Fall kennen Sie die Aliase vorher nicht.

In solchen Fällen können Sie zuerst die Tabellenspaltennamen für alle dynamischen Tabellen abrufen:

$tblFields = array_keys($zendDbInstance->describeTable($tableName));

Wenn $ zendDbInstance eine Instanz von Zend_Db ist oder Sie eine der Funktionen hier verwenden können, um sich nicht auf Zend php pdo zu verlassen: Rufen Sie den Spaltennamen einer Tabelle ab

Dann können Sie für alle dynamischen Tabellen die Aliase abrufen und $ tableName. * Für diejenigen verwenden, für die Sie keine Aliase benötigen:

$aliases = "";
foreach($tblKeys as $field)
    $aliases .= $tableName . '.' . $field . ' AS ' . $tableName . '_' . $field . ',' ;
$aliases = trim($aliases, ',');

Sie können diesen gesamten Prozess in einer generischen Funktion zusammenfassen und einfach saubereren Code haben oder fauler werden, wenn Sie möchten :)

haknick
quelle
1

Wenn Sie keine Lust auf Aliassing haben, können Sie auch einfach die Tabellennamen voranstellen.

Auf diese Weise können Sie die Generierung Ihrer Abfragen besser automatisieren. Es wird auch empfohlen, select * nicht zu verwenden (dies ist offensichtlich langsamer als nur die Auswahl der Felder, die Sie benötigen. Benennen Sie außerdem nur die Felder, die Sie haben möchten, explizit.

SELECT
    news.id, news.title, news.author, news.posted, 
    users.id, users.name, users.registered 
FROM 
    news 
LEFT JOIN 
    users 
ON 
    news.user = user.id
SchizoDuckie
quelle
Damit ist ein Problem verbunden. PHP entfernt das Präfix. Wenn Sie also das Ergebnis abrufen, können Sie $row['new.id']keine Lösung dafür finden.
CoffeDeveloper
0

Hier ist eine Antwort auf das oben Gesagte, die sowohl einfach ist als auch mit zurückgegebenen JSON-Ergebnissen funktioniert. Während die SQL-Abfrage bei Verwendung von SELECT * jeder Instanz identischer Feldnamen automatisch Tabellennamen voranstellt, ignoriert die JSON-Codierung des Ergebnisses zum Zurücksenden an die Webseite die Werte dieser Felder mit einem doppelten Namen und gibt stattdessen einen NULL-Wert zurück .

Genau das, was es tut, ist, die erste Instanz des duplizierten Feldnamens einzuschließen, aber seinen Wert auf NULL zu setzen. Und die zweite Instanz des Feldnamens (in der anderen Tabelle) wird vollständig weggelassen, sowohl der Feldname als auch der Wert. Wenn Sie die Abfrage jedoch direkt in der Datenbank testen (z. B. mit Navicat), werden alle Felder in der Ergebnismenge zurückgegeben. Erst wenn Sie das nächste Mal die JSON-Codierung dieses Ergebnisses durchführen, haben sie NULL-Werte und nachfolgende doppelte Namen werden vollständig weggelassen.

Eine einfache Möglichkeit, dieses Problem zu beheben, besteht darin, zuerst SELECT * auszuführen und anschließend Alias-Felder für die Duplikate zu erstellen. Hier ist ein Beispiel, in dem beide Tabellen Felder mit dem gleichen Namen site_name haben.

SELECT *, w.site_name AS wo_site_name FROM ws_work_orders w JOIN ws_inspections i WHERE w.hma_num NOT IN(SELECT hma_number FROM ws_inspections) ORDER BY CAST(w.hma_num AS UNSIGNED);

Jetzt können Sie im dekodierten JSON das Feld wo_site_name verwenden und es hat einen Wert. In diesem Fall haben Site-Namen Sonderzeichen wie Apostrophe und einfache Anführungszeichen, daher die Codierung beim ursprünglichen Speichern und die Decodierung bei Verwendung des Ergebnisses aus der Datenbank.

...decHTMLifEnc(decodeURIComponent( jsArrInspections[x]["wo_site_name"]))

Sie müssen immer das * zuerst in die SELECT-Anweisung setzen, aber danach können Sie so viele benannte und aliasierte Spalten einfügen, wie Sie möchten, da das wiederholte Auswählen einer Spalte kein Problem verursacht.

GAG und Spice
quelle
-1

Wenn Sie PDO für eine Datenbankinteraktion verwenden, können Sie den Abrufmodus PDO :: FETCH_NAMED verwenden , um das Problem zu beheben:

$sql = "SELECT * FROM news JOIN users ON news.user = user.id";
$data = $pdo->query($sql)->fetchAll(PDO::FETCH_NAMED);
foreach ($data as $row) {
    echo $row['column-name'][0]; // from the news table
    echo $row['column-name'][1]; // from the users table
    echo $row['other-column']; // any unique column name
}
Ihr gesunder Menschenverstand
quelle
-2

Es gibt zwei Ansätze:

  1. Verwenden von Aliasen; Bei dieser Methode geben Sie den verschiedenen Spalten neue eindeutige Namen (ALIAS) und verwenden sie dann beim PHP-Abruf. z.B

    SELECT student_id AS FEES_LINK, student_class AS CLASS_LINK 
    FROM students_fee_tbl 
    LEFT JOIN student_class_tbl ON students_fee_tbl.student_id = student_class_tbl.student_id
    

    und dann die Ergebnisse in PHP abrufen:

    $query = $PDO_stmt->fetchAll();
    
    foreach($query as $q) {
        echo $q['FEES_LINK'];
    }
    
  2. Verwenden der Ortsposition oder des Ergebnismengen-Spaltenindex; In diesem Fall werden die Array-Positionen verwendet, um auf die duplizierten Spaltennamen zu verweisen. Da sie an verschiedenen Positionen angezeigt werden, sind die verwendeten Indexnummern immer eindeutig. Die Indexpositionierungsnummern beginnen jedoch bei 0. z

    SELECT student_id, student_class 
    FROM students_fee_tbl 
    LEFT JOIN student_class_tbl ON students_fee_tbl.student_id = student_class_tbl.student_id
    

    und dann die Ergebnisse in PHP abrufen:

    $query = $PDO_stmt->fetchAll();
    
    foreach($query as $q) {
        echo $q[0];
    }
    
Musman
quelle