Σε αυτό το μέρος της σειράς των posts μας για εισαγωγή στην T-SQL θα ασχοληθούμε αναλυτικότερα με το SELECT statement. Για το σκοπό αυτό θα χρησιμοποιήσουμε την Northwind sample database.
Απλουστευμένα η βασική δομή ενός SELECT statement είναι η παρακάτω:
SELECT select_list
[FROM table_source]
[WHERE search_condition]
[GROUP BY group_by_expression]
[HAVING search_condition]
[ORDER BY order_expression [ASC|DESC]]
Ξεκινώντας με αυτό που ήδη έχουμε δει στο προηγούμενο post ας δούμε αρχικά όλα τα περιεχόμενα του πίνακα Products (query 1). Στο select list παραθέτουμε τις στήλες τις οποίες θέλουμε να διαλέξουμε. Το * που έχουμε χρησιμοποιήσει ως τώρα αντιστοιχεί στο «όλες τις στήλες». Αντί για αυτό σε ένα select list παραθέτουμε τις στήλες που θέλουμε να επιστρέψει το query μας χωρισμένες με “,” όπως βλέπουμε στο query 2. Εκτός από ονόματα στηλών μπορούμε να έχουμε και κάποιο υπολογισμό πάνω στις τιμές κάποιας/κάποιων στηλών. Για παράδειγμα στο query 3 βλέπουμε ότι μπορούμε να υπολογίσουμε το ποσό που αντιστοιχεί στο ΦΠΑ θεωρώντας οτι όλα μας τα προϊόντα εμπίπτουν στην κατηγορία του 23%.
-- QUERY 1: All info for Products
SELECT *
FROM Products;
GO
--QUERY 2: SELECT retrieving specific columns from a table
SELECT ProductName,UnitPrice
FROM Products;
GO
--QUERY 3: Using calculations with SELECT
SELECT ProductName, UnitPrice,
(UnitPrice*0.23) AS [UnitTaxPrice]
FROM Products;
GO
Σημειώνεται ότι για τις στήλες που περιλαμβάνονται στο select list, αν δεν ορίσουμε κάτι άλλο, το όνομα της στήλης του dataset που παίρνουμε σαν αποτέλεσμα είναι το ίδιο με το όνομα της στήλης. Για να το αλλάξουμε αυτό ή για να ορίσουμε όνομα στις στήλες του dataset που δεν θα πάρουν by default όνομα, όπως η 3η στήλη του query 3, χρησιμοποιούμε το keyword AS ακολουθούμενο από το όνομα που θέλουμε να δώσουμε.
CASE
Εκτός από απλό υπολογισμό, όπως αυτός που είδαμε, μπορούμε να έχουμε και αποτέλεσμα υπό συνθήκη πάνω στις τιμές της στήλης χρησιμοποιώντας το CASE. Έστω ότι κάποιες κατηγορίες προϊόντων έχουν διαφορετικό συντελεστή ΦΠΑ. Τότε ο υπολογισμός που κάναμε στο query 3 θα αλλάξει σε αυτόν που βλέπουμε στο query 4. Αυτό είναι ένα απλό CASE expression, δηλαδή βασίζεται στης τιμές μίας μόνο στήλης τις οποίες συγκρίνουμε με διακριτές τιμές. Μια διαφορετική σύνταξη για CASE expressions είναι τα searched CASE expressions, όπως αυτό του query 5. Για παράδειγμα, έστω ότι θέλουμε να δείξουμε την διαθεσιμότητα των προϊόντων ανάλογα με το πόσα είναι στο stock. Η απόφαση αυτή στηρίζεται σε περιοχές τιμών (μικρότερο του 20, μεταξύ 20 και 90, μεγαλύτερο του 90) και έτσι η σύνταξη αλλάζει σε αυτή που φαίνεται στο query 5. Στο ίδιο query βλέπουμε και έναν εναλλακτικό τρόπο να ορίσει κανείς alias για το αποτέλεσμα του υπολογισμού.
--QUERY 4:Using simple CASE expression
SELECT ProductName, UnitPrice ,
CASE CategoryId
WHEN 1 THEN UnitPrice*0.18
WHEN 2 THEN UnitPrice*0.09
ELSE UnitPrice*0.23
END AS [UnitTaxPrice],
CategoryID
FROM Products;
GO
--QUERY 5:Using searched CASE expression
SELECT ProductName , UnitsInStock,
[Stock Availability] =
CASE
WHEN UnitsInStock < 20 THEN 'Low'
WHEN UnitsInStock > 90 THEN 'High'
ELSE 'Normal'
END
FROM Products;
GO
DISTINCT
Έστω ότι από τον πίνακα των πελατών θέλουμε να δούμε τις πόλεις στις οποίες βρίσκονται αυτοί. Την πληροφορία αυτή μπορούμε να την πάρουμε από την στήλη City με το query 6:
-- Query 6: Customers' cities
SELECT City
FROM Customers;
-- 91 rows
-- Query 7: Using distinct
SELECT DISTINCT City
FROM Customers;
GO
-- 69 rows
Αυτό μας επιστρέφει 91 γραμμές. Πράγματι μπορούμε να δούμε τις πόλεις στις οποίες έχουμε πελάτες, όμως αυτοί δεν βρίσκονται σε 91 διαφορετικές πόλεις καθώς κάποιες εμφανίζονται πολλαπλές φορές στο αποτέλεσμα. Θα ήταν λοιπόν πιο λογικό για να πάρουμε την πληροφορία που χρειαζόμαστε να χρησιμοποιήσουμε το keyword DISTINCT ώστε στο αποτέλεσμα μας να εμφανίζεται μια φορά η κάθε πόλη. Με το query 7 λοιπόν, θα πάρουμε τις 69 διαφορετικές πόλεις στις οποίες έχουμε πελάτες.
ORDER BY
Στα παραπάνω παραδείγματα δεν μας ενδιέφερε η σειρά των αποτελεσμάτων. Για παράδειγμα, στο query 2 τα προϊόντα δεν έχουν κάποια συγκεκριμένη σειρά. Αν θέλουμε να είναι ταξινομημένα με βάση το όνομα του προϊόντος, θα πρέπει να προσθέσουμε το ORDER BY clause στο select statement και να δηλώσουμε επιθυμητό τρόπο ταξινόμησης (query 8). Σε αυτή την περίπτωση η ταξινόμηση βασίζεται στις τιμές μίας μόνο στήλης και εφόσον δεν διευκρινίζουμε κάτι διαφορετικό, η ταξινόμηση αυτή είναι αύξουσα. Δίπλα από την στήλη με βάση την οποία ταξινομούμε βάζουμε προαιρετικά το keyword ASC ή DESC για να δηλώσουμε ότι θέλουμε αύξουσα ή φθίνουσα ταξινόμηση αντιστοίχως. Στο query 9 βλέπουμε παράδειγμα φθίνουσας ταξινόμησης με βάση την τιμή του προϊόντος, δηλαδή θα δούμε τα προϊόντα από το ακριβότερο προς το φθηνότερο. Στα query 10 και query 11 βλέπουμε δύο ακόμα παραδείγματα συνδυάζοντας τιμές από δύο στήλες για να προσδιορίσουμε την ταξινόμηση.
-- Query 8: order by ascending product name
SELECT ProductName, UnitPrice
FROM Products
ORDER BY ProductName;
GO
-- Query 9:order by descending price
SELECT ProductName, UnitPrice
FROM Products
ORDER BY UnitPrice DESC;
GO
-- Query 10: order by based on 2 columns
SELECT ProductName, UnitsInStock, UnitsOnOrder
FROM Products
ORDER BY UnitsInStock ASC, UnitsOnOrder DESC;
GO
-- Query 11: order by remaining units in stock
SELECT ProductName, UnitsInStock, UnitsOnOrder
FROM Products
ORDER BY UnitsInStock - UnitsOnOrder;
GO
WHERE
Όπως είδαμε και στο προηγούμενο post, χρησιμοποιούμε το where clause σαν φίλτρο για να περιορίσουμε τις γραμμές του αποτελέσματος. Παρακάτω βλέπουμε μερικά παραδείγματα χρήσης του. Στο πρώτο (query 12) παίρνουμε τα προϊόντα που έχουν αποσυρθεί από την αγορά, ενώ στο δεύτερο (query 13) βλέπουμε ποια από αυτά υπάρχουν ακόμη στην αποθήκη. Με το query 14 παίρνουμε τα προϊόντα που έχουν απόθεμα από 31 ως και 85 τεμάχια, με χρήση της συνθήκης between. Η συνθήκη αυτή, όπως είναι φανερό, είναι ισοδύναμη με το να είχαμε γράψει UnitsInStock >= 31 and UnitsInStock <= 85. Τέλος στο query 15, βλέπουμε την χρήση μιας συνθήκης με τον τελεστή like. Στο παράδειγμά μας, θέλουμε να πάρουμε όλα τα προϊόντα που το όνομά τους ξεκινάει από “Queso” οπότε το αποτέλεσμα θα είναι 2 προϊόντα, τα: Queso Cabrales και Queso Manchego La Pastora. Το like χρησιμοποιείται για να ελεγχθεί αν ένα string ταιριάζει με το δεδομένο pattern. (Περισσότερες πληροφορίες για το LIKE: LIKE (Transact-SQL) , [](Wildcard-Character(s) to Match) (Transact-SQL))
-- Query 12: discontinued products
SELECT ProductName, UnitsInStock
FROM Products
WHERE Discontinued = 1;
GO
-- Query 13: discontinued products in stock
SELECT ProductName, UnitsInStock
FROM Products
WHERE Discontinued = 1 AND UnitsInStock > 0;
GO
-- Query 14: products with units in stock in specified range
SELECT ProductName, UnitsInStock,Discontinued
FROM Products
WHERE UnitsInStock BETWEEN 31 AND 85
ORDER BY UnitsInStock;
GO
-- Query 15: products with name starting with "Queso"
SELECT ProductName
FROM Products
WHERE ProductName LIKE 'Queso%';
GO
Πριν κλείσουμε αυτό το κομμάτι θα πρέπει να δούμε τι γίνεται όταν στις συνθήκες μας εμπλέκονται NULL τιμές. Αν δει κανείς τα δεδομένα του πίνακα Suppliers θα δει ότι υπάρχουν 20 γραμμές που η στήλη Region έχει NULL τιμή. Αν όμως χρησιμοποιούσαμε το query 16 για να πάρουμε αυτές τις 20 γραμμές, θα βλέπαμε ότι το result set που επιστρέφεται είναι άδειο. Για να πάρουμε τις 20 γραμμές που ήταν οι επιθυμητές θα πρέπει να χρησιμοποιήσουμε το query 17.
-- Query 16: WRONG
SELECT CompanyName, City, Region
FROM Suppliers
WHERE Region = NULL
ORDER BY City;
GO
-- Query 16: RIGHT
SELECT CompanyName, City, Region
FROM Suppliers
WHERE Region IS NULL
ORDER BY City;
GO
Σημ: Για την συμπεριφορά της σύγκρισης με NULL τιμές υπάρχει στον SQL Server, μέχρι στιγμής, ένα option της βάσης που λέγεται ANSI_NULLS και καθορίζει αν η συμπεριφορά των nulls θα συμφωνεί με το ISO ή όχι. Η συμπεριφορά που περιγράφηκε παραπάνω προϋποθέτει ότι αυτό είναι ON. Σε γενικές γραμμές θα πρέπει να είναι ON και μάλιστα σε επόμενες εκδόσεις του SQL Server θα είναι πάντοτε ON (reference).