Relacja 1:N (Jeden do Wielu) - Działy i Pracownicy

MySQL
Priorytet: Normalny Szkic

Zadanie 101: Relacja Jeden do Wielu (1:N) w MySQL - Działy i Pracownicy

Wstęp

Relacja Jeden do Wielu (1:N) jest najczęściej spotykaną relacją w bazach danych. Przykład: Jeden dział w firmie może mieć wielu pracowników, ale jeden pracownik przypisany jest (zazwyczaj) do jednego działu. W tym zadaniu nauczysz się jak poprawnie zaimplementować tę relację, stosując klucze obce oraz jak wyciągać zintegrowane dane za pomocą złączeń i agregacji.

Część 1: Definiowanie Struktury (DDL)

Zadanie 1: Przygotowanie Bazy

Jeśli jeszcze nie masz, użyj bazy FirmaIT. Jeśli chcesz zacząć od zera, usuń starą i stwórz nową.

CREATE DATABASE IF NOT EXISTS FirmaIT;
USE FirmaIT;

Zadanie 2: Tabela Działy (Strona "Jeden")

Tabela nadrzędna, która przechowuje definicje działów.

CREATE TABLE Dzialy (
    id INT AUTO_INCREMENT PRIMARY KEY,
    nazwa VARCHAR(100) NOT NULL UNIQUE,
    lokalizacja VARCHAR(50)
);

Zadanie 3: Tabela Pracownicy (Strona "Wiele")

Tabela podrzędna, która będzie posiadać klucz obcy wskazujący na dział.

CREATE TABLE Pracownicy (
    id INT AUTO_INCREMENT PRIMARY KEY,
    imie VARCHAR(50) NOT NULL,
    nazwisko VARCHAR(50) NOT NULL,
    pensja DECIMAL(10,2),
    dzial_id INT
);

Zadanie 4: Powiązanie Kluczem Obcym

Dodaj klucz obcy. Użyjemy ON DELETE RESTRICT, co oznacza, że nie można usunąć działu, dopóki są w nim przypisani pracownicy (ochrona danych).

ALTER TABLE Pracownicy ADD CONSTRAINT fk_dzial
FOREIGN KEY (dzial_id) REFERENCES Dzialy(id)
ON DELETE RESTRICT;

[!IMPORTANT] Commit 1: Definicja relacji 1:N.


Część 2: Zarządzanie Danymi (DML)

Zadanie 5: Dodawanie Działów

Wstaw trzy działy.

INSERT INTO Dzialy (nazwa, lokalizacja) VALUES 
('IT', 'Warszawa'),
('HR', 'Kraków'),
('Marketing', 'Wrocław');

Zadanie 6: Dodawanie Pracowników

Przypisz pracowników do odpowiednich działów (ID 1 = IT, ID 2 = HR).

INSERT INTO Pracownicy (imie, nazwisko, pensja, dzial_id) VALUES 
('Marek', 'Zieliński', 8000.00, 1),
('Ewa', 'Kowalska', 6500.00, 1),
('Adam', 'Nowak', 5500.00, 2),
('Karolina', 'Woźniak', 4800.00, 2);

Zadanie 7: Pracownik bez działu

Dodaj pracownika, który jeszcze nie został przypisany do żadnego działu.

INSERT INTO Pracownicy (imie, nazwisko, pensja, dzial_id) VALUES 
('Tomasz', 'Bąk', 4000.00, NULL);

Zadanie 8: Test Restrykcji (Błąd)

Spróbuj dodać pracownika do nieistniejącego działu (np. ID 99).

-- To zwróci błąd klucza obcego
INSERT INTO Pracownicy (imie, nazwisko, dzial_id) VALUES ('Jan', 'Testowy', 99);

Zadanie 9: Test Usuwania Działu (Błąd)

Spróbuj usunąć dział IT (ID 1), w którym są pracownicy.

-- Powinno zwrócić błąd "Cannot delete or update a parent row: a foreign key constraint fails"
DELETE FROM Dzialy WHERE id = 1;

[!IMPORTANT] Commit 2: Testy integralności relacji 1:N.


Część 3: Zapytania i Agregacja (DQL)

Zadanie 10: Pełna lista pracowników z nazwami działów (INNER JOIN)

SELECT p.imie, p.nazwisko, d.nazwa AS dzial
FROM Pracownicy p
INNER JOIN Dzialy d ON p.dzial_id = d.id;

Uwaga: W zapytaniu użyliśmy aliasu d.nazwa AS dzial.

Zadanie 11: Wszyscy pracownicy, nawet bez działu (LEFT JOIN)

SELECT p.nazwisko, d.nazwa 
FROM Pracownicy p 
LEFT JOIN Dzialy d ON p.dzial_id = d.id;

Zadanie 12: Wszystkie działy, nawet te puste (RIGHT JOIN)

Znajdź działy, które nie mają jeszcze pracowników (np. Marketing).

SELECT d.nazwa, p.nazwisko 
FROM Pracownicy p 
RIGHT JOIN Dzialy d ON p.dzial_id = d.id;

Zadanie 13: Liczba pracowników w każdym dziale (GROUP BY)

SELECT d.nazwa, COUNT(p.id) AS liczba_pracownikow
FROM Dzialy d
LEFT JOIN Pracownicy p ON d.id = p.dzial_id
GROUP BY d.nazwa;

Zadanie 14: Średnie zarobki na dział

SELECT d.nazwa, ROUND(AVG(p.pensja), 2) AS srednia_pensja
FROM Dzialy d
JOIN Pracownicy p ON d.id = p.dzial_id
GROUP BY d.nazwa
HAVING srednia_pensja > 5000;

[!IMPORTANT] Commit 3: Raporty i złączenia.


Część 4: Modyfikacje i Porządki

Zadanie 15: Przeniesienie pracownika

Przenieś Marka Zielińskiego (ID 1) z IT do Marketingu (ID 3).

UPDATE Pracownicy SET dzial_id = 3 WHERE id = 1;

Zadanie 16: Zmiana nazwy działu

SQL automatycznie "pilnuje" relacji po ID, więc zmiana nazwy jest bezpieczna.

UPDATE Dzialy SET nazwa = 'Public Relations' WHERE id = 3;

Zadanie 17: Rozwiązanie zależności i usunięcie działu

Aby usunąć dział HR (ID 2), musisz najpierw przenieść lub usunąć jego pracowników.

UPDATE Pracownicy SET dzial_id = 1 WHERE dzial_id = 2; -- Przenosimy do IT
DELETE FROM Dzialy WHERE id = 2;

Zadanie 18: Czyszczenie (Opcjonalne)

DROP TABLE Pracownicy;
DROP TABLE Dzialy;

[!IMPORTANT] Commit 4: Aktualizacje i zachowanie spójności.

Git Help

git add .
git commit -m "MySQL Zadanie 101: Relacja 1-N"