Wprowadzenie: Czym jest SQL?
SQL (Structured Query Language) to język używany do zarządzania danymi w relacyjnych bazach danych. W kontekście agencji nieruchomości, baza danych pozwala na efektywne przechowywanie informacji o klientach, nieruchomościach, ofertach i transakcjach, a także automatyzację procesów biznesowych.
Ten przewodnik przedstawia najważniejsze możliwości SQL na przykładzie rzeczywistego systemu. Dowiesz się, jak zbudować solidną strukturę bazy danych, uprościć dostęp do danych za pomocą widoków, zwiększyć wydajność dzięki funkcjom, zautomatyzować procesy za pomocą triggerów i stworzyć zaawansowaną logikę biznesową w procedurach składowanych.
1. Struktura bazy danych
Fundament każdej aplikacji bazodanowej – poprawnie zaprojektowane tabele to podstawa działającego systemu. Nasza struktura wykorzystuje klucze główne (PRIMARY KEY) do unikalnej identyfikacji rekordów, klucze obce (FOREIGN KEY) do powiązań między tabelami oraz ograniczenia (CHECK constraints) do walidacji danych.
Dlaczego to ważne?
Ograniczenia na poziomie bazy danych gwarantują integralność danych niezależnie od tego, która aplikacja lub użytkownik wprowadza zmiany. Na przykład, nikt nie może dodać klienta poniżej 18 roku życia ani nieruchomości z ceną ujemną.
CREATE TABLE Klienci (
KlientID INT PRIMARY KEY IDENTITY(1,1),
Imie VARCHAR(50) NOT NULL,
Nazwisko VARCHAR(50) NOT NULL,
Email VARCHAR(100) UNIQUE NOT NULL,
Wiek INT CHECK (Wiek >= 18 AND Wiek <= 100)
);
CREATE TABLE Nieruchomosci (
NieruchomoscID INT PRIMARY KEY IDENTITY(1,1),
Adres VARCHAR(200) NOT NULL,
Cena DECIMAL(15,2) CHECK (Cena > 0),
Typ VARCHAR(50),
Powierzchnia DECIMAL(10,2) CHECK (Powierzchnia > 0)
);
CREATE TABLE Oferty (
OfertaID INT PRIMARY KEY IDENTITY(1,1),
KlientID INT FOREIGN KEY REFERENCES Klienci(KlientID),
NieruchomoscID INT FOREIGN KEY REFERENCES Nieruchomosci(NieruchomoscID),
DataOferta DATE NOT NULL,
Status VARCHAR(20) CHECK (Status IN ('Aktywna', 'Zakonczona', 'Wycofana'))
);
CREATE TABLE Transakcje (
TransakcjaID INT PRIMARY KEY IDENTITY(1,1),
OfertaID INT FOREIGN KEY REFERENCES Oferty(OfertaID),
DataTransakcji DATE NOT NULL,
Kwota DECIMAL(15,2) CHECK (Kwota > 0)
);
2. Widoki (VIEW)
Widoki to „wirtualne tabele” – nie przechowują danych, ale prezentują je w sposób uproszczony. Zamiast za każdym razem pisać skomplikowane zapytania z wieloma JOIN-ami, tworzymy widok i odwołujemy się do niego jak do zwykłej tabeli. To szczególnie przydatne, gdy dane z wielu tabel są często używane razem.
Zastosowanie biznesowe
Nasz widok vw_OffertyPodsumowanie łączy dane z czterech tabel, pokazując pełny obraz każdej
oferty: kto jest klientem, jaka to nieruchomość, ile było transakcji i na jaką sumę. To idealna baza
do raportów i dashboardów.
CREATE VIEW vw_OffertyPodsumowanie AS
SELECT
O.OfertaID,
K.Imie AS ImieKlienta,
K.Nazwisko AS NazwiskoKlienta,
N.Adres AS AdresNieruchomosci,
N.Cena AS CenaNieruchomosci,
O.Status,
COUNT(T.TransakcjaID) AS LiczbaTransakcji,
ISNULL(SUM(T.Kwota), 0) AS SumaKwotTransakcji
FROM
Oferty O
INNER JOIN Klienci K ON O.KlientID = K.KlientID
INNER JOIN Nieruchomosci N ON O.NieruchomoscID = N.NieruchomoscID
LEFT JOIN Transakcje T ON O.OfertaID = T.OfertaID
GROUP BY
O.OfertaID, K.Imie, K.Nazwisko, N.Adres, N.Cena, O.Status;
-- Użycie widoku
SELECT * FROM vw_OffertyPodsumowanie WHERE Status = 'Aktywna';
vw_OffertyPodsumowanie.3. Funkcje SQL
Funkcje to wielokrotnego użytku komponenty logiki – możesz je wywoływać z parametrami i otrzymywać zwracane wartości. Rozróżniamy funkcje tabelaryczne (zwracające zestawy rekordów – jak mini-zapytania) oraz skalarne (zwracające pojedyncze wartości – na przykład obliczoną prowizję).
Kiedy używać funkcji?
Gdy ta sama logika jest potrzebna w wielu miejscach. Na przykład funkcja
fn_getClientOffers może być używana w raportach, na stronie klienta czy w API – zawsze
zwróci aktualne oferty dla danego e-maila. Jedna implementacja, wiele zastosowań.
CREATE FUNCTION dbo.fn_getClientOffers
(
@Email VARCHAR(100)
)
RETURNS TABLE
AS
RETURN
(
SELECT
O.OfertaID,
O.DataOferta,
O.Status,
N.Adres,
N.Cena
FROM
Oferty O
INNER JOIN Klienci K ON O.KlientID = K.KlientID
INNER JOIN Nieruchomosci N ON O.NieruchomoscID = N.NieruchomoscID
WHERE
K.Email = @Email
);
-- Test funkcji
SELECT * FROM dbo.fn_getClientOffers('anna.nowak@example.com');
CREATE FUNCTION dbo.fn_calculateCommission
(
@Price DECIMAL(15,2),
@CommissionRate DECIMAL(5,2)
)
RETURNS DECIMAL(15,2)
AS
BEGIN
RETURN @Price * (@CommissionRate / 100);
END;
-- Test funkcji
SELECT dbo.fn_calculateCommission(500000, 3.5) AS Prowizja;
fn_getClientOffers dla wybranego klienta.
4. Procedury składowane (PROCEDURE)
Procedury składowane to „programy w bazie danych” – mogą zawierać złożoną logikę biznesową, instrukcje warunkowe (IF), pętle, obsługę błędów i wiele operacji na danych. W przeciwieństwie do funkcji procedury mogą modyfikować dane, wywoływać inne procedury i zwracać wiele zestawów wyników.
Dlaczego procedury?
Centralizacja logiki biznesowej w bazie danych oznacza, że wszystkie aplikacje (web, mobile, desktop) używają tych samych reguł. Zmiana procesu biznesowego? Aktualizujesz jedną procedurę, a nie kod w kilku aplikacjach. Dodatkowo procedury często działają szybciej, bo kod SQL jest już skompilowany.
CREATE PROCEDURE usp_InsertOrUpdateKlient
@KlientID INT,
@Imie VARCHAR(50),
@Nazwisko VARCHAR(50),
@Email VARCHAR(100),
@Wiek INT
AS
BEGIN
SET NOCOUNT ON;
-- Logika biznesowa: poprawność wieku
IF @Wiek < 18 OR @Wiek > 100
BEGIN
PRINT 'Błąd: Wiek musi być w przedziale 18-100 lat.';
RETURN;
END
-- Jeśli dany KlientID istnieje, zaktualizuj
IF EXISTS (SELECT 1 FROM Klienci WHERE KlientID = @KlientID)
BEGIN
UPDATE Klienci
SET Imie = @Imie, Nazwisko = @Nazwisko, Email = @Email, Wiek = @Wiek
WHERE KlientID = @KlientID;
PRINT 'Rekord został zaktualizowany.';
END
ELSE
BEGIN
-- W przeciwnym wypadku dodaj nowy
INSERT INTO Klienci (KlientID, Imie, Nazwisko, Email, Wiek)
VALUES (@KlientID, @Imie, @Nazwisko, @Email, @Wiek);
PRINT 'Nowy rekord został dodany.';
END
END;
CREATE PROCEDURE sp_AddNewOffer
@KlientEmail VARCHAR(100),
@AdresNieruchomosci VARCHAR(200),
@CenaNieruchomosci DECIMAL(15,2),
@TypNieruchomosci VARCHAR(50),
@PowierzchniaNieruchomosci DECIMAL(10,2)
AS
BEGIN
DECLARE @KlientID INT;
DECLARE @NieruchomoscID INT;
-- Sprawdź czy klient istnieje
SELECT @KlientID = KlientID FROM Klienci WHERE Email = @KlientEmail;
IF @KlientID IS NULL
BEGIN
RAISERROR('Klient nie istnieje!', 16, 1);
RETURN;
END;
-- Dodaj nieruchomość
INSERT INTO Nieruchomosci (Adres, Cena, Typ, Powierzchnia)
VALUES (@AdresNieruchomosci, @CenaNieruchomosci,
@TypNieruchomosci, @PowierzchniaNieruchomosci);
SET @NieruchomoscID = SCOPE_IDENTITY();
-- Dodaj ofertę
INSERT INTO Oferty (KlientID, NieruchomoscID, DataOferta, Status)
VALUES (@KlientID, @NieruchomoscID, GETDATE(), 'Aktywna');
PRINT 'Oferta została dodana pomyślnie!';
END;
CREATE PROCEDURE sp_GetClientStatistics
@KlientID INT,
@LiczbaOfert INT OUTPUT,
@LacznaWartoscTransakcji DECIMAL(15,2) OUTPUT
AS
BEGIN
SELECT
@LiczbaOfert = COUNT(DISTINCT O.OfertaID),
@LacznaWartoscTransakcji = ISNULL(SUM(T.Kwota), 0)
FROM
Oferty O
LEFT JOIN Transakcje T ON O.OfertaID = T.OfertaID
WHERE
O.KlientID = @KlientID;
END;
Testy pokazują
Test 1 dodaje nowego klienta (np. ID = 11), Test 2 aktualizuje istniejącego (ID = 1), a Test 3 demonstruje walidację – próba dodania osoby w wieku 16 lat zostaje odrzucona z odpowiednim komunikatem błędu.
5. Triggery (TRIGGER)
Triggery to „strażnicy bazy danych” – automatycznie reagują na zmiany w tabelach. Gdy ktoś próbuje dodać, zaktualizować lub usunąć rekord, trigger może sprawdzić poprawność operacji, zalogować zmianę, zaktualizować powiązane dane lub nawet anulować całą operację. Działają niezależnie od aplikacji – nawet jeśli ktoś zmieni dane bezpośrednio w bazie, trigger zadziała.
Audyt i compliance
Triggery są nieocenione w systemach wymagających śledzenia zmian. Każda modyfikacja statusu oferty może być automatycznie logowana z informacją kto, kiedy i co zmienił. To kluczowe dla branży nieruchomości, gdzie historia transakcji musi być przejrzysta.
-- Tabela audytu
CREATE TABLE AuditOferty (
AuditID INT IDENTITY(1,1) PRIMARY KEY,
OfertaID INT,
Operation VARCHAR(10),
OldStatus VARCHAR(50),
NewStatus VARCHAR(50),
ChangeDate DATETIME DEFAULT GETDATE()
);
-- AFTER UPDATE - audyt zmian statusu oferty
CREATE TRIGGER trg_AuditOfertyUpdate
ON Oferty
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO AuditOferty (OfertaID, Operation, OldStatus, NewStatus, ChangeDate)
SELECT
i.OfertaID,
'UPDATE',
d.Status,
i.Status,
GETDATE()
FROM
inserted i
INNER JOIN deleted d ON i.OfertaID = d.OfertaID
WHERE
ISNULL(i.Status, '') <> ISNULL(d.Status, '');
END;
-- AFTER INSERT - walidacja transakcji
CREATE TRIGGER trg_AfterInsertTransakcja
ON Transakcje
AFTER INSERT
AS
BEGIN
DECLARE @OfertaID INT;
DECLARE @StatusOferty VARCHAR(20);
SELECT @OfertaID = OfertaID FROM INSERTED;
SELECT @StatusOferty = Status
FROM Oferty
WHERE OfertaID = @OfertaID;
IF @StatusOferty != 'Aktywna'
BEGIN
RAISERROR('Transakcja może być dodana tylko do aktywnej oferty!', 16, 1);
ROLLBACK TRANSACTION;
END;
END;
-- INSTEAD OF DELETE - ochrona przed usunięciem klienta
CREATE TRIGGER trg_InsteadOfDeleteKlient
ON Klienci
INSTEAD OF DELETE
AS
BEGIN
DECLARE @KlientID INT;
DECLARE @LiczbaOfert INT;
SELECT @KlientID = KlientID FROM DELETED;
SELECT @LiczbaOfert = COUNT(*)
FROM Oferty
WHERE KlientID = @KlientID;
IF @LiczbaOfert > 0
BEGIN
RAISERROR('Nie można usunąć klienta z aktywnymi ofertami!', 16, 1);
END
ELSE
BEGIN
DELETE FROM Klienci WHERE KlientID = @KlientID;
END;
END;
AuditOferty.
Podsumowanie
Poznałeś pięć kluczowych narzędzi nowoczesnej bazy danych SQL:
- Struktura tabel z ograniczeniami – fundament zapewniający integralność danych.
- Widoki – upraszczają dostęp do złożonych danych z wielu tabel.
- Funkcje – wielokrotnego użytku komponenty logiki zwracające dane lub wartości.
- Triggery – automatyczne reakcje na zmiany, audyt i ochrona danych.
- Procedury składowane – zaawansowana logika biznesowa w bazie danych.
Najlepsza praktyka
Połączenie tych mechanizmów tworzy solidny, wydajny i łatwy w utrzymaniu system bazodanowy. Strukturą zapewniasz spójność, widokami upraszczasz dostęp, funkcjami redukujesz duplikację kodu, triggerami automatyzujesz procesy, a procedurami centralizujesz logikę biznesową.