-- Μια βάση δεδομένων είναι ένας τρόπος οργάνωσης δεδομένων έτσι ώστε να μπορείτε να εκτελέσετε τέσσερις λειτουργίες σε αυτήν
-- -δημιουργία
-- -ανάγνωση
-- -ενημέρωση
-- -διαγραφή
-- Ένα σύστημα διαχείρισης βάσεων δεδομένων (DBMS) είναι ένας τρόπος αλληλεπίδρασης με μια βάση δεδομένων χρησιμοποιώντας 
-- μια γραφική διεπαφή ή μια γλώσσα κειμένου.
-- Παραδείγματα DBMS: MySQL, Oracle, PostgreSQL, SQLite, Microsoft Access, MongoDB κ.λπ.
-- Η επιλογή ενός DBMS βασίζεται σε παράγοντες όπως
-- -Κόστος: ιδιόκτητο έναντι δωρεάν λογισμικού,
-- -Μέγεθος υποστήριξης: δωρεάν και ανοιχτού κώδικα λογισμικό όπως MySQL, PostgreSQL και SQLite συνοδεύεται από το μειονέκτημα ότι πρέπει να ρυθμίσετε μόνοι σας τη βάση δεδομένων,
-- -Βάρος: τα πιο πλήρως εξοπλισμένα συστήματα όπως η MySQL ή η PostgreSQL είναι βαρύτερα και απαιτούν περισσότερους υπολογισμούς για να λειτουργήσουν από συστήματα όπως η SQLite.


-- Σε αυτό το μάθημα, θα εργαστούμε με την PostgreSQL.


-- Η SQL σημαίνει Structured Query Language. Είναι μια γλώσσα που χρησιμοποιείται για την αλληλεπίδραση με βάσεις δεδομένων, 
-- μέσω της οποίας μπορείτε να δημιουργήσετε, να διαβάσετε, να ενημερώσετε και να διαγράψετε δεδομένα σε μια βάση δεδομένων. 
-- Μερικές σημαντικές επισημάνσεις σχετικά με την SQL
-- -είναι δομημένη, όπως θα δούμε σε αυτό το μάθημα,
-- -έχει κάποιες λέξεις-κλειδιά που μπορούν να χρησιμοποιηθούν για αλληλεπίδραση με τη βάση δεδομένων και
-- -είναι μια γλώσσα ερωτημάτων — μπορεί να χρησιμοποιηθεί για να θέσει ερωτήσεις σε δεδομένα μέσα σε μια βάση δεδομένων.


-- Γνωριμία με το PGAdmin GUI...


-- Ποια δεδομένα υπάρχουν στην πραγματικότητα στη βάση δεδομένων μας; 
-- Για να απαντήσουμε σε αυτό, θα χρησιμοποιήσουμε την πρώτη λέξη-κλειδί SQL, SELECT, 
-- η οποία μας επιτρέπει να επιλέξουμε ορισμένες (ή όλες) τις γραμμές από έναν πίνακα μέσα στη βάση δεδομένων.


SELECT * 
FROM "books";


-- Το αποτέλεσμα που λαμβάνουμε περιέχει όλες τις στήλες όλων των γραμμών σε αυτόν τον πίνακα, που είναι πολλά δεδομένα. 
-- Μπορούμε να το απλοποιήσουμε επιλέγοντας μια συγκεκριμένη στήλη, ας πούμε τον τίτλο, από τον πίνακα.


SELECT "title" 
FROM "books";


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


SELECT "title", "authors" 
FROM "books";


-- Η PostgreSQL κάνει διάκριση πεζών-κεφαλαίων σε ονόματα πινάκων, πεδίων, αλφαρηθμητικών, κ.λπ. 
-- Στις λέξεις της SQL δεν κάνει διάκριση πεζών-κεφαλαίων. Συνήθως όμως οι SQL λέξεις-κλειδιά γράφονται με κεφαλαία γράμματα. Αυτό είναι ιδιαίτερα χρήσιμο για τη βελτίωση της αναγνωσιμότητας μεγαλύτερων ερωτημάτων.


-- Αν μια βάση δεδομένων είχε εκατομμύρια γραμμές, ίσως να μην είχε νόημα να επιλέξουμε όλες τις γραμμές της. 
-- Αντίθετα, ίσως να θέλαμε απλώς να ρίξουμε μια ματιά στα δεδομένα που περιέχει. 
-- Χρησιμοποιούμε τη λέξη-κλειδί SQL LIMIT για να καθορίσουμε τον αριθμό των γραμμών στην έξοδο του ερωτήματος.
-- Οι τίτλοι ταξινομούνται με τον ίδιο τρόπο στην έξοδο αυτού του ερωτήματος όπως και στη βάση δεδομένων


SELECT "title" 
FROM "books" 
LIMIT 10;


-- Η λέξη-κλειδί WHERE χρησιμοποιείται για την επιλογή γραμμών με βάση μια συνθήκη. 
-- Θα εμφανίσει τις γραμμές για τις οποίες η καθορισμένη συνθήκη είναι αληθής. 
-- Το 2008 δεν είναι σε εισαγωγικά επειδή είναι ακέραιος αριθμός, όχι συμβολοσειρά ή αναγνωριστικό


SELECT "title", "authors" 
FROM "books" 
WHERE "publication_year" = 2008; 


-- Οι τελεστές που μπορούν να χρησιμοποιηθούν για τον καθορισμό συνθηκών στην SQL είναι 
-- = (“ίσο με”), != (“όχι ίσο με”) και <> (επίσης “όχι ίσο με”).


-- Για να επιλέξουμε τα βιβλία που δεν είναι γραμμένα σε en-US, μπορούμε να εκτελέσουμε το ερώτημα
-- Το en-US βρίσκεται σε μονά εισαγωγικά επειδή είναι μια συμβολοσειρά SQL και όχι ένα αναγνωριστικό.


SELECT "title", "language_code" 
FROM "books" 
WHERE "language_code" <> 'en-US';


SELECT "title", "language_code" 
FROM "books" 
WHERE NOT "language_code" = 'en-US';


-- Για να συνδυάσουμε συνθήκες, μπορούμε να χρησιμοποιήσουμε τις λέξεις-κλειδιά SQL AND και OR. 
-- Μπορούμε επίσης να χρησιμοποιήσουμε παρενθέσεις για να υποδείξουμε πώς να συνδυάσουμε τις συνθήκες 
-- σε μια σύνθετη πρόταση υπό όρους.


SELECT "title", "authors" 
FROM "books" 
WHERE "publication_year" = 2008 OR "publication_year" = 2013;


-- Για να επιλέξετε τα βιβλία που εκδόθηκαν το 2008 ή το 2013 και δεν γράφτηκαν σε en-US
-- Εδώ, οι παρενθέσεις υποδεικνύουν ότι το OR θα πρέπει να αξιολογηθεί πριν από το AND.


SELECT "title", "authors" 
FROM "books" 
WHERE ("publication_year" = 2008 OR "publication_year" = 2013) AND "language_code" != 'en-US'; 


-- Είναι πιθανό οι πίνακες να έχουν δεδομένα που λείπουν. 
-- Ο τύπος NULL χρησιμοποιείται για να υποδείξει ότι ορισμένα δεδομένα δεν έχουν τιμή ή δεν υπάρχουν στον πίνακα.


SELECT "title", "language_code" 
FROM "books" 
WHERE "language_code" IS NULL;

 


SELECT "title", "language_code" 
FROM "books" 
WHERE "language_code" != 'en-US'; 
SELECT "title", "language_code" 
FROM "books" 
WHERE "language_code" IS NOT NULL;


-- Η λέξη-κλειδί LIKE χρησιμοποιείται για την επιλογή δεδομένων που αντιστοιχούν περίπου στην καθορισμένη συμβολοσειρά. 
-- Για παράδειγμα, το LIKE θα μπορούσε να χρησιμοποιηθεί για την επιλογή βιβλίων 
-- που έχουν μια συγκεκριμένη λέξη ή φράση στον τίτλο τους.
-- Το LIKE συνδυάζεται με τους τελεστές % (αντιστοιχεί σε οποιονδήποτε χαρακτήρα γύρω από μια δεδομένη συμβολοσειρά) 
-- και _ (αντιστοιχεί σε έναν μόνο χαρακτήρα).
-- Για να επιλέξουμε τα βιβλία που έχουν τη λέξη «love» στους τίτλους τους, μπορούμε να εκτελέσουμε


SELECT "title" 
FROM "books" 
WHERE "title" LIKE '%love%';


-- Για να επιλέξετε τα βιβλία των οποίων ο τίτλος αρχίζει με “The”


SELECT "title" 
FROM "books" 
WHERE "title" LIKE 'The%';


-- Το παραπάνω ερώτημα μπορεί επίσης να επιστρέψει βιβλία των οποίων οι τίτλοι αρχίζουν με “Their” ή “They”. 
-- Για να επιλέξετε μόνο τα βιβλία των οποίων οι τίτλοι αρχίζουν με τη λέξη “The”, μπορούμε να προσθέσουμε ένα κενό.


SELECT "title" 
FROM "books" 
WHERE "title" LIKE 'The %';


-- Δεδομένου ότι υπάρχει ένα βιβλίο στον πίνακα του οποίου το όνομα περιέχει είτε “Show” ή “Snow”, μπορούμε να εκτελέσουμε


SELECT "title" 
FROM "books" 
WHERE "title" LIKE '%S_ow';


-- Μπορούμε να χρησιμοποιήσουμε πολλαπλά % ή _ σε ένα ερώτημα


SELECT "title" 
FROM "books" 
WHERE "title" LIKE 'The%Sleep%';


SELECT "title"
FROM "books" 
WHERE "title" LIKE 'the%Sleep%'; -- διάκριση πεζών-κεφαλαίων


-- Αν γνωρίζαμε ότι υπήρχε ένα βιβλίο στον πίνακα του οποίου ο τίτλος αρχίζει με «Τ» και έχει τέσσερα γράμματα, 
-- μπορούμε να προσπαθήσουμε να το βρούμε εκτελώντας


SELECT "title" 
FROM "books" 
WHERE "title" LIKE 'T____';


-- Αν θέλουμε να εντοπίσουμε τίτλους που να περιέχουν κάποιον από τους 
-- ειδικούς χαρακτήρες % ή _ χρησιμοποιούμε το escape χαρακτήρα \


SELECT *
FROM "books"
WHERE "title" LIKE '%\%%';


-- Μπορούμε επίσης να χρησιμοποιήσουμε τους τελεστές <, >, <= και >= στις συνθήκες μας για να αντιστοιχίσουμε ένα εύρος τιμών.


SELECT "title", "publication_year" 
FROM "books" 
WHERE "publication_year" >= 2008 AND "publication_year" <= 2013;


-- Ένας άλλος τρόπος για να έχετε τα ίδια αποτελέσματα είναι να χρησιμοποιήσετε τις λέξεις-κλειδιά BETWEEN και AND 
-- για να καθορίσετε εύρη που περιλαμβάνουν όλα τα δεδομένα.


SELECT "title", "publication_year" 
FROM "books" 
WHERE "publication_year" BETWEEN 2008 AND 2013;


-- Για να επιλέξετε τα βιβλία που έχουν βαθμολογία 4.0 ή υψηλότερη


SELECT "title", "average_rating" 
FROM "books" 
WHERE "average_rating" > 4.0;


-- Για να περιοριστούν περαιτέρω τα επιλεγμένα βιβλία με βάση τον αριθμό των αξιολογήσεων και να υπάρχουν μόνο τα βιβλία 
-- με τουλάχιστον 30.000 αξιολογήσεις


SELECT "title", "average_rating", "rating_count" 
FROM "books" 
WHERE "average_rating" > 4.0 AND "rating_count" > 30000;


-- Για να επιλέξετε τα βιβλία που έχουν λιγότερες από 100 αξιολογήσεις


SELECT "title", "rating_count" 
FROM "books" 
WHERE "rating_count" < 100;


-- Η λέξη-κλειδί ORDER BY μας επιτρέπει να οργανώσουμε τις γραμμές που επιστρέφονται με κάποια καθορισμένη σειρά.
-- Το ακόλουθο ερώτημα επιλέγει τα 10 βιβλία που βρίσκονται στη βάση δεδομένων μας με βάση την βαθμολογία τους


SELECT "title", "average_rating" 
FROM "books" 
ORDER BY "average_rating" LIMIT 10; -- επιλέγει αύξουσα σειρά από προεπιλογή


-- για να επιλέξετε τα 10 κορυφαία βιβλία


SELECT "title", "average_rating" 
FROM "books" 
ORDER BY "average_rating" DESC LIMIT 10;


-- για να επιλέξετε τα 10 κορυφαία βιβλία με βάση την βαθμολογία και επίσης 
-- να συμπεριλάβετε τον αριθμό των αξιολογήσεων ως σημείο ισοβαθμίας
-- για κάθε στήλη στον όρο ORDER BY, μπορούμε να καθορίσουμε αύξουσα ή φθίνουσα σειρά


SELECT "title", "average_rating", "rating_count" 
FROM "books" 
ORDER BY "average_rating" DESC, "rating_count" DESC 
LIMIT 10;


-- Για να ταξινομήσουμε (λεξικογραφικά) τα βιβλία με βάση τον τίτλο τους αλφαβητικά, 
-- μπορούμε να χρησιμοποιήσουμε την εντολή ORDER BY


SELECT "title" 
FROM "books" 
ORDER BY "title";


SELECT "title" 
FROM "books" 
ORDER BY "title" DESC;


-- Η PostgreSQL χρησιμοποιεί Τύπους Δεδομένων. Είναι μια βάση δεδομένων με ισχυρούς τύπους, που σημαίνει 
-- ότι επιβάλλει αυστηρούς κανόνες σχετικά με το είδος των δεδομένων που μπορούν να εισαχθούν σε μια στήλη:


-- 1. Αριθμητικοί Τύποι (Αριθμοί)
-- INTEGER (ή INT): Ο τυπικός ακέραιος αριθμός 4 byte. Κυμαίνεται από -2 δισεκατομμύρια έως +2 δισεκατομμύρια.
-- BIGINT: Ακέραιος 8 byte. Χρησιμοποιήστε το για αναγνωριστικά σε τεράστια σύνολα δεδομένων 
-- NUMERIC / DECIMAL: Ακριβής ακρίβεια. 
-- REAL / DOUBLE PRECISION: Αριθμοί "κινητής υποδιαστολής". Ταχύτεροι για μαθηματικά, αλλά μπορεί να έχουν 
-- μικρά σφάλματα στρογγυλοποίησης.
-- 2. Τύποι Χαρακτήρων (Κείμενο)Στην Postgres, δεν υπάρχει διαφορά απόδοσης μεταξύ αυτών των τριών, 
-- επομένως οι περισσότεροι χρησιμοποιούν απλώς TEXT.
-- TEXT: Μια συμβολοσειρά απεριόριστου μήκους (η καλύτερη προεπιλογή).
-- VARCHAR(n): Μεταβλητό μήκος με όριο. Εάν προσπαθήσετε να εισαγάγετε μια συμβολοσειρά μεγαλύτερη από n, αποτυγχάνει.
-- CHAR(n): Σταθερού μήκους, με κενό. Σπάνια χρησιμοποιείται σήμερα, εκτός εάν έχετε μια πολύ συγκεκριμένη 
-- απαίτηση παλαιού τύπου (όπως κωδικό χώρας 2 χαρακτήρων).
-- 3. Τύποι ημερομηνίας/ώρας
-- DATE: Μόνο η ημερομηνία (ΕΕΕΕ-ΜΜ-ΗΗ).
-- TIMESTAMP: Ημερομηνία και ώρα.
-- TIMESTAMPTZ: Επίγνωση ημερομηνίας, ώρας και ζώνης ώρας. Αυτό είναι το επαγγελματικό πρότυπο για τις στήλες "created_at".
-- INTERVAL: Αντιπροσωπεύει ένα χρονικό διάστημα (π.χ., "3 ημέρες" ή "1 μήνας 2 ώρες").
-- 4. Τύπος Boolean
-- BOOLEAN: Αποθηκεύει TRUE, FALSE ή NULL (άγνωστο). ξ=Η Postgres είναι έξυπνη και δέχεται επίσης συμβολοσειρές 
-- όπως  'yes', 'no', '1', '0', 't', ή 'f' κατά την εισαγωγή.


-- Εκτελούμε την ακόλουθη εντολή για να δημιουργήσουμε τον πίνακα my_table. 


CREATE TABLE "my_table" (
"id" INTEGER,
"name" TEXT
);


-- απόρριψη ενός πίνακα (και των δεδομένων του)


DROP TABLE "my_table";


-- Μπορούμε να χρησιμοποιήσουμε περιορισμούς πίνακα για να επιβάλουμε περιορισμούς σε ορισμένες τιμές στους πίνακές μας.
-- Για παράδειγμα, μια στήλη πρωτεύοντος κλειδιού πρέπει να έχει μοναδικές τιμές. Ο περιορισμός πίνακα που χρησιμοποιούμε 
-- για αυτό είναι PRIMARY KEY.
-- Περισσότεροι τύποι περιορισμών θα παρουσιαστούν στα επόμενα εργαστήρια


CREATE TABLE "my_table" (
"id" INTEGER PRIMARY KEY,
"name" TEXT
);


DROP TABLE "my_table";


-- εναλλακτική γραφή του περιορισμού


CREATE TABLE "my_table" (
"id" INTEGER,
"name" TEXT,
PRIMARY KEY("id")
);


-- Μετονομασία πίνακα


ALTER TABLE "my_table"
RENAME TO "my_table_1";


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


CREATE TABLE "my_table_2" (
"id" INTEGER,
"fname" TEXT,
"lname" TEXT,
"phone" TEXT,
PRIMARY KEY("fname", "lname", "phone")
);


-- Προσθήκη στήλης πίνακα


ALTER TABLE "my_table_1"
ADD COLUMN "description" TEXT;


-- Μετονομασία στήλης πίνακα


ALTER TABLE "my_table_1"
RENAME COLUMN "name" TO "area_name";


-- Απόρριψη στήλης πίνακα


ALTER TABLE "my_table_1"
DROP COLUMN "description";


SELECT * FROM "my_table_2";


INSERT INTO "my_table_2" ("id", "fname", "lname", "phone")
VALUES(1, 'Fotis', 'Kazasis', '988345678');


SELECT * FROM "my_table_2";


-- Μπορούμε να προσθέσουμε περισσότερες γραμμές στη βάση δεδομένων εισάγοντας πολλές φορές. 
-- Ωστόσο, η χειροκίνητη πληκτρολόγηση της τιμής του πρωτεύοντος κλειδιού (ως 1, 2, 3 κ.λπ.) μπορεί να οδηγήσει σε σφάλματα.
-- Για το λόγο αυτό χρησιμοποιούμε την αυτόματη αύξηση


CREATE TABLE authors (
    author_id SERIAL PRIMARY KEY,
    name TEXT
);


-- Τι εκτελείται από την PostgreSQL όταν δίνουμε τον τύπο SERIAL


-- 1
CREATE SEQUENCE authors_author_id_seq;
-- 2
CREATE TABLE authors (
    author_id INTEGER NOT NULL DEFAULT nextval('authors_author_id_seq') PRIMARY KEY,
    name TEXT
);
-- 3
ALTER SEQUENCE authors_author_id_seq OWNED BY authors.author_id;


-- 


SELECT * FROM "authors";


INSERT INTO "authors"("name")
VALUES('Fotis Kazasis');


SELECT * FROM "authors";


DROP TABLE "authors";


-- Ο ενδεδειγμένος τρόπος αυτόματης αύξησης 


CREATE TABLE authors (
    author_id INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
    name TEXT
);

-- Το σετ δεδομένων books.csv που χρησιμοποιείται Kaggle - Goodbooks-10k (https://www.kaggle.com/datasets/zygmunt/goodbooks-10k)


CREATE DATABASE "db2026_lab1_test";


CREATE TABLE "goodreads_raw" (
    "book_id" TEXT, "id" TEXT, "best_book_id" TEXT, "work_id" TEXT, "books_count" TEXT,
    "isbn" TEXT, "isbn13" TEXT, "authors" TEXT, "original_publication_year" TEXT,
    "original_title" TEXT, "title" TEXT, "language_code" TEXT, "average_rating" TEXT,
    "ratings_count" TEXT, "work_ratings_count" TEXT, "work_text_reviews_count" TEXT,
    "ratings_1" TEXT, "ratings_2" TEXT, "ratings_3" TEXT, "ratings_4" TEXT, "ratings_5" TEXT,
    "image_url" TEXT, "small_image_url" TEXT
);


-- Εισαγωγή των δεδομένων από το αρχείο books.csv είτε με γραφικό τρόπο είτε με την εντολή COPY (θα την δούμε σε άλλο εργαστήριο)
-- format = csv, encoding = UTF8, Header on, Delimiter = ',', Quote = '"', Escape='''


CREATE TABLE "books" (
    "book_id" SERIAL PRIMARY KEY,
    "title" TEXT,
    "authors" TEXT,
"language_code" VARCHAR(5),
    "isbn" VARCHAR(20),
    "publication_year" INT,
    "average_rating" NUMERIC,
    "rating_count" INT
);


-- κατά την εισαγωγή για όσες στήλες δεν έχουν τύπο TEXT γίνεται κατάλληλη μετατροπή τύπων (type casting)


INSERT INTO "books" ("title", "authors", "language_code", "isbn", "publication_year", "average_rating", "rating_count")
SELECT 
    "title", 
    "authors",
"language_code",
"isbn",
    "original_publication_year"::NUMERIC::INT,
"average_rating"::NUMERIC,
    "work_text_reviews_count"::INT
FROM "goodreads_raw";