Εξοντώνοντας τα n+1 προβλήματα με ομαδοποίηση
Διόρθωσε τα n+1 update queries ομαδοποιώντας τις εγγραφές ανά νέα τιμή: ένα UPDATE … WHERE id IN (…) ανά ομάδα, και η εκδοχή με update_all στο Rails.
Ας πούμε ότι έχεις τα παρακάτω δεδομένα σε ένα αρχείο .csv και έναν πίνακα users με στήλες id και first_name στη βάση σου.
| Database ID | First name |
|---|---|
| 1 | Nick |
| 2 | Nick |
| 3 | Alex |
| 4 | Alex |
| 5 | Panos |
| 6 | Panos |
Φαντάσου ότι θέλεις να ενημερώσεις προγραμματιστικά τη στήλη first_name με τα αντίστοιχα δεδομένα από το csv.
Το σύνολο δεδομένων είναι αρκετά μικρό ώστε να μπορούμε να τρέξουμε ένα update query για κάθε γραμμή. Αν όμως οι εγγραφές ήταν χιλιάδες, αυτό πιθανότατα θα έπαιρνε πολύ περισσότερο χρόνο ή θα αποτύγχανε εντελώς.
Αυτό που χρειάζεται είναι να διαβάσουμε τα δεδομένα από το αρχείο και μετά να τα ομαδοποιήσουμε/μετατρέψουμε σε dictionaries, hashes, associative arrays ή ό,τι άλλο προσφέρει η γλώσσα προγραμματισμού που προτιμάς. Εδώ θα πάμε με json.
{
"Nick": [1, 2]
"Alex": [3, 4]
"Panos": [5, 6]
}Τώρα, αντί να ενημερώνουμε κάθε εγγραφή ξεχωριστά, μπορούμε να τις ενημερώσουμε μαζικά ανά ομάδα 🎉
UPDATE users SET first_name = Nick WHERE id IN (1, 2);UPDATE users SET first_name = Alex WHERE id IN (3, 4);UPDATE users SET first_name = Panos WHERE id IN (5, 6);Αν χρησιμοποιείς ένα ORM όπως το ActiveRecord του Rails ή το Eloquent του Laravel, πιθανότατα θα φόρτωνες πρώτα τις εγγραφές και μετά θα τις ενημέρωνες αναλόγως.
Στο Rails θα έκανες κάτι τέτοιο:
data = {
"Nick" => [1, 2]
"Alex" => [3, 4]
"Panos" => [5, 6]
}
data.each do |name, record_ids|
User.where(id: record_ids).update_all(first_name: name)
endΠαρ’ όλα αυτά, υπάρχουν τρόποι να στύψεις λίγη παραπάνω απόδοση. Το να διαβάζεις το αρχείο σε batches και να χρησιμοποιείς το find_in_batches του Rails θα βοηθήσει πιθανότατα να εξοικονομήσεις μνήμη, ή μπορείς ακόμα και να παρακάμψεις το ORM και να κατέβεις σε raw SQL.