Heim > Web-Frontend > js-Tutorial > QL-Ansätze zur Massenaktualisierung von Datensätzen mit Knex.js

QL-Ansätze zur Massenaktualisierung von Datensätzen mit Knex.js

王林
Freigeben: 2024-08-12 22:31:15
Original
1137 Leute haben es durchsucht

QL Approaches to Bulk Update Records with Knex.js

In der Welt der Webentwicklung ist die effiziente Arbeit mit Datenbanken von entscheidender Bedeutung, insbesondere bei der Abwicklung von Massenvorgängen wie der gleichzeitigen Aktualisierung mehrerer Datensätze. Unabhängig davon, ob Sie Lagerbestände verwalten, Benutzerdaten verarbeiten oder Transaktionen abwickeln, ist die Fähigkeit, Massenaktualisierungen auf effiziente und zuverlässige Weise durchzuführen, von entscheidender Bedeutung.

In diesem Leitfaden erläutern wir drei wesentliche SQL-Techniken für die Massenaktualisierung von Datensätzen mit Knex.js, einem vielseitigen Abfrage-Builder für Node.js. Jeder Ansatz ist auf unterschiedliche Szenarien zugeschnitten und bietet je nach Ihrem spezifischen Anwendungsfall unterschiedliche Vorteile. Wir behandeln Folgendes:

  1. Einzelne Aktualisierung mit mehreren Bedingungen: Eine Methode, die es Ihnen ermöglicht, mehrere Datensätze in einer einzigen Abfrage zu aktualisieren und dabei bedingte Logik zu nutzen, um verschiedene Aktualisierungen basierend auf bestimmten Kriterien anzuwenden.

  2. Batchaktualisierungen mit einzelnen Abfragen in einer Transaktion: Dieser Ansatz nutzt Transaktionen, um die Atomizität sicherzustellen und mehrere Aktualisierungsabfragen sicher und effizient auszuführen.

  3. Upsert (Einfügen oder Aktualisieren) mit onConflict: Ideal für Szenarien, in denen Sie entweder neue Datensätze einfügen oder vorhandene aktualisieren müssen, ohne das Risiko doppelter Daten einzugehen.

In den folgenden Abschnitten werden wir näher auf jede dieser Methoden eingehen und ihre Implementierung, Vorteile und besten Anwendungsfälle untersuchen. Wenn Sie diese Ansätze verstehen, können Sie die für Ihre spezifischen Anforderungen am besten geeignete Technik auswählen und so sowohl die Leistung als auch die Datenintegrität in Ihren Anwendungen optimieren.


1. Einzelnes Update mit mehreren Bedingungen

Wenn es darum geht, mehrere Datensätze in einer Datenbank zu aktualisieren, ist Effizienz der Schlüssel. Eine leistungsstarke Technik besteht darin, eine einzelne UPDATE-Abfrage mit mehreren Bedingungen zu verwenden. Diese Methode ist besonders nützlich, wenn Sie basierend auf bestimmten Kriterien verschiedene Aktualisierungen auf verschiedene Datensätze anwenden müssen, alles innerhalb einer einzigen SQL-Anweisung.

Das Konzept:

Die Kernidee hinter dem Ansatz „Einzelaktualisierung mit mehreren Bedingungen“ besteht darin, eine einzige UPDATE-Abfrage zu verwenden, um mehrere Zeilen zu ändern, wobei jede Zeile basierend auf ihren einzigartigen Eigenschaften möglicherweise unterschiedliche Werte erhält. Dies wird durch die Verwendung von CASE-Anweisungen innerhalb der UPDATE-Abfrage erreicht, sodass Sie bedingte Logik für jedes Feld angeben können, das aktualisiert werden muss.

Warum diesen Ansatz verwenden:

  • Effizienz: Bei einer kleinen bis mittleren Anzahl von Datensätzen (z. B. ein paar Dutzend bis ein paar Hundert) kann die Konsolidierung mehrerer Aktualisierungen in einer einzigen Abfrage die Leistung erheblich verbessern, indem die Anzahl reduziert wird von Datenbank-Roundtrips. Dies kann besonders bei hochfrequenten Updates von Vorteil sein. Bei sehr großen Datensätzen (Tausende oder mehr) ist dieser Ansatz jedoch möglicherweise nicht so effektiv. Wir besprechen später in diesem Leitfaden alternative Methoden für den Umgang mit großen Datensätzen.

  • Einfachheit: Die Verwaltung von Updates mit einer einzigen Abfrage ist oft einfacher und wartbarer als die Ausführung mehrerer separater Abfragen. Dieser Ansatz reduziert die Komplexität Ihrer Datenbankinteraktionen und erleichtert das Verständnis des Codes, insbesondere bei einer moderaten Anzahl von Aktualisierungen.

  • Reduzierter Overhead: Weniger Abfragen bedeuten weniger Overhead für die Datenbank, was zu einer besseren Gesamtleistung führen kann. Dies ist besonders wichtig in Szenarien, in denen Netzwerklatenz oder Datenbanklast die Betriebsgeschwindigkeit beeinträchtigen könnten.
    Für eine sehr große Anzahl von Datensätzen untersuchen wir in diesem Leitfaden andere Strategien, um den potenziellen Overhead effektiver zu verwalten.

Beispielimplementierung:

Hier ist ein praktisches Beispiel dafür, wie Sie diesen Ansatz mit Knex.js, einem beliebten SQL-Abfrage-Builder für Node.js, implementieren können. Dieses Beispiel zeigt, wie Sie mehrere Felder für mehrere Datensätze auf einmal aktualisieren und dabei bedingte Logik verwenden, um unterschiedliche Aktualisierungen basierend auf der Datensatz-ID anzuwenden:

const queryHeaderProductUpdate = 'UPDATE products SET '; // Start of the SQL UPDATE query
const updatesProductUpdate = []; // Array to hold the individual update statements
const parametersProductUpdate = []; // Array to hold the parameters for the query

const updateProducts = [
  { product_id: 1, name: 'New Name 1', price: 100, status: 'Active' },
  { product_id: 2, name: 'New Name 2', price: 150, status: 'Inactive' },
  { product_id: 3, name: 'New Name 3', price: 200, status: 'Active' }
];

// Extract the product IDs to use in the WHERE clause
const productIds = updateProducts.map(p => p.product_id);

// Build the update statements for each field
updateProducts.forEach((item) => {
  // Add conditional logic for updating the 'name' field
  updatesProductUpdate.push('name = CASE WHEN product_id = ? THEN ? ELSE name END');
  parametersProductUpdate.push(item.product_id, item.name);

  // Add conditional logic for updating the 'price' field
  updatesProductUpdate.push('price = CASE WHEN product_id = ? THEN ? ELSE price END');
  parametersProductUpdate.push(item.product_id, item.price);

  // Add conditional logic for updating the 'status' field
  updatesProductUpdate.push('status = CASE WHEN product_id = ? THEN ? ELSE status END');
  parametersProductUpdate.push(item.product_id, item.status);

  // Add 'updated_at' field with the current timestamp
  updatesProductUpdate.push('updated_at = ?');
  parametersProductUpdate.push(knex.fn.now());

  // Add 'updated_by' field with the user ID
  updatesProductUpdate.push('updated_by = ?');
  parametersProductUpdate.push(req.user.userId);
});

// Construct the full query by joining the individual update statements and adding the WHERE clause
const queryProductUpdate = `${queryHeaderProductUpdate + updatesProductUpdate.join(', ')} WHERE product_id IN (${productIds.join(', ')})`;

// Execute the update query
await db.raw(queryProductUpdate, parametersProductUpdate);

Nach dem Login kopieren

Was dieser Code bewirkt:

  1. Erstellt den Abfrageheader: Beginnt die UPDATE-Anweisung für die Produkttabelle.

  2. Erstellt bedingte Aktualisierungen: Verwendet CASE-Anweisungen, um unterschiedliche Aktualisierungen für jedes Feld basierend auf der Produkt-ID anzugeben.

  3. Generiert die vollständige Abfrage: Kombiniert die Update-Anweisungen und die WHERE-Klausel.

  4. Führt die Abfrage aus: Führt die erstellte Abfrage aus, um die Aktualisierungen auf die angegebenen Datensätze anzuwenden.

Durch die Implementierung dieser Technik können Sie Massenaktualisierungen effizient mit bedingter Logik durchführen und so Ihre Datenbankvorgänge rationalisieren und effektiver gestalten.

Note: In the provided example, we did not use a transaction because the operation involves a single SQL query. Since a single query inherently maintains data integrity and consistency, there's no need for an additional transaction. Adding a transaction would only increase overhead without providing additional benefits in this context.

Having explored the "Single Update with Multiple Conditions" approach, which works well for a moderate number of records and provides simplicity and efficiency, we now turn our attention to a different scenario. As datasets grow larger or when atomicity across multiple operations becomes crucial, managing updates effectively requires a more robust approach.

Batch Updates with Individual Queries in a Transaction is a method designed to address these needs. This approach involves executing multiple update queries within a single transaction, ensuring that all updates are applied atomically. Let's dive into how this method works and its advantages.


2. Batch Updates with Individual Queries in a Transaction

When dealing with bulk updates, especially for a large dataset, managing each update individually within a transaction can be a robust and reliable approach. This method ensures that all updates are applied atomically and can handle errors gracefully.

Why Use This Approach:

  • Scalability: For larger datasets where Single Update with Multiple Conditions might become inefficient, batch updates with transactions offer better control. Each query is executed separately, and a transaction ensures that all changes are committed together, reducing the risk of partial updates.

  • Error Handling: Transactions provide a safety net by ensuring that either all updates succeed or none do. This atomicity guarantees data integrity, making it ideal for scenarios where you need to perform multiple related updates.

  • Concurrency Control: Using transactions can help manage concurrent modifications to the same records, preventing conflicts and ensuring consistency.

Code Example

Here’s how you can implement batch updates with individual queries inside a transaction using Knex.js:

const updateRecordsInBatch = async () => {
    // Example data to update
    const dataToUpdate = [
        { id: 1, name: 'Updated Name 1', price: 100 },
        { id: 2, name: 'Updated Name 2', price: 200 },
        { id: 3, name: 'Updated Name 3', price: 300 }
    ];

    // Start a transaction
    const trx = await db.transaction();
    const promises = [];

    try {
        // Iterate over the data and push update queries to the promises array
        dataToUpdate.forEach(record => {
            promises.push(
                trx('products')
                    .update({
                        name: record.name,
                        price: record.price,
                        updated_at: trx.fn.now()
                    })
                    .where('id', record.id)
            );
        });

        // Execute all queries concurrently
        await Promise.all(promises);

        // Commit the transaction
        await trx.commit();
        console.log('All records updated successfully.');
    } catch (error) {
        // Rollback the transaction in case of error
        await trx.rollback();
        console.error('Update failed:', error);
    }
};

Nach dem Login kopieren

Explanation

  1. Transaction Initialization: The transaction is started using db.transaction(), which ensures that all subsequent queries are executed within this transaction.

  2. Batch Updates: Each update query is constructed and added to an array of promises. This method allows for multiple updates to be performed concurrently.

  3. Executing Queries: Promise.all(promises) is used to execute all update queries concurrently. This approach ensures that all updates are sent to the database in parallel.

  4. Committing or Rolling Back: If all queries succeed, the transaction is committed with trx.commit(). If any query fails, the transaction is rolled back with trx.rollback(), ensuring that no partial updates are applied.

Using batch updates with individual queries inside a transaction provides a reliable way to manage large datasets. It ensures data integrity through atomic transactions and offers better control over concurrent operations. This method is especially useful when Single Update with Multiple Conditions may not be efficient for very large datasets.


3. Upsert (Insert or Update) Using onConflict

When you're working with data that might need to be inserted or updated depending on its existence in the database, an "upsert" operation is the ideal solution. This approach allows you to handle both scenarios—insert new records or update existing ones—in a single, streamlined operation. It's particularly useful when you want to maintain data consistency without having to write separate logic for checking whether a record exists.

Why Use This Approach:

  • Simplicity: An upsert enables you to combine the insert and update operations into a single query, simplifying your code and reducing the need for additional checks.

  • Efficiency: This method is more efficient than performing separate insert and update operations, as it minimizes database round-trips and handles conflicts automatically.

  • Conflict Handling: The onConflict clause lets you specify how to handle conflicts, such as when records with unique constraints already exist, by updating the relevant fields.

const productData = [
  {
    product_id: 1,
    store_id: 101,
    product_name: 'Product A',
    price: 10.99,
    category: 'Electronics',
  },
  {
    product_id: 2,
    store_id: 102,
    product_name: 'Product B',
    price: 12.99,
    category: 'Books',
  },
  {
    product_id: 3,
    store_id: 103,
    product_name: 'Product C',
    price: 9.99,
    category: 'Home',
  },
  {
    product_id: 4,
    store_id: 104,
    product_name: 'Product D',
    price: 15.49,
    category: 'Garden',
  },
];

await knex('products')
  .insert(productData)
  .onConflict(['product_id', 'store_id'])
  .merge({
    product_name: knex.raw('EXCLUDED.product_name'),
    price: knex.raw('EXCLUDED.price'),
    category: knex.raw('EXCLUDED.category'),
  });

Nach dem Login kopieren

Explanation

  1. Datendefinition: Wir definieren Produktdaten, ein Array von Objekten, die die Produktdatensätze darstellen, die wir einfügen oder aktualisieren möchten. Jedes Objekt enthält eine Produkt-ID, eine Geschäfts-ID, einen Produktnamen, einen Preis und eine Kategorie.

  2. Einfügen oder Aktualisieren: Die Funktion knex('products').insert(productData) versucht, jeden Datensatz aus dem ProductData-Array in die Produkttabelle einzufügen.

  3. Konflikte behandeln: Die onConflict(['product_id', 'store_id'])-Klausel gibt an, dass bei Auftreten eines Konflikts bei der Kombination von product_id und store_id der nächste Schritt ausgeführt werden soll.

  4. Zusammenführen (Aktualisierung bei Konflikt): Wenn ein Konflikt erkannt wird, aktualisiert die Methode merge({...}) den vorhandenen Datensatz mit den neuen Werten für Produktname, Preis und Kategorie aus Produktdaten. Die knex.raw('EXCLUDED.column_name')-Syntax wird verwendet, um auf die Werte zu verweisen, die eingefügt worden wären, sodass die Datenbank die vorhandenen Datensätze mit diesen Werten aktualisieren kann.

Damit die onConflict-Klausel in einem Upsert-Vorgang ordnungsgemäß funktioniert, müssen die beteiligten Spalten Teil einer eindeutigen Einschränkung sein. So funktioniert es:

  • Einzelne eindeutige Spalte: Wenn Sie eine einzelne Spalte in der onConflict-Klausel verwenden, muss diese Spalte in der gesamten Tabelle eindeutig sein. Diese Einzigartigkeit stellt sicher, dass die Datenbank anhand dieser Spalte genau erkennen kann, ob bereits ein Datensatz vorhanden ist.
  • Mehrere Spalten: Wenn in der onConflict-Klausel mehrere Spalten verwendet werden, muss die Kombination dieser Spalten eindeutig sein. Diese Einzigartigkeit wird durch einen eindeutigen Index oder eine eindeutige Einschränkung erzwungen, die sicherstellt, dass die kombinierten Werte dieser Spalten in der gesamten Tabelle eindeutig sind.

Indizes und Einschränkungen:
Indizes: Ein eindeutiger Index für eine oder mehrere Spalten ermöglicht der Datenbank eine effiziente Überprüfung der Eindeutigkeit von Werten. Wenn Sie einen eindeutigen Index definieren, verwendet die Datenbank diesen, um schnell zu überprüfen, ob die Werte in den angegebenen Spalten bereits vorhanden sind. Dies ermöglicht es der onConflict-Klausel, Konflikte genau zu erkennen und zu behandeln.

Einschränkungen: Eine eindeutige Einschränkung stellt sicher, dass Werte in einer oder mehreren Spalten eindeutig sein müssen. Diese Einschränkung ist für die Funktion der onConflict-Klausel von entscheidender Bedeutung, da sie Regeln erzwingt, die doppelte Werte verhindern und es der Datenbank ermöglichen, Konflikte basierend auf diesen Spalten zu erkennen.

Ähnlich wie beim Ansatz „Einzelaktualisierung mit mehreren Bedingungen“ erfordert ein Upsert-Vorgang keine Transaktion. Da es sich um eine einzige Abfrage handelt, die entweder Datensätze einfügt oder aktualisiert, funktioniert es effizient, ohne dass der zusätzliche Aufwand für die Verwaltung einer Transaktion anfällt.


Abschluss

Jede Technik bietet unterschiedliche Vorteile, von der Vereinfachung des Codes und der Reduzierung von Datenbankinteraktionen bis hin zur Gewährleistung der Datenintegrität und der effizienten Bearbeitung von Konflikten. Indem Sie die für Ihren Anwendungsfall am besten geeignete Methode auswählen, können Sie effizientere und zuverlässigere Updates in Ihren Anwendungen erzielen.

Wenn Sie diese Ansätze verstehen, können Sie Ihre Datenbankoperationen an Ihre spezifischen Anforderungen anpassen und so sowohl die Leistung als auch die Wartbarkeit verbessern. Unabhängig davon, ob es sich um Massenaktualisierungen oder komplexe Datenverwaltungsaufgaben handelt, ist die Wahl der richtigen Strategie entscheidend für die Optimierung Ihrer Arbeitsabläufe und die Erzielung besserer Ergebnisse in Ihren Entwicklungsprojekten.

Das obige ist der detaillierte Inhalt vonQL-Ansätze zur Massenaktualisierung von Datensätzen mit Knex.js. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Quelle:dev.to
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage