360°BlogsBooksAnswers
Login
phigita.net! Homepage
Preferences | Friday, 03 September 2010, 13:15 EEST
Question
Posted: Monday October 19, 2009
Number of answers: 10

MySQL - Division

Καλησπέρα :)

Κάνω μια εργασία στις βάσεις δεδομένων όπου μας ζητάει να δώσουμε τα αποτελέσματα κάποιων queries σε relation algebra. Μας έχουν δώσει μια εργασία που ζητάει να κάνουμε διαίρεση (δηλαδή, να απομονώσουμε τα στοιχεία του πίνακα Α τα οποία έχουν ζεύγος για κάθε τιμή του πίνακα Β). Εγώ αντί να παραδώσω μόνο τα αποτελέσματα αποφάσισα να τα τρέχω κιόλας, έτσι πέρασα τα στοιχεία σε μια βάση και άρχισα να γράφω τις σχέσεις σε MySQL. Γιατί πιστεύω θα μου χρειαστεί πιο μετά.

Το πρόβλημα: Δεν ξέρω πως να κάνω την διαίρεση (νομίζω δεν υποστηρίζεται) και θέλω να μάθω! (Το βιβλίο δεν λέει κάτι, ούτε και βρήκα κάτι καλό στο διαδίκτυο).

Ευχαριστώ :)

-- Γιώργος Μιχαήλ ~xeirwn

Reader's Answers

Ενδιαφέρον! Έκανα μια μίνι αναζήτηση χωρίς να βρω λύση, αλλά βρήκα αυτό που με βοήθησε να καταλάβω τι είναι η διαίρεση. Δεν έχω μια απλή λύση, αλλά θα ήθελα να δω την απάντηση.

-- Κωνσταντίνος Κωνσταντίνου ~constandinos, October 19, 2009



Φαίνεται ότι δεν υποστηρίζεται απευθείας από το πρότυπο SQL.

Για δες εδώ: http://www2.cs.uh.edu/~marek/notes/lecture7/sld015.htm

Το αντιγράφω:

      Find sailors who have reserved all boats.

      SELECT S.sname                            Find all sailors …
      FROM    Sailors S
      WHERE NOT EXISTS                      Such that there is no boat …
            (SELECT B.bid FROM Boats B
              WHERE NOT EXISTS              That they did not reserve
                    (SELECT R.bid
                    FROM Reserves R
                    WHERE R.bid=B.bid AND R.sid=S.sid))

Φυσικά υπάρχουν και άλλες προσεγγίσεις. Το παραπάνω θα μπορούσε ίσως να υλοποιηθεί και με COUNT DISTINCT:

  SELECT S.sname
  FROM Sailors S
  Where (SELECT COUNT(DISTINCT R.BID)  Count different boats
              FROM Reserves R                            reserved by this sailor
              WHERE R.SID = S.SID)
              =
            (SELECT COUNT(DISTINCT B.BID) FROM Boats B) Count all boats

Δηλαδή, μετρώ πόσα διαφορετικά πλοία έχει κρατήσει ο ναύτης (αφού μπορεί να κρατήσει ένα πλοίο περισσότερο από μια φορά), και συγκρίνω με το πόσα διαφορετικά πλοία υπάρχουν. Όταν ο αριθμός είναι ο ίδιος, τότε ο ναύτης έχει κάνει κράτησει σε όλα τα πλοία. Βέβαια, αυτό με την υπόθεση ότι τα Boats.BID και Sailors.SID είναι primary keys στα δύο tables αντίστοιχα.

-- Χρίστος Ευαγγέλου ~christose, October 19, 2009



Καλημέρα :)

Ευχαριστώ για την απάντηση.

Τις έχω σκεφτεί λίγο και μου φαίνονται λογικές και με λίγη αλλαγή θα ταιριάζουν με το πρόβλημα που θέλω να λύσω.

Μια παρατήρηση, δεν ξέρω κατά πόσο χρειάζεται, η δεύτερη λύση θα δώσει λάθος αποτέλεσμα στην περίπτωση όπου έχουμε: 4 βάρκες διαθέσιμες και θέλουμε να βρούμε τους βαρκάρηδες που έχουν χρησιμοποιήσει μόνο τις πρώτες τρεις. Το πρόβλημα: αν κάποιος βαρκάρης είχε χρησιμοποιήσει όλες τις βάρκες από το 2 μέχρι το 4 αντί για τις βάρκες 1 μέχρι 3, πάλι θα ήταν στα αποτελέσματα.

Ο τρόπος που θα γινόταν με τον τελεστή διαίρεσης: θα δημιουργούσαμε ένα view με τις βάρκες που θέλουμε μόνο και θα την εκτελούσαμε.

Υποθέτω εδώ πρέπει να προηγηθεί ένα select για να κόψουμε τις άσχετες βάρκες έξω.

Και πάλι ευχαριστώ!!

Θα το δοκιμάσω απόψε :)

-- Γιώργος Μιχαήλ ~xeirwn, October 19, 2009



Γειααα :). Άργησα να μπω phigita σήμερα. Νομίζω τούτο δουλεύει:

Select Sailor.SailorID

from

Boat cross join Sailor left outer join Reservation on (Boat.BoatID=Reservation.BoatID and Sailor.SailorID=Reservation.SailorID)

group by Sailor.SailorID

having MIN(isnull( Reservation.ReservationID,-1)) > -1

Τωρά δεν ξέρω κατά πόσο είναι συνετό να χρησιμοποιήσεις cross join και group by και having στην ίδια πρόταση, αλλά και το Exists και το Not Exists νομίζω είναι εξίσου "ακριβά" :).

-- Γεωργία Γεωργίου ~georgia, October 19, 2009



Φαίνεται και αυτός ο τρόπος να δουλεύει. Βέβαια θα πρέπει να ισχύουν κάποιες υποθέσεις όπως ότι το column RID παίρνει πάντα τιμές > -1 (που νομίζω συνήθως ισχύει σε αυτόματα-αυξανόμενα πεδία ή key sequences).

Όμως, νομίζω ότι το CROSS JOIN (Καρτεσιανό γινόμενο) είναι ανησυχητικό, επειδή αν έχουν τα tables Μ και N rows αντίστοιχα, το αποτέλεσμα του Καρτεσιανού γινόμενου είναι MxN rows. Πες M=1000, Ν=1000. Αμέσως το subquery θα επιστρέψει ένα εκατομμύριο rows!

Μερικά πειράματα σε PostgreSQL, για όσους ενδιαφέρονται. Το πρώτο query φαίνεται να είναι το πιο γρήγορο.

(Unique indexes στα: BOATS.BID, SAILORS.SID. Index στα: RESERVES.SID,RESERVES.BID,RESERVES.<SID,BID>)

        testdb=# SELECT
        testdb-#  (Select Count(1) From SAILORS) as countSailors,
        testdb-#  (Select Count(1) From BOATS) as countBoats,
        testdb-#  (Select Count(1) From RESERVES) as countReserves;
        countsailors | countboats | countreserves
        - – – – – – – -+- – – – – – -+- – – – – – – –
                4048 |      7236 |        73018
        (1 row)

        testdb=# \timing
        Timing is on.
        testdb=#
        testdb=#  Select S.name From SAILORS s Where Not Exists (
        testdb(#    Select BID From BOATS b Where Not Exists (
        testdb(#        Select R.BID From RESERVES r Where R.BID = B.BID And R.SID = S.SID));
        name
        - - - - - -
        (0 rows)

        Time: 47.000 ms

        testdb=#  Select S.name
        testdb-#  From SAILORS S
        testdb-#  Where (Select COUNT(DISTINCT R.BID)  From RESERVES R Where R.SID = S.SID)
        testdb-#              = (Select COUNT(B.BID) From BOATS B);
        name
        - - - - - -
        (0 rows)

        Time: 125.000 ms

        testdb=#
        testdb=#  Select S.SID
        testdb-#  From BOATS B Cross Join SAILORS S Left Outer Join RESERVES R On (B.BID=R.BID And S.SID=R.SID)
        testdb-#  Group By S.SID
        testdb-#  Having Min(Coalesce(R.RID,-1)) > -1;
        sid
        - - - - -
        (0 rows)

        Time: 118187.000 ms

-- Χρίστος Ευαγγέλου ~christose, October 20, 2009



wow! Time: 118187.000 ms!!! :D Νομίζω την παραπάνω ζημιά κάμνει την το cross join και το having. Αλλά θεωρητικά δουλεύει ;) .

Χρίστο, πώς έβγαλες αυτούς τους αριθμούς; Υπάρχει αντίστοιχο εργαλείο για MS SQL ή Oracle αν γνωρίζεις;

-- Γεωργία Γεωργίου ~georgia, October 20, 2009



Τα queries τα εκτέλεσα χρησιμοποιόντας το psql. Το psql είναι το standard client-εργαλείο το οποίο συνοδεύει την PostgreSQL. Μια από τις εντολές που υποστηρίζει, η \timing, καταμετρεί τον χρόνο εκτέλεσης ενός query. Εξόσων γνωρίζω η Oracle έχει ως αντίστοιχο εργαλείο το SQL Plus ενώ ο MSSQL τα isql, osql και sqlcmd.

Η PostgreSQL μπορεί να δείξει και το πλάνο εκτέλεσης για ένα query πολύ εύκολα, χρησιμοποιώντας την εντολή EXPLAIN (π.χ. "EXPLAIN Select * From ZOLOS"). Επειδή όμως παράγει πολύ output, και επίσης επειδή δεν ξέρω να εξηγώ το output πολύ καλά, δεν αντέγραψα τα πλάνα εκτέλεσης εδώ. Η Oracle υποστηρίζει επίσης την εμφάνιση πλάνου εκτέλεσης (αν και με διαφορετικές εντολές). Δεν ξέρω για τον MS SQL, αλλά λογικά θα πρέπει επίσης να το υποστηρίζει.

-- Χρίστος Ευαγγέλου ~christose, October 20, 2009



Στο ms SQL server mgmt studio, όταν κάνεις ένα νέο query, δεξί click και 'include actual execution plan'. Είναι βοηθητικό, σου παρουσιάζει αναλυτικά κόστα χρόνους κ.τ.λ για κάθε Nested Loop. Χρησιμοποιώ το συνέχεια.

-- Ελένη Γεωργίου ~geleni, October 20, 2009



Ευχαριστώ! :)

-- Γεωργία Γεωργίου ~georgia, October 21, 2009



Για Oracle υπάρχει το Oracle SQL Developer που για κάθε SQL σου αναλύει η κάθε γραμμή που έγραψες πόσο κοστίζει (F6)

-- Ανδρέας Φλωρίδης ~flo, October 30, 2009




Copyright © 2000-2010 phigita. All rights reserved.
Powered by the blood, sweat, and tears of the phigita.net community.