Spalte aus einer anderen Spalte berechnet?

75

Angesichts der folgenden Tabelle:

id | value
--------------
1     6
2     70

Gibt es eine Möglichkeit, eine Spalte hinzuzufügen, die automatisch basierend auf einer anderen Spalte in derselben Tabelle berechnet wird? Wie eine ANSICHT, aber Teil derselben Tabelle. Als Beispiel calculatedwäre die Hälfte von value. Calculatedsollte bei valueÄnderungen automatisch aktualisiert werden , genau wie eine ANSICHT.

Das Ergebnis wäre:

id | value | calculated
-----------------------
1     6       3
2     70      35
Matthew
quelle
6
Warum also nicht eine ANSICHT verwenden?
Tim Child
2
Permanente (auch als gespeicherte) berechnete Spalten sind oft billiger zu lesen, da sie genau wie andere Spalten gespeichert werden. Sie können sogar indiziert werden.
Jonathan Allen
1
Nicht persistente berechnete Spalten sind nur eine praktische Funktion. Es funktioniert oft besser als Ansichten, wenn es um ORMs geht.
Jonathan Allen

Antworten:

68

Generated Column ist einer der guten Ansätze für die MySql-Version 5.7.6 und höher.

Es gibt zwei Arten von generierten Spalten:

  • Virtuell (Standard) - Die Spalte wird im laufenden Betrieb berechnet, wenn ein Datensatz aus einer Tabelle gelesen wird
  • Gespeichert - Die Spalte wird berechnet, wenn ein neuer Datensatz in die Tabelle geschrieben / aktualisiert wird

Beide Typen können NICHT NULL-Einschränkungen haben, aber nur eine gespeicherte generierte Spalte kann Teil eines Index sein.

Für den aktuellen Fall verwenden wir eine gespeicherte generierte Spalte. Zur Implementierung habe ich berücksichtigt, dass beide für die Berechnung erforderlichen Werte in der Tabelle vorhanden sind

CREATE TABLE order_details (price DOUBLE, quantity INT, amount DOUBLE AS (price * quantity));

INSERT INTO order_details (price, quantity) VALUES(100,1),(300,4),(60,8);

Der Betrag wird automatisch in der Tabelle angezeigt und Sie können direkt darauf zugreifen. Beachten Sie außerdem, dass der Betrag bei jeder Aktualisierung einer der Spalten ebenfalls aktualisiert wird.

Abhishek Gupta
quelle
Laut Dokumentation funktioniert dies nur mit der NDB-Speicher-Engine, nicht mit InnoDB
Cliffordheath,
@cliffordheath - du liegst falsch. Ich habe ein Beispiel für eine SQL-Geige mit mySQL 5.7, der InnoDB-Engine und der generierten Spalte erstellt - es funktioniert einwandfrei: db-fiddle . Das mySQL- Dokument, auf das Sie sich beziehen, ist in der Tat irreführend, aber nehmen wir an, es sollte lauten: "Generierte Spalten werden ab mySQL 5.7 und der NDB-Speicher-Engine ab MySQL NDB Cluster 7.5.3 unterstützt"
user2988142
@ user2988142 Gut zu wissen. Sogar Ihr Wortlaut ist möglicherweise nicht eindeutig. Vielleicht einen Dokumentationsfehlerbericht einreichen?
Cliffordheath
Ich versuche, die automatisch generierte Spaltenfunktionalität über MySQL zu verwenden, während ich versuche, mit der Datenbank über JDBC zu interagieren. -Die automatisch generierte Zelle, in der das Ergebnis gemäß der gerahmten Abfrage angezeigt werden soll. Diese Zelle kann bearbeitet und nicht für die automatische Generierung blockiert werden. Lassen Sie mich bitte wissen, welche Änderungen ich an meinem Code benötige, damit diese Zeile automatisch ausgefüllt wird. Auch seit der automatischen Generierung wurde die Funktion in MySQL 5.7 eingeführt, aber ich verwende 5.1 ver Connector / Treiber. Könnte das ein Problem sein?
Ashish Ramtri
41

Wenn es sich um eine Auswahl handelt, können Sie Folgendes tun:

SELECT id, value, (value/2) AS calculated FROM mytable

Andernfalls können Sie auch zuerst die Tabelle ändern, um die fehlende Spalte hinzuzufügen, und dann eine UPDATE-Abfrage ausführen, um die Werte für die neue Spalte wie folgt zu berechnen:

UPDATE mytable SET calculated = value/2;

Wenn es automatisch sein muss und Ihre MySQL-Version dies zulässt, können Sie es mit Triggern versuchen

Vincent Mimoun-Prat
quelle
7
Ja, das ist ein SELECT, bei dem der Tabelle keine Spalte hinzugefügt wird.
Matthew
4
Können Sie diese calculatedSpalte verwenden, um weitere Berechnungen in derselben Abfrage auszuführen? Wie ( calculated* 2) AS double_calculated?
ZurabWeb
@ZurabWeb können Sie, aber in äußeren select (verschachtelte select-Anweisungen sind verfügbar)
Kamil Gosciminski
21

@ krteks Antwort ist in die richtige Richtung, hat aber ein paar Probleme.

Die schlechte Nachricht ist, dass die Verwendung von UPDATE in einem Trigger für dieselbe Tabelle nicht funktioniert. Die gute Nachricht ist, dass es nicht notwendig ist; Es gibt ein NEUES Objekt, an dem Sie arbeiten können, bevor der Tisch überhaupt berührt wird.

Der Auslöser wird:

CREATE TRIGGER halfcolumn_update BEFORE UPDATE ON my_table
  FOR EACH ROW BEGIN
    SET NEW.calculated = NEW.value/2;
  END;

Beachten Sie auch, dass BEGIN ... END; Die Syntax muss mit einem anderen Trennzeichen analysiert werden. Der ganze Schebang wird:

DELIMITER |

CREATE TRIGGER halfcolumn_insert BEFORE INSERT ON my_table
  FOR EACH ROW BEGIN
    SET NEW.calculated = NEW.value/2;
  END;
|

CREATE TRIGGER halfcolumn_update BEFORE UPDATE ON my_table
  FOR EACH ROW BEGIN
    SET NEW.calculated = NEW.value/2;
  END;
|

DELIMITER ;
Jerry
quelle
3
Hinweis: Ersetzen Sie im obigen Code das Wort "Tabelle" durch Ihren Tabellennamen. Ich dachte, der Autor hätte den Tabellennamen einfach vergessen und nach "ON table" hinzugefügt und ein paar Minuten damit verschwendet.
Bloodboiler
Guter Anruf @Bloodboiler - Ich habe das Snippet so geändert, dass es etwas klarer ist.
Jerry
1
Beachten Sie, dass MySQL all diese Jahre später einen viel eleganteren Weg gefunden hat, um diese Aufgabe zu erfüllen. Diese Antwort funktioniert immer noch, aber es gibt einige gute Argumente gegen die Verwendung von Triggern. Wenn es für Sie praktisch ist, die Antwort von Abhishek Gupta zu verwenden, erhalten Sie ein wartbareres System.
Jerry
8

Sie können generierte Spalten aus MYSQL 5.7 verwenden.

Anwendungsbeispiel:

ALTER TABLE tbl_test
ADD COLUMN calc_val INT 
GENERATED ALWAYS AS (((`column1` - 1) * 16) + `column2`) STORED;

VIRTUELL / GESPEICHERT

  • Virtuell: Wird im laufenden Betrieb berechnet, wenn ein Datensatz aus einer Tabelle gelesen wird (Standard).
  • Gespeichert: Wird berechnet, wenn ein neuer Datensatz in die Tabelle eingefügt / aktualisiert wird
PodTech.io
quelle
4

Wenn Sie Ihrer Tabelle eine Spalte hinzufügen möchten, die automatisch auf die Hälfte einer anderen Spalte aktualisiert wird, können Sie dies mit einem Trigger tun.

Aber ich denke, die bereits vorgeschlagene Antwort ist ein besserer Weg, dies zu tun.

Trockencodierter Auslöser:

CREATE TRIGGER halfcolumn_insert AFTER INSERT ON table
  FOR EACH ROW BEGIN
    UPDATE table SET calculated = value / 2 WHERE id = NEW.id;
  END;
CREATE TRIGGER halfcolumn_update AFTER UPDATE ON table
  FOR EACH ROW BEGIN
    UPDATE table SET calculated = value / 2 WHERE id = NEW.id;
  END;

Ich glaube nicht, dass Sie nur einen Auslöser setzen können, da das Ereignis, auf das wir reagieren müssen, unterschiedlich ist.

krtek
quelle
Vielen Dank. In diesem Fall sollten wir auch Löschen?
Ernest Han
3

Ich hoffe, das hilft immer noch jemandem, so viele Leute wie möglich zu diesem Artikel kommen. Wenn Sie eine berechnete Spalte benötigen, können Sie Ihre gewünschten Spalten in einer Ansicht anzeigen. Speichern Sie nicht nur Daten oder überladen Sie die Leistung mit Triggern. Stellen Sie einfach die Daten bereit, die Sie bereits in einer Ansicht formatiert / berechnet haben.

Hoffe das hilft...

Doron
quelle
Wenn eine OLAP-Lösung zu viel Aufwand verursacht, möchten Sie möglicherweise Spalten mit gerundeten Werten hinzufügen, die für schnelle Berichtszwecke verwendet werden sollen (gruppierte Aggregate summieren sich also immer zu denselben Beträgen wie das CRM / ERP- oder Bankkonto). Das Speichern der Werte kann Berichte beschleunigen, wenn die Berechnung mehr als eine triviale Berechnung umfasst, z. B. Berechnungen der Umsatzmarge, oder wenn für die Berechnung andere Tabellen verknüpft werden müssen. Geben Sie den Wert lieber ein, während Sie beim Einfügen alle Daten zur Hand haben, als alles zusammen zu verschlüsseln, wenn ein Bericht angefordert wird.
Louis Somers