2016-04-10 10 views
0

Ich habe einige Aufgaben, die ich in meiner Datenbank speichern muss. Und jede Aufgabe hat eine Reihe von Daten, in denen die Aufgaben abgeschlossen wurden. Ich habe gelernt, dass es besser ist, kein Array (serialize) zu verwenden, um Daten zu speichern, sondern stattdessen eine andere Tabelle zu erstellen. Also habe ich:Speichern einer Reihe von Daten in einer separaten Tabelle, aber Probleme beim Abrufen

taskTable enthält Spalten: taskID, userid, description, name

task_days enthält Spalten: taskID, day

Aber Im Probleme mit php, in der Regel kann ich meine Daten senden leicht zu Kunde mit:

 function getTasks(){ 

      $app = \Slim\Slim::getInstance(); 
      $userid = $app->request->params('userid'); 

      $db = getDB(); 
      $result = $db->prepare("Select * From taskTable where userid = ?"); 
      $result->execute(array($userid)); 
      $result->setFetchmode(PDO::FETCH_ASSOC); 



      echo json_encode($result->fetchAll()); 
     } 

Ich kodiere es, dann kann Klient leicht rea d es als ein Array von JSON. Aber jetzt, mit zwei Tabellen, weiß ich nicht, wie ich es effizient machen soll. Ich weiß, dass ich die erforderlichen Informationen mit dieser Abfrage erhalten:

Select * from taskTable as t, task_days as d where t.taskID = d.taskID

Aber wie mache ich es so die Tage in einer Reihe mit der richtigen Aufgabe verbunden sein wird.

Ich zuerst Select * From taskTable where userid = $userid, dann für jede Aufgabe, werde ich eine Abfrage für die Tabelle task_days? das scheint jedoch äußerst ineffizient zu sein.

Deshalb möchte ich etwas wie folgt aus:

[ 
{taskid: 123, userid: 1, description: "do task", name: "tony", day:[1998-01-02, 1998-02-03]}, 
{taskid: 124, userid: 2, description: "do task2", name: "Ann", day:[2016-01-02, 2016-02-03, 2016-01-01]}, 
... 
] 

Antwort

1

Es gibt ein paar Ansätze.

1) Ein Ansatz, wie Sie bereits skizzieren, besteht darin, eine Abfrage auszuführen, die die Spalten nur aus 'taskTable' zurückgibt. Führen Sie für jede zurückgegebene Zeile eine weitere Abfrage aus, um die zugehörigen Zeilen von task_days abzurufen. Und Sie haben Recht, das ist normalerweise nicht der effizienteste Ansatz. Aber für eine einigermaßen kleine Anzahl von Zeilen sollte, Leistung sinnvoll sein, solange entsprechender Indizes zur Verfügung.)


2) Ein weiterer Ansatz, unter der Annahme, `taskid` ist der Primärschlüssel von` taskTable` ist ein auszuführen Join, und verwenden Sie eine "GROUP BY", um die Zeilen zu reduzieren. Die Aggregatfunktion "GROUP_CONCAT" kann die mehreren Werte von "Tag" aus der Tabelle "task_days" in eine einzige Zeichenfolge konvertieren. Zum Beispiel:

SELECT t.taskid 
     , t.userid 
     , t.description 
     , t.name 
     , GROUP_CONCAT(d.day ORDER BY d.day) AS `day` 
    FROM taskTable t 
    LEFT 
    JOIN task_days d 
    ON d.taskid = t.taskid 
    GROUP BY t.taskid 
    ORDER BY t.taskid 

Dies würde die day als String zurück, kein Array. Wenn Sie ein Array benötigen, müsste Ihr Code das tun. (Als eine bequeme Möglichkeit, das zu tun, könnte die PHP-Explode-Funktion geeignet sein.)

HINWEIS: die Länge der von GROUP_CONCAT zurückgegebenen Zeichenfolge ist durch Variable und auch durch max_allowed_packet begrenzt.


3) Ein anderer Weg, um diesen Ansatz ist es, eine Join-Operation auszuführen, und ziehen Sie die „dupliziert“ Task-Informationen, zurückbeordert von taskid und day

SELECT t.taskid 
     , t.userid 
     , t.description 
     , t.name 
     , d.day 
    FROM taskTable t 
    LEFT 
    JOIN task_days d 
    ON d.taskid = t.taskid 
    ORDER BY t.taskid, d.day 

, das wie ein Ergebnis gesetzt bekommen würde diese:

taskid userid description name day 
    ------ ------ ----------- ----- ---------- 
     123  1 do task  tony 1998-01-02 
     123  1 do task  tony 1998-02-03 
     124  2 do task2  Ann 2016-01-02 
     124  2 do task2  Ann 2016-02-03 
     124  2 do task2  Ann 2016-01-01 

Dann müsste Ihr Code einige rudimentäre "Kontrolle brechen" Verarbeitung. Vergleichen Sie im Grunde die Task-ID der aktuellen Zeile mit der Task-ID aus der vorherigen Zeile. Wenn sie übereinstimmen, verarbeiten Sie nur einen neuen "Tag" -Wert für dieselbe Aufgabe.

Wenn sich die Task-ID der aktuellen Zeile von der Task-ID der vorherigen Zeile unterscheidet, wird eine neue Aufgabe gestartet.

Ihr Code effektiv die doppelten Reihen von `taskTable` würde ignoriert, im Grunde auf dem Ergebnis gesetzt Schielen und es so zu sehen:

taskid userid description name day 
    ------ ------ ----------- ----- ---------- 
- 123  1 do task  tony 1998-01-02 
+          1998-02-03 
- 124  2 do task2  Ann 2016-01-02 
+          2016-02-03 
+          2016-01-01 

Followup

Die zweite Option ist Ihrer ursprünglichen Implementierung am nächsten, eine durch Kommas getrennte Liste von Werten als Zeichenfolge in einer Zeichenspalte.

Soweit speichern eine Komma getrennte Liste, das ist ein SQL-Anti-Muster, und es ist in der Regel am besten zu vermeiden. Mehrwertige Attribute können in einer separaten Tabelle gespeichert werden, wie Sie es getan haben.

Die Ausnahme wäre, wenn Sie nie jemals die Datenbank benötigen, um die Werte in der Liste als separate Werte zu sehen.

Wenn Sie speichern, dass „Liste der Daten“, als ob es ein Bild waren, zum Beispiel wie der Inhalt eines jpeg ... wenn Sie immer Laden der gesamte Wert in die Spalte und immer Extrakt der Inhalt der Spalte als einzelner Wert ... wenn nie müssen Sie nach einem individuellen Datum suchen, oder ein Datum zu einer vorhandenen Liste hinzufügen, oder entfernen Sie ein Datum aus einer Liste ... und wenn Sie nie brauchen die Datenbank, um irgendwelche Einschränkungen für die Werte zu erzwingen oder irgendeine Überprüfung des Inhalts durchzuführen ...

Wenn alle diese Bedingungen erfüllt sind, nur dann könnte es Es ist sinnvoll, eine kommagetrennte Liste als einzelne Spalte zu speichern.


Meine persönliche Präferenz, wenn die Umsetzung nur auf MySQL ausgerichtet ist, wäre die zweite Option sein ... mit GROUP_CONCAT. Wenn die Länge der von GROUP_CONCATgenerierten Zeichenfolgegroup_concat_max_len überschreitet, wird die Zeichenfolge ohne Warnung oder Fehler abgeschnitten. (Ich glaube, das ist eine Einschränkung in Byte und nicht in Zeichen.)

Die sicherste Codierung der Praxis eine Abfrage zu tun wäre, um auszuführen:

SELECT @@session.group_concat_max_len 

den Wert durch das zurück speichern.Dann vergleichen Sie für die Werte, die vom Ausdruck GROUP_CONCAT zurückgegeben werden, die Länge (in Bytes) mit dem gespeicherten Wert, um zu sehen, ob eine Kürzung aufgetreten ist. (Wenn die Länge der zurückgegebenen Zeichenfolge weniger als der Wert group_concat_max_len ist, können Sie ziemlich sicher sein, dass die Kürzung nicht aufgetreten ist.) Es ist auch möglich, den aktuellen Wert der Variablen zu überschreiben (bevor Sie die Anweisung ausführen, die GROUP_CONCAT enthält) ., mit einem separaten SET Aussage Etwas wie folgt aus:

SET SESSION group_concat_max_len = 131072 ; 

(Nur vorsichtig sein max_allowed_packet nicht zu überschreiten.)

+0

Vielen Dank für alle meine Optionen aufzeigt, so weit wie ich die zweite, aber das machen. Ich frage, was ich gelesen habe, in meinem Fall ist es dann nicht besser, alle Tage in einem Array inst serialisieren und speichern ead zum Erstellen dieser neuen Tabelle, da ich nicht sql Funktion wie count (*) oder avg verwenden werde. Ich möchte nur die Tage für jede Aufgabe bekommen, wenn ich GET oder POST anrufe. Mit etwas wie der zweiten Option muss ich jedes Mal, wenn ich eine Verbindung zur Datenbank herstelle, die Zeichenfolge in ein Array analysieren oder wenn ich ein Array parse, muss ich ein Array in eine Zeichenfolge analysieren. Oder ist das effizienter? Nochmals vielen Dank für die ausführliche Erklärung – user308553

Verwandte Themen