MySQL erhält den nächsten eindeutigen Wert ohne automatische Inkrementierung

7

Ich habe eine MySQL-Datenbank (InnoDB), die ich zur Verfolgung von Arbeitsaufträgen in meiner Organisation entwickle. Es gibt mehrere "Standorte", und jeder Standort verfügt über Arbeitsauftragsnummern, die bei 100 beginnen.

WorkorderID    SiteID    SiteWorkorderNum
     1           1            100
     2           1            101
     3           2            100

WorkorderID ist das Feld für die automatische Inkrementierung.
SiteID und SiteWorkorderNum werden beide als eindeutige Einschränkung festgelegt.

Immer wenn ein neuer Arbeitsauftrag für eine bestimmte Site eingefügt werden soll, prüft die Anwendung das Maximum (SiteWorkorderNum) für die Site-ID des angemeldeten Benutzers und gibt diesen Wert an die Einfügeanweisung zurück. Das Problem, das ich vermeiden möchte, besteht darin, dass zwei Benutzer derselben Site genau zur gleichen Zeit einen neuen Arbeitsauftrag eingeben.

Ich habe ein paar mögliche Lösungen, aber ich möchte sehen, ob es einen besseren Weg gibt, da jeder seine Nachteile hat, aber einer ist sicher besser als der andere, und natürlich bin ich offen für neue Ideen.

1) Sperren Sie die Tabelle, um sicherzustellen, dass keine anderen Benutzer einen SiteWorkorderNum-Wert abrufen, bis wir unsere Werte abgerufen und eingefügt haben (tun wir jedoch so, als hätten wir Tausende von Benutzern, die jede Minute versuchen, Arbeitsaufträge einzugeben. )

2) Sperren Sie die Tabelle nicht und rufen Sie die nächste verfügbare SiteWorkorderNum ab. Versuchen Sie, sie in die Datenbank einzufügen. Wenn ich einen eindeutigen Einschränkungsfehler erhalte, fangen Sie den Fehler ab und versuchen Sie es erneut, bis ich erfolgreich bin. (Dies könnte die Tabelle frei halten, aber wenn wir wieder so tun, als hätten wir Tausende von Benutzern am selben Standort, wären die mehreren Datenbankaufrufe ein wenig ineffizient?)

Bin ich zu paranoid darüber, wie eine dieser Lösungen die Leistung beeinflussen könnte? Natürlich habe ich nicht Tausende von Benutzern, aber ich möchte Best Practices in diesem Design anwenden, falls ich jemals an einem Projekt mit hohem Datenverkehr arbeite.

Dangel
quelle
Es würde eine einfache Lösung geben, wenn Sie MyISAM verwenden würden.
Ypercubeᵀᴹ

Antworten:

2

Was an dieser Situation interessant ist, kann mit dem MyISAM-Speicher gelöst werden.

Ich habe bereits im April 2012 eine Frage wie diese beantwortet: Wie können Sie zwei automatisch inkrementelle Spalten in einer Tabelle haben? Sie müssen eine Tabelle erstellen, deren einziger Zweck darin besteht, Arbeitsauftragssequenzen für jeden Standort zu erstellen

CREATE TABLE site_workorder_seq
(
    SiteID int not null,
    SiteWorkorderNum int not null auto_increment,
    PRIMARY KEY (SiteID,SiteWorkorderNum)
) ENGINE=MyISAM;

Hier ist ein Beispiel, das in diese Tabelle geladen wird:

mysql>     DROP DATABASE david;
Query OK, 1 row affected (0.01 sec)

mysql>     CREATE DATABASE david;
Query OK, 1 row affected (0.00 sec)

mysql>     USE david
Database changed
mysql>     CREATE TABLE site_workorder_seq
    ->     (
    ->         SiteID int not null,
    ->         SiteWorkorderNum int not null auto_increment,
    ->         PRIMARY KEY (SiteID,SiteWorkorderNum)
    ->     ) ENGINE=MyISAM;
Query OK, 0 rows affected (0.01 sec)

mysql>     INSERT INTO site_workorder_seq (SiteID) VALUES
    ->     (1),(1),(2),(3),(3),(3),(3),(4),(4),(4),
    ->     (5),(5),(4),(2),(2),(2);
Query OK, 16 rows affected (0.00 sec)
Records: 16  Duplicates: 0  Warnings: 0

mysql>     SELECT * FROM site_workorder_seq;
+--------+------------------+
| SiteID | SiteWorkorderNum |
+--------+------------------+
|      1 |                1 |
|      1 |                2 |
|      2 |                1 |
|      2 |                2 |
|      2 |                3 |
|      2 |                4 |
|      3 |                1 |
|      3 |                2 |
|      3 |                3 |
|      3 |                4 |
|      4 |                1 |
|      4 |                2 |
|      4 |                3 |
|      4 |                4 |
|      5 |                1 |
|      5 |                2 |
+--------+------------------+
16 rows in set (0.00 sec)

mysql>

Schauen wir uns die letzte WorkorderNum von jeder Site an

mysql> SELECT SiteID,MAX(SiteWorkorderNum) SiteWorkorderNum
    -> FROM site_workorder_seq GROUP BY SiteID;
+--------+------------------+
| SiteID | SiteWorkorderNum |
+--------+------------------+
|      1 |                2 |
|      2 |                4 |
|      3 |                4 |
|      4 |                4 |
|      5 |                2 |
+--------+------------------+
5 rows in set (0.05 sec)

mysql>

Angenommen, Sie möchten die nächste SiteWorkorderNum für SiteID 3 erhalten. Sie können dies tun:

INSERT INTO site_workorder_seq (SiteID) VALUES (3);
SELECT MAX(SiteWorkorderNum) INTO @nextworkordernum
FROM site_workorder_seq WHERE SiteID=3;
SELECT @nextworkordernum;

Lassen Sie uns dies ausführen und sehen, was passiert

mysql> INSERT INTO site_workorder_seq (SiteID) VALUES (3);
Query OK, 1 row affected (0.00 sec)

mysql> SELECT MAX(SiteWorkorderNum) INTO @nextworkordernum
    -> FROM site_workorder_seq WHERE SiteID=3;
Query OK, 1 row affected (0.00 sec)

mysql> SELECT @nextworkordernum;
+-------------------+
| @nextworkordernum |
+-------------------+
|                 5 |
+-------------------+
1 row in set (0.03 sec)

mysql> SELECT * FROM site_workorder_seq;
+--------+------------------+
| SiteID | SiteWorkorderNum |
+--------+------------------+
|      1 |                1 |
|      1 |                2 |
|      2 |                1 |
|      2 |                2 |
|      2 |                3 |
|      2 |                4 |
|      3 |                1 |
|      3 |                2 |
|      3 |                3 |
|      3 |                4 |
|      3 |                5 |
|      4 |                1 |
|      4 |                2 |
|      4 |                3 |
|      4 |                4 |
|      5 |                1 |
|      5 |                2 |
+--------+------------------+
17 rows in set (0.00 sec)

mysql> SELECT SiteID,MAX(SiteWorkorderNum) SiteWorkorderNum
    -> FROM site_workorder_seq GROUP BY SiteID;
+--------+------------------+
| SiteID | SiteWorkorderNum |
+--------+------------------+
|      1 |                2 |
|      2 |                4 |
|      3 |                5 |
|      4 |                4 |
|      5 |                2 |
+--------+------------------+
5 rows in set (0.00 sec)

mysql>

Solange Sie dieses eine MyISAM neben allen InnoDB-Tabellen verwenden, können Sie nach Herzenslust Workordernums pro Site generieren.

RolandoMySQLDBA
quelle
Was wäre, wenn ich eine zusätzliche Spalte für das automatische Inkrementieren als Primärschlüssel für alle Arbeitsaufträge verwenden möchte? (in der Tabelle site_workorder_seq). Ich vermute, dass dies nicht möglich ist, da ich bereits eine automatische Inkrementierungsspalte zugewiesen habe.
Dangel
Eine weitere Frage (möglicherweise etwas außerhalb des Geltungsbereichs dieses Beitrags), da MyISAM keine Transaktionen unterstützt. Wenn ich eine Transaktion starte, in eine MyISAM-Tabelle einfüge und meine Operationen an den anderen Tabellen fortsetze, wird die Einfügung in die MyISAM-Tabelle bewirkt, dass die Transaktion vorzeitig festgeschrieben wird?
Dangel
Sie würden höchstwahrscheinlich eine andere Tabelle benötigen, um über einen AFTER INSERT-Trigger auf site_workorder_seq gefüllt zu werden. Wenn Sie es als eine andere Frage posten, kann ich es versuchen.
RolandoMySQLDBA
Um die andere Frage zu beantworten, ja leider.
RolandoMySQLDBA
Danke für Ihre Hilfe! Ich werde darüber nachdenken und sehen, wohin ich von hier aus gehe und einen neuen Beitrag mit allen neuen Fragen verfassen, die mir einfallen.
Dangel