Προς το περιεχόμενο

MySQL: χρειάζομαι ένα ισοσύναμο, πιο αποδοτικό query


philos

Προτεινόμενες αναρτήσεις

Λοιπόν, έχουμε αυτόν τον πίνακα MySQL:

CREATE TABLE IF NOT EXISTS `sc_expirations` (
`dataid` int(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`node_id` int(10) NOT NULL DEFAULT 0,
`stickyid` int(10) NOT NULL DEFAULT 0,
`user_id` int(10) NOT NULL DEFAULT 0,
`thread_id` int(10) NOT NULL DEFAULT 0,
`dateline` int(11) NOT NULL DEFAULT 0,
`notification_sent` int(1) NOT NULL DEFAULT 0) 

Και τρέχω συγκεκριμένα, αυτό το query:

SELECT t1.*, xf_node.node_id, xf_node.title
FROM sc_expirations AS t1
LEFT JOIN xf_node USING (node_id)
WHERE FROM_UNIXTIME(`dateline`) < DATE_SUB(NOW(), INTERVAL 5 DAY)
		AND notification_sent = 0
		AND NOT EXISTS 
			(SELECT * FROM sc_expirations AS higher 
			 WHERE higher.node_id = t1.node_id and higher.dateline > t1.dateline)
ORDER BY dateline DESC

Δυστυχώς με 31k εγγραφές, αυτό το query θέλει 2 (!) λεπτά να ολοκληρωθεί.

Έχετε καμία ιδέα ώστε να πάρω τα ίδια στοιχεία με πιο αποδοτικό τρόπο;

Υποθέτω ότι το πρόβλημα είναι στο subquery. :)

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

2 ώρες πριν, philos είπε

Λοιπόν, έχουμε αυτόν τον πίνακα MySQL:


CREATE TABLE IF NOT EXISTS `sc_expirations` (
`dataid` int(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`node_id` int(10) NOT NULL DEFAULT 0,
`stickyid` int(10) NOT NULL DEFAULT 0,
`user_id` int(10) NOT NULL DEFAULT 0,
`thread_id` int(10) NOT NULL DEFAULT 0,
`dateline` int(11) NOT NULL DEFAULT 0,
`notification_sent` int(1) NOT NULL DEFAULT 0) 

Και τρέχω συγκεκριμένα, αυτό το query:


SELECT t1.*, xf_node.node_id, xf_node.title
FROM sc_expirations AS t1
LEFT JOIN xf_node USING (node_id)
WHERE FROM_UNIXTIME(`dateline`) < DATE_SUB(NOW(), INTERVAL 5 DAY)
		AND notification_sent = 0
		AND NOT EXISTS 
			(SELECT * FROM sc_expirations AS higher 
			 WHERE higher.node_id = t1.node_id and higher.dateline > t1.dateline)
ORDER BY dateline DESC

Δυστυχώς με 31k εγγραφές, αυτό το query θέλει 2 (!) λεπτά να ολοκληρωθεί.

Έχετε καμία ιδέα ώστε να πάρω τα ίδια στοιχεία με πιο αποδοτικό τρόπο;

Υποθέτω ότι το πρόβλημα είναι στο subquery. :)

Πόση ώρα κάνει άμα αφαιρέσεις τα subquery; Δοκιμασε πρωτα χωρις το 2ο εσωτερικο, μετα χωρις το πρωτο εσωτερικο, δες τι προκαλει το bottleneck, και προσπαθησε να το κανεις optimize.  

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Εικάζοντας γιατί δεν βλέπω τα δεδομένα πιστεύω ότι το query πρέπει να "πονάει" εδώ 

FROM_UNIXTIME(`dateline`) < DATE_SUB(NOW(), INTERVAL 5 DAY)

καθώς για κάθε εγγραφή εξαναγκάζεις το σύστημα να ξαναυπολογίσει την ημερομηνία.

Κάνε τον υπολογισμό πριν σε μια έξτρα μεταβλητή και κάνε με αυτήν την σύγκριση .

Επίσης αυτό το EXISTS δεν μπορείς κάπως να το περιορίσεις ?

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Τι μέγεθος έχει ο πίνακας xf_node ;

Που τρέχει το query ;

Αν και υποθέτω ότι θα έχεις , έχεις FK ; ( δεν φαίνεται στο CREATE )

 

Πώσα rows επιστρέφει το query σου ;

Δοκίμασε λιγάκι να βάλεις στο τέλος LIMIT 1, 1000 και πες το χρόνο απόκρισης...

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Αναφορά σε κείμενο

SELECT * FROM sc_expirations AS higher WHERE higher.node_id = t1.node_id and higher.dateline > t1.dateline

εδω δεν χρειαζετε να ψαχνεις για Not exists σε ολα τα records.
προσθεσε το  LIMIT 1 στο τελος

SELECT * FROM sc_expirations AS higher WHERE higher.node_id = t1.node_id and higher.dateline > t1.dateline limit 1



 
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δημοσ. (επεξεργασμένο)
4 ώρες πριν, papmel είπε

εδω δεν χρειαζετε να ψαχνεις για Not exists σε ολα τα records.
προσθεσε το  LIMIT 1 στο τελος



SELECT * FROM sc_expirations AS higher WHERE higher.node_id = t1.node_id and higher.dateline > t1.dateline limit 1


 

Είσαι σίγουρος γι' αυτό;

Αν θυμάμαι καλά το πρόβλημα (καθώς πάει καιρός που το έφτιαξα), το NOT EXISTS εξαιρεί όλα τα (πολλά) records που είναι πέραν από την τάδε ημερομηνία.

Αν βάλω LIMIT 1 λογικά δεν εξαιρεί μόνο 1 record;

Χίλια συγγνώμη που δε θυμάμαι 100% το πρόβλημα, απλά το query εν τέλει βγάζει σωστά αποτελέσματα.

Αν πράγματι με το να βάλω LIMIT 1 στο subquery, τα αποτελέσματα είναι ίδια (βάσει της λογικής των subqueries), τότε να το δοκιμάσω (το query τρέχει πλέον σε production site που δεν έχω πρόσβαση και μου έγινε η αναφορά ότι το query είναι αργό).

Σε ευχαριστώ! :)

Επεξ/σία από philos
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Στις 15/12/2020 στις 8:51 ΠΜ, masteripper είπε

Εικάζοντας γιατί δεν βλέπω τα δεδομένα πιστεύω ότι το query πρέπει να "πονάει" εδώ 


FROM_UNIXTIME(`dateline`) < DATE_SUB(NOW(), INTERVAL 5 DAY)

καθώς για κάθε εγγραφή εξαναγκάζεις το σύστημα να ξαναυπολογίσει την ημερομηνία.

Κάνε τον υπολογισμό πριν σε μια έξτρα μεταβλητή και κάνε με αυτήν την σύγκριση .

Επίσης αυτό το EXISTS δεν μπορείς κάπως να το περιορίσεις ?

Έχω ξανά χρησιμοποιήσει τη συγκεκριμένη WHERE ημερομηνιών, σε πίνακα με περισσότερα records χωρίς πρόβλημα! Μάλλον το πρόβλημα είναι στο subquery.

Στις 15/12/2020 στις 11:32 ΠΜ, dhmm είπε

Τι μέγεθος έχει ο πίνακας xf_node ;

Που τρέχει το query ;

Αν και υποθέτω ότι θα έχεις , έχεις FK ; ( δεν φαίνεται στο CREATE )

 

Πώσα rows επιστρέφει το query σου ;

Δοκίμασε λιγάκι να βάλεις στο τέλος LIMIT 1, 1000 και πες το χρόνο απόκρισης...

Ο xf_node δε χρειάζεται να μας απασχολεί, έχει περίπου 1500 - 2000 records.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δημοσ. (επεξεργασμένο)

Προσπάθησα να προσομοιώσω και όσον έβαζα data σε αυτό τον πίνακα xf_node αυξάνοντας το time. Ενώ δεν είχα καθυστέρηση καθόλου με 60κ. Όσον έκαμνα insert στο xf_node το επόμενο run αργούσε περισσότερο.

Το LIMIT φαίρνει την πρώτη γραμμή απο το αποτέλεσμα. Αν κάνεις για test ένα desc θα δείς ότι θα σου φέρει το τελευταίο

Επεξ/σία από dhmm
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

11 λεπτά πριν, dhmm είπε

Προσπάθησα να προσομοιώσω και όσον έβαζα data σε αυτό τον πίνακα xf_node αυξάνοντας το time. Ενώ δεν είχα καθυστέρηση καθόλου με 60κ. Όσον έκαμνα insert στο xf_node το επόμενο run αργούσε περισσότερο.

Το LIMIT φαίρνει την πρώτη γραμμή απο το αποτέλεσμα. Αν κάνεις για test ένα desc θα δείς ότι θα σου φέρει το τελευταίο

Ναι, ο πίνακας xf_node έχει πολύ λίγες εγγραφές (1500 - 2000 όπως είπα), συνεπώς δε νομίζω να είναι το πρόβλημα.

Το πρόβλημα πρέπει να είναι στο subquery, απλά δεν είμαι σίγουρος ότι με το να βάλω LIMIT 1 στο subquery θα έχω τα ίδια αποτελέσματα. Το LIMIT 1 θα φέρει μια γραμμή, ενώ χωρίς το LIMIT εξαιρεί πολλές εγγραφές. :) Είναι πράγματι ισοδύναμο;

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

1 ώρα πριν, philos είπε

Ναι, ο πίνακας xf_node έχει πολύ λίγες εγγραφές (1500 - 2000 όπως είπα), συνεπώς δε νομίζω να είναι το πρόβλημα.

Το πρόβλημα πρέπει να είναι στο subquery, απλά δεν είμαι σίγουρος ότι με το να βάλω LIMIT 1 στο subquery θα έχω τα ίδια αποτελέσματα. Το LIMIT 1 θα φέρει μια γραμμή, ενώ χωρίς το LIMIT εξαιρεί πολλές εγγραφές. :) Είναι πράγματι ισοδύναμο;

Όχι δεν είναι εννοείται... Διότι όπως είπα θα φέρει μόνο την ρώτη γραμμή. Αν χρειάζεσαι και τις άλλες δεν θα βργεί και σωστό αποτέλεσμα.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δημοσ. (επεξεργασμένο)
Στις 16/12/2020 στις 5:53 ΜΜ, dhmm είπε

Όχι δεν είναι εννοείται... Διότι όπως είπα θα φέρει μόνο την ρώτη γραμμή. Αν χρειάζεσαι και τις άλλες δεν θα βργεί και σωστό αποτέλεσμα.

Θα σας εξηγήσω τι ακριβώς κάνει το query. Στην ουσία λειτουργεί σωστά, ωστόσο με πολλές εγγραφές αποτυγχάνει. Κάνω ήδη προσπάθειες να σκεφτώ μια εναλλακτική λύση, αλλά κάθε βοήθεια ευπρόσδεκτη :)

Αρχικό query κι ο πίνακας:

CREATE TABLE IF NOT EXISTS `sc_expirations` (
`dataid` int(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`node_id` int(10) NOT NULL DEFAULT 0,
`stickyid` int(10) NOT NULL DEFAULT 0,
`user_id` int(10) NOT NULL DEFAULT 0,
`thread_id` int(10) NOT NULL DEFAULT 0,
`dateline` int(11) NOT NULL DEFAULT 0,
`notification_sent` int(1) NOT NULL DEFAULT 0) 
SELECT t1.*, xf_node.node_id, xf_node.title
FROM sc_expirations AS t1
LEFT JOIN xf_node USING (node_id)
WHERE FROM_UNIXTIME(`dateline`) < DATE_SUB(NOW(), INTERVAL 5 DAY)
		AND notification_sent = 0
		AND NOT EXISTS 
			(SELECT * FROM sc_expirations AS higher 
			 WHERE higher.node_id = t1.node_id and higher.dateline > t1.dateline)
ORDER BY dateline DESC

Ο πίνακας sc_expirations περιλαμβάνει τα δεδομένα αγορασμένων συνδρομών που ας το πούμε έληξαν. Δηλαδή κάθε φορά που λήγει μια συνδρομή, μια νέα γραμμή δημιουργείται στον πίνακα. Θέλουμε οι διαχειριστές της σελίδας να λάβουν μια ειδοποίηση αν δεν υπάρχουν νέες αγορές στη κατηγορία (node_id) μέσα σε 5 μέρες.

(dateline η unix time λήξης του προιόντος (stickyid), notification_sent bool για το αν στάλθηκε ειδοποίηση)

Μόλις γίνει το SELECT και εντοπιστούν στοιχεία, μπαίνει μια δομή επανάληψης για να σταλεί η ειδοποίηση και το αντίστοιχο notification_sent γίνεται από 0 σε 1 ώστε να μην ξανά ληφθεί υπόψιν στις μετέπειτα SELECT.

Τώρα που το σκέφτομαι, δε βρίσκω το νόημα του subquery, ωστόσο όταν το είχα τεστάρει έκανε ακριβώς αυτό που πρέπει. χμμμμ

 

Επεξ/σία από philos
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

  • 2 εβδομάδες αργότερα...
Δημοσ. (επεξεργασμένο)

Καλησπέρα. Πρώτων ούτε είχα την γνώση ότι υπάρχουν και τέτοια sql queries.

Γιατί δεν κάνεις το εξής (σε PHP)

$after5 = mktime(0, 0, 0, date('m'), date('d') + 5, date('Y'));
$sql = "SELECT t1.*, xf_node.node_id, xf_node.title
FROM sc_expirations AS t1
LEFT JOIN xf_node USING (node_id)
WHERE `dateline` < $after5
		AND notification_sent = 0
		AND NOT EXISTS 
			(SELECT * FROM sc_expirations AS higher 
			 WHERE higher.node_id = t1.node_id and higher.dateline > t1.dateline)
ORDER BY dateline DESC";

 

Επεξ/σία από Επισκέπτης
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

@philos θέλεις να μας περιγράψεις τι ακριβώς θέλεις να πετύχεις (χωρίς να αναφέρεις σχέσεις της βάσης); Μπορεί να σκεφτούμε κι άλλες ιδέες/συνδεσμολογίες.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

  • 2 εβδομάδες αργότερα...

Θα μπορούσες να βάλεις στο sc_expirations table να βάλεις index στο dateline, ως μια γρήγορη και lazy λύση, σίγουρα θα βελτιώσει την ταχύτητα των εργασιών στον πίνακα.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε

Πρέπει να είστε μέλος για να αφήσετε σχόλιο

Δημιουργία λογαριασμού

Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!

Δημιουργία νέου λογαριασμού

Σύνδεση

Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.

Συνδεθείτε τώρα
  • Δημιουργία νέου...