Καλησπέρα :)
Κάνω μια εργασία στις βάσεις δεδομένων όπου μας ζητάει να δώσουμε τα αποτελέσματα κάποιων queries σε relation algebra. Μας έχουν δώσει μια εργασία που ζητάει να κάνουμε διαίρεση (δηλαδή, να απομονώσουμε τα στοιχεία του πίνακα Α τα οποία έχουν ζεύγος για κάθε τιμή του πίνακα Β). Εγώ αντί να παραδώσω μόνο τα αποτελέσματα αποφάσισα να τα τρέχω κιόλας, έτσι πέρασα τα στοιχεία σε μια βάση και άρχισα να γράφω τις σχέσεις σε MySQL. Γιατί πιστεύω θα μου χρειαστεί πιο μετά.
Το πρόβλημα: Δεν ξέρω πως να κάνω την διαίρεση (νομίζω δεν υποστηρίζεται) και θέλω να μάθω! (Το βιβλίο δεν λέει κάτι, ούτε και βρήκα κάτι καλό στο διαδίκτυο).
Ευχαριστώ :)
Ενδιαφέρον! Έκανα μια μίνι αναζήτηση χωρίς να βρω λύση, αλλά βρήκα αυτό που με βοήθησε να καταλάβω τι είναι η διαίρεση. Δεν έχω μια απλή λύση, αλλά θα ήθελα να δω την απάντηση.
-- Κωνσταντίνος Κωνσταντίνου ~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