maturarozszerzona.pl

Bazy danych i SQL: zapytania, złączenia, agregacja

6 zadań z oficjalnych arkuszy matury rozszerzonej z informatyki (2023–2025). Spróbuj rozwiązać samodzielnie, potem odsłoń odpowiedź — przy każdym zadaniu znajdziesz typową pułapkę, na której wykładają się maturzyści.

  1. Matura CKE · maj 2025 · zad. 6 7 pkt martianeum, plik tekstowy, transport drona, agregacja danych, ekstrema, wykresy

    Zadanie 6. Martianeum

    W 2033 roku na Marsie wylądowała automatyczna stacja wydobywcza wyposażona w transporter i w autonomiczny dron pobierający ładunki skał zawierających minerał niewystępujący na Ziemi – martianeum.

    Stacja działa według następujących zasad:

    • dron codziennie przywozi ładunek z pewnego obszaru Marsa
    • stacja waży ładunek przywieziony przez drona i bada zawartość martianeum
    • jeśli zawartość martianeum w przywiezionym ładunku wynosi co najmniej 1%, to stacja automatycznie wydobywa cały minerał z tego ładunku
    • jeśli na koniec dnia (po wydobyciu martianeum) ilość minerału na stacji osiągnie co najmniej 100 kg, to transporter zabiera 100 kg na orbitę, skąd będzie on wysyłany na Ziemię, a transporter wraca do stacji (jeśli na stacji zgromadzone jest więcej niż 100 kg, to nadmiar pozostaje na stacji)
    • początkowy stan magazynu na stacji – 0 kg martianeum.

    W pliku tekstowym martianeum.txt w kolejnych wierszach zapisano dane z lat 2033–2038:

    • data – data przywozu ładunku w formacie rrrr-mm-dd
    • nazwa_obszaru – nazwa obszaru Marsa, z którego ładunek został pobrany
    • masa [kg] – zważona przez drona w kilogramach
    • zawartość [%] – zawartość martianeum w próbce w % (nieujemna liczba z jednym miejscem po przecinku, np. 0,1 oznacza 0,1%)

    Dane w pliku rozdzielone są znakami tabulacji.

    Przykład.

    data         nazwa_obszaru   masa [kg]   zawartosc [%]
    2033-03-01   Cebrenia        27,8        0,2
    2033-03-02   Amenthes        13,8        1,7
    2033-03-05   Noachis         21,0        4,0
    2033-03-06   Coprates        26,3        11,4
    2033-03-07   Ismenius Lacus  28,8        0,0
    2033-03-08   Mare Boreum     29,2        0,0
    

    Dane w pliku martianeum.txt są dostępnych narzędzi informatycznych wykonaj podane zadania. Wyniki zapisz w pliku tekstowym wyniki6.txt. Odpowiedzi do każdego zadania poprzedź numerem tego zadania.

    Zadanie 6.1. (0–2)

    Podaj łączną masę ładunków drona oraz łączną masę wydobytego przez stację martianeum.

    Zadanie 6.2. (0–1)

    Podaj nazwę obszaru, dla którego średnia masa przywiezionych ładunków jest najmniejsza.

    Zadanie 6.3. (0–2)

    Czas pracy stacji dzielimy na kolejne 7-dniowe okresy. Pierwszy okres obejmuje dni od 03.03.2033 do 09.03.2033, drugi – od 10.03.2033 do 16.03.2033 itd. Podaj największą łączną masę ładunków przywiezionych w ciągu kolejnych 7-dniowych okresów oraz podaj datę początku okresu, w którym przywieziono tę największą masę.

    Zadanie 6.4. (0–3)

    Wykonaj zestawienie, w którym dla każdego obszaru podasz, ile razy dron przywoził ładunek z tego obszaru w poszczególnych latach. Na podstawie wykonanego zestawienia utwórz wykres skumulowany kolumnowy. Pamiętaj o czytelnym opisie wykresu: na osi X umieść nazwy obszarów, dodaj opisy osi – "nazwy obszarów", dodaj opis i "liczba przewozów" dla osi Y, tytuł oraz legendę zawierającą kolejne lata.

    Zadanie 6.5. (0–3)

    Uwzględniając zasady działania stacji opisane na początku zadania i podaj:

    • ile razy stacja wysyłała ładunek na orbitę
    • datę pierwszego transportu ładunku na orbitę
    • datę ostatniego transportu ładunku na orbitę.
    Pokaż odpowiedź

    Wspólne wczytanie danych (np. pandas):

    import pandas as pd
    
    df = pd.read_csv("martianeum.txt", sep="\t", decimal=",")
    df["data"] = pd.to_datetime(df["data"])
    df["rok"] = df["data"].dt.year
    

    6.1 – łączna masa ładunków i wydobyte martianeum:

    masa_lacznie = df["masa [kg]"].sum()
    # martianeum wydobyte tylko gdy zawartość >= 1%
    df["wydobyte"] = df.apply(
        lambda r: r["masa [kg]"] * r["zawartosc [%]"] / 100
                  if r["zawartosc [%]"] >= 1.0 else 0.0,
        axis=1
    )
    martianeum_lacznie = df["wydobyte"].sum()
    print(round(masa_lacznie, 2), round(martianeum_lacznie, 4))
    

    6.2 – obszar o najmniejszej średniej masie:

    srednia = df.groupby("nazwa_obszaru")["masa [kg]"].mean()
    print(srednia.idxmin())
    

    6.3 – największa masa w 7-dniowym okresie (start 2033-03-03):

    from datetime import timedelta
    start = pd.Timestamp("2033-03-03")
    df["okres"] = ((df["data"] - start) // pd.Timedelta(days=7))
    okres_start = df.groupby("okres")["data"].min()  # lub wyliczone z indeksu
    suma_okres = df.groupby("okres")["masa [kg]"].sum()
    najw = suma_okres.idxmax()
    data_start = start + pd.Timedelta(days=int(najw)*7)
    print(round(suma_okres[najw], 2), data_start.strftime("%Y-%m-%d"))
    

    6.4 – tabela przewozów obszar × rok + wykres skumulowany kolumnowy:

    zestawienie = df.groupby(["nazwa_obszaru", "rok"]).size().unstack(fill_value=0)
    zestawienie.plot(kind="bar", stacked=True, title="Liczba przewozów per obszar")
    # dalej: opisy osi (xlabel="nazwy obszarów", ylabel="liczba przewozów"), legenda = lata
    

    6.5 – symulacja zasad transportu na orbitę:

    df = df.sort_values("data").reset_index(drop=True)
    magazyn = 0.0
    liczba_transportow = 0
    pierwsza, ostatnia = None, None
    for _, r in df.iterrows():
        if r["zawartosc [%]"] >= 1.0:
            magazyn += r["masa [kg]"] * r["zawartosc [%]"] / 100
        while magazyn >= 100:
            magazyn -= 100
            liczba_transportow += 1
            if pierwsza is None:
                pierwsza = r["data"]
            ostatnia = r["data"]
    print(liczba_transportow, pierwsza.strftime("%Y-%m-%d"), ostatnia.strftime("%Y-%m-%d"))
    

    Uwaga – pętla while obsługuje przypadek, gdyby pojedynczy dzień zgromadził >200 kg (mało prawdopodobne, ale matematycznie możliwe).

    ⚠ Typowa pułapka: W 6.1 – wydobywamy martianeum **tylko gdy zawartość ≥ 1%**, nie zawsze. W 6.3 – okres 7-dniowy zaczyna się **3 marca** (nie 1 marca), trzeba to zaszyć w obliczeniach. W 6.5 – stacja transportuje **dokładnie 100 kg** na raz, więc nadmiar musi pozostać na stacji. Liczne arkusze maturalne karzą za przeoczenie warunku "łącznie z magazynem martianeum przekroczył 100 kg" – nie tylko dzienny ładunek. W 6.4 – wykres ma być **skumulowany** (stacked), nie zwykły kolumnowy.

    Zobacz pełne rozwiązanie krok po kroku →
  2. Matura CKE · maj 2025 · zad. 7 10 pkt baza danych, SQL, JOIN, łaziki Mars, agregacja, filtracja zapytań, modelowanie danych

    Zadanie 7. Poszukiwanie wody na Marsie

    W trzech plikach tekstowych o nazwach laziki.txt, obszary.txt, pomiary.txt zapisano informacje zawierające dane o pracach łazików na Marsie w latach 2050–2080. Łaziki zasilane energią słoneczną poruszają się po różnych obszarach Marsa i wykonują pomiary georadarowe, na podstawie których szacują ilość wody i głębokość, na której się ona znajduje. Pierwszy wiersz każdego z plików jest wierszem nagłówkowym, a dane w wierszach rozdzielone są znakami tabulacji.

    Plik laziki.txt zawiera informacje o różnych łazikach, które wykonywały pomiary. W każdym wierszu tego pliku znajdują się:

    • nr_lazika – co najwyżej trzycyfrowy, unikatowy numer łazika
    • nazwa_lazika – nazwa łazika (tekst do 50 znaków)
    • rok_wyslania – rok startu z Ziemi
    • wsp_ladowania – współrzędne lądowania na Marsie oddzielone znakiem przecinka i spacji

    Przykład.

    nr_lazika  nazwa_lazika  rok_wyslania  wsp_ladowania
    1          Mariner 3     2049          50.51N, 70.01E
    2          Mariner 6     2050          11.90N, 119.49E
    3          Mariner 7     2050          44.90S, 135.80W
    

    Plik obszary.txt zawiera informacje o obszarach na Marsie. W każdym wierszu tego pliku znajdują się:

    • kod_obszaru – pięcioznakowy, unikatowy kod obszaru
    • nazwa_obszaru – nazwa obszaru (tekst do 50 znaków)

    Przykład.

    kod_obszaru  nazwa_obszaru
    MC-01        Mare Boreum
    MC-02        Diacria
    MC-03        Arcadia
    

    Plik pomiary.txt zawiera informacje o wynikach badań georadarowych wykonanych przez łaziki. W każdym wierszu tego pliku znajdują się:

    • nr_lazika – co najwyżej trzycyfrowy numer łazika
    • data_pomiaru – data wykonania pomiaru (w formacie rrrr-mm-dd)
    • kod_obszaru – pięcioznakowy kod obszaru, na którym został wykonany pomiar
    • wspolrzedne – współrzędne wykonania pomiaru, oddzielone przecinkiem i spacją
    • glebokosc – szacowana głębokość, na której znajduje się woda (w metrach)
    • ilosc – szacowana ilość wody (w m³)

    Zadanie 7.1. (0–2)

    Podaj nazwę obszaru, na którym znaleziono łącznie we wszystkich pomiarach najwięcej m³ wody na głębokości do 100 metrów włącznie. Jest jeden taki obszar.

    Zadanie 7.2. (0–2)

    Podaj nazwę łazika, który wykonywał pomiary w najdłuższym okresie, licząc od pierwszego (najwcześniejszego) do ostatniego (najpóźniejszego) pomiaru. Podaj datę pierwszego i ostatniego pomiaru wykonanego przez ten łazik.

    Zadanie 7.3. (0–2)

    Podaj nazwy obszarów na Marsie, na których żaden z łazików nie wykonał ani jednego pomiaru w tym samym roku, w którym został wysłany z Ziemi.

    Zadanie 7.4. (0–2)

    Podaj nazwy łazików, które wylądowały na półkuli południowej, ale wykonywały pomiary na obu półkulach: północnej (N) i południowej (S).

    Zadanie 7.5. (0–2)

    Do tabel utworzonych na podstawie opisanych wcześniej plików dołączamy kolejną – o nazwie Producent, w której zapisano informacje o producentach poszczególnych modeli łazików.

    Tabela Producent zawiera następujące pola:

    • kod_producenta – unikatowy kod producenta
    • nazwa – nazwa producenta
    • kraj – kraj producenta

    Do tabeli Laziki dodano pole kod_producenta.

    Napisz w języku SQL zapytanie, w wyniku którego otrzymasz listę nazw producentów, których łaziki badały obszar Marsa o nazwie Arcadia lub o nazwie Acidalia w roku 2060. Nazwy producentów nie mogą się powtarzać.

    Pokaż odpowiedź

    Zakładamy importowanie plików do bazy jako tabel Laziki, Obszary, Pomiary (np. SQLite/PostgreSQL).

    7.1 – obszar z największą ilością wody do głębokości 100 m:

    SELECT o.nazwa_obszaru, SUM(p.ilosc) AS suma_wody
    FROM Pomiary p
    JOIN Obszary o ON o.kod_obszaru = p.kod_obszaru
    WHERE p.glebokosc <= 100
    GROUP BY o.nazwa_obszaru
    ORDER BY suma_wody DESC
    LIMIT 1;
    

    7.2 – łazik z najdłuższym okresem pomiarów:

    SELECT l.nazwa_lazika,
           MIN(p.data_pomiaru) AS pierwszy,
           MAX(p.data_pomiaru) AS ostatni,
           JULIANDAY(MAX(p.data_pomiaru)) - JULIANDAY(MIN(p.data_pomiaru)) AS dni
    FROM Laziki l
    JOIN Pomiary p ON p.nr_lazika = l.nr_lazika
    GROUP BY l.nr_lazika, l.nazwa_lazika
    ORDER BY dni DESC
    LIMIT 1;
    

    (W PostgreSQL: MAX(...) - MIN(...) zwraca interval). Wynik podaje nazwę + obie daty.

    7.3 – obszary bez pomiarów w roku wysłania łazika:

    SELECT DISTINCT o.nazwa_obszaru
    FROM Obszary o
    WHERE o.kod_obszaru NOT IN (
        SELECT p.kod_obszaru
        FROM Pomiary p
        JOIN Laziki l ON l.nr_lazika = p.nr_lazika
        WHERE CAST(SUBSTR(p.data_pomiaru, 1, 4) AS INTEGER) = l.rok_wyslania
    );
    

    7.4 – łaziki które wylądowały na półkuli S, ale mierzyły na obu półkulach:

    Półkulę odczytujemy z wsp_ladowania (zawiera N lub S w pierwszej współrzędnej). Półkulę w pomiarze – analogicznie z wspolrzedne.

    SELECT l.nazwa_lazika
    FROM Laziki l
    WHERE l.wsp_ladowania LIKE '%S,%'
      AND EXISTS (
        SELECT 1 FROM Pomiary p WHERE p.nr_lazika = l.nr_lazika
          AND p.wspolrzedne LIKE '%N,%'
      )
      AND EXISTS (
        SELECT 1 FROM Pomiary p WHERE p.nr_lazika = l.nr_lazika
          AND p.wspolrzedne LIKE '%S,%'
      );
    

    7.5 – producenci łazików mierzących Arcadia/Acidalia w 2060:

    SELECT DISTINCT pr.nazwa
    FROM Producent pr
    JOIN Laziki l ON l.kod_producenta = pr.kod_producenta
    JOIN Pomiary p ON p.nr_lazika = l.nr_lazika
    JOIN Obszary o ON o.kod_obszaru = p.kod_obszaru
    WHERE o.nazwa_obszaru IN ('Arcadia', 'Acidalia')
      AND CAST(SUBSTR(p.data_pomiaru, 1, 4) AS INTEGER) = 2060;
    

    ⚠ Typowa pułapka: W 7.1 – warunek brzmi "do 100 metrów **włącznie**", więc `<=`, nie `<`. W 7.3 – "żaden łazik" wymaga `NOT IN` lub `NOT EXISTS` z podzapytaniem – częsty błąd to próba odwrócenia warunku po stronie JOIN, co nie wykluczy obszarów bez pomiarów. W 7.4 – ostrzeżenie: łazik mógł lądować na N i mierzyć tylko na S – nie liczy się; trzeba: lądowanie na S **i** pomiary zarówno na N **i** na S. W 7.5 – `DISTINCT` jest obowiązkowy (treść zadania mówi explicite "nie mogą się powtarzać").

    Zobacz pełne rozwiązanie krok po kroku →
  3. Matura CKE · maj 2024 · zad. 7 10 pkt analiza danych, arkusz kalkulacyjny, SQL, agregacja, wykres, rabat

    Zadanie 7. Hurtownia

    Pewna hurtownia sprzedaje jabłka. W pliku jablka.txt znajduje się 2500 wierszy z danymi dotyczącymi sprzedaży jabłek od 3 stycznia 2022 roku do 31 grudnia 2022 roku. W każdym wierszu podane są dane opisujące jedną transakcję sprzedaży, oddzielone pojedynczymi znakami tabulacji:

    • data sprzedaży zapisana w formacie rrrr-mm-dd
    • nazwa odmiany jabłka
    • kod, który określa, czy odmiana jest: L — letnia, J — jesienna czy Z — zimowa
    • numer NIP klienta (13-znakowy tekst)
    • liczba kilogramów sprzedanego towaru.

    Fragment pliku jablka.txt:

    2022-01-03    Jonagold     Z    128-29-15-591   470
    2022-01-03    Jonagold     Z    192-09-72-275   410
    2022-01-03    Jonagored    Z    140-36-11-559   242
    

    Cena sprzedaży jednego kilograma jabłek zależy od odmiany jabłek. W pliku cennik.txt jest podana cena w złotych 1 kg jabłek każdej odmiany.

    Fragment pliku cennik.txt:

    Alwa         2,9
    Antonowka    3,2
    Cortland     3,2
    

    Zadanie 7.1. (0–2) Dla każdego klienta policz, ile kupił on łącznie (we wszystkich swoich transakcjach) kilogramów jabłek odmian zimowych. Podaj numery NIP trzech klientów, którzy kupili najwięcej jabłek odmian zimowych (Z), oraz podaj dla każdego z nich liczbę kilogramów jabłek odmian zimowych przez nich kupionych.

    Zadanie 7.2. (0–2) Przychód z pojedynczej sprzedaży to cena sprzedaży jednego kilograma jabłek pomnożona przez liczbę kilogramów. Podaj całkowity przychód hurtowni uzyskany w całym okresie. Podaj również odmiany jabłek, która dała największy przychód.

    Zadanie 7.3. (0–3) Wykonaj zestawienie, w którym dla każdego miesiąca roku 2022 podasz nazwę najbardziej popularnej odmiany w tym miesiącu, czyli takiej, której w danym miesiącu sprzedano najwięcej. Na podstawie wykonanego zestawienia utwórz wykres kolumnowy ilustrujący wielkości sprzedaży najpopularniejszych odmian jabłek w poszczególnych miesiącach. Pamiętaj o czytelnym opisie wykresu — na osi X umieść daty kolejnych miesięcy, oś Y odpowiada wielkości sprzedaży najbardziej popularnej odmiany w tym miesiącu, dodaj opis osi Y i tytuł wykresu.

    Zadanie 7.4. (0–3) Hurtownia ma system premiowania klientów hurtowych. Klient otrzymuje przy zakupie rabat, którego wysokość zależy od łącznej ilości jabłek zakupionych do tej pory przez tego klienta, wliczając w to jabłka zakupione w bieżącej transakcji. Wysokość rabatu za każdy kilogram w bieżącej transakcji wynosi:

    • 5 gr, jeśli klient dotychczas zakupił co najwyżej 15 000 kg, ale mniej niż 20 000 kg,
    • 10 gr, jeśli klient dotychczas zakupił co najmniej 20 000 kg.

    Podaj, w tej kolejności (dla 3 klientów z najwyższymi łącznymi wartościami rabatów): imię + NIP klienta, wartość rabatu.

    Pokaż odpowiedź

    Najwygodniej rozwiązać arkuszem kalkulacyjnym lub Pythonem z pandas.

    Załadowanie danych (Python):

    import pandas as pd
    jablka = pd.read_csv("jablka.txt", sep="\t", header=None,
                        names=["data","odmiana","kod","nip","kg"])
    jablka["data"] = pd.to_datetime(jablka["data"])
    cennik = pd.read_csv("cennik.txt", sep="\t", header=None,
                         names=["odmiana","cena"])
    cennik["cena"] = cennik["cena"].str.replace(",", ".").astype(float)
    

    7.1. Top-3 klientów wg sumy kg odmian zimowych:

    zimowe = jablka[jablka["kod"] == "Z"]
    top3 = zimowe.groupby("nip")["kg"].sum().sort_values(ascending=False).head(3)
    print(top3)
    

    7.2. Przychód całkowity i odmiana z najwyższym przychodem:

    d = jablka.merge(cennik, on="odmiana")
    d["przychod"] = d["kg"] * d["cena"]
    print(f"Całkowity przychód: {d['przychod'].sum():.2f} zł")
    print(d.groupby("odmiana")["przychod"].sum().idxmax(),
          d.groupby("odmiana")["przychod"].sum().max())
    

    7.3. Najpopularniejsza odmiana w każdym miesiącu + wykres:

    jablka["miesiac"] = jablka["data"].dt.to_period("M")
    pop = (jablka.groupby(["miesiac","odmiana"])["kg"].sum()
                  .reset_index()
                  .sort_values(["miesiac","kg"], ascending=[True,False])
                  .drop_duplicates("miesiac"))
    print(pop[["miesiac","odmiana","kg"]])
    
    import matplotlib.pyplot as plt
    plt.bar(pop["miesiac"].astype(str), pop["kg"])
    plt.xlabel("Miesiąc 2022")
    plt.ylabel("Sprzedaż najpopularniejszej odmiany [kg]")
    plt.title("Najpopularniejsze odmiany jabłek 2022")
    plt.xticks(rotation=45)
    plt.tight_layout()
    plt.savefig("wykres_7_3.png", dpi=150)
    

    7.4. Rabat naliczamy iteracyjnie — dla każdej transakcji sprawdzamy stan zakupów klienta do tej pory włącznie z tą transakcją:

    jablka_s = jablka.sort_values("data").reset_index(drop=True)
    laczne = {}  # nip -> kg dotychczas
    rabat  = {}  # nip -> łączna kwota rabatu w zł
    for _, r in jablka_s.iterrows():
        nip, kg = r["nip"], r["kg"]
        nowy = laczne.get(nip, 0) + kg
        laczne[nip] = nowy
        if nowy >= 20000:
            stawka = 0.10
        elif nowy >= 15000:
            stawka = 0.05
        else:
            stawka = 0.0
        rabat[nip] = rabat.get(nip, 0) + kg * stawka
    top3 = sorted(rabat.items(), key=lambda x: -x[1])[:3]
    for nip, kwota in top3:
        print(nip, round(kwota, 2))
    

    Wyniki konkretne (zależne od danych w pliku) zapisujemy do wyniki7.txt, poprzedzając każdą sekcję numerem zadania (7.1, 7.2, 7.3, 7.4).

    ⚠ Typowa pułapka: Najczęstsze błędy: (1) w 7.2 — czytanie cen jako stringów z przecinkiem (PL) i zapominanie o zamianie na kropkę → string concatenation zamiast iloczynu; (2) w 7.3 — wybór odmiany "popularnej" wagowo (suma kg), nie liczbowo (liczba transakcji) — treść CKE mówi „której najwięcej sprzedano" = kg; (3) w 7.4 najczęściej myli się próg rabatu: zdanie „**wliczając** w to jabłka zakupione w bieżącej transakcji" znaczy, że stan po dodaniu bieżących kg decyduje o stawce; (4) brakuje sortowania chronologicznego transakcji przed naliczaniem rabatu.

    Zobacz pełne rozwiązanie krok po kroku →
  4. Matura CKE · maj 2024 · zad. 8 7 pkt baza danych, SQL, JOIN, agregacja, antyjoin, sortowanie

    Zadanie 8. Rejestr wykroczeń

    W trzech plikach tekstowych o nazwach: kierowcy.txt, taryfikator.txt, rejestr.txt, zapisano dane związane z przekroczeniem dozwolonych prędkości pojazdów na pewnej trasie w okresie 2023-01-01 — 2023-12-30 (wszystkie dane są fikcyjne i wygenerowane na potrzeby zadania).

    Pierwszy wiersz każdego z plików jest wierszem nagłówkowym, a dane w wierszach rozdzielono znakiem średnika.

    Plik o nazwie kierowcy.txt zawiera informacje o 1000 osobach będących właścicielami samochodów. W każdym wierszu znajdują się:

    • IdOsoby — identyfikator osoby
    • Imie — imię
    • Nazwisko — nazwisko
    • NrRejestracyjny — numer rejestracyjny samochodu.

    Plik o nazwie taryfikator.txt zawiera informacje o 6 rodzajach wykroczeń związanych z przekroczeniem prędkości. W każdym wierszu znajdują się:

    • IdWykroczenia — identyfikator
    • Wykroczenie — opis wykroczenia
    • Punkty — liczba punktów karnych za dane wykroczenie
    • Kwota — kwota mandatu, jaką należy zapłacić za to wykroczenie.

    Przykład taryfikator.txt:

    1;Przekroczenie predkosci do 10 km/h;0;50
    2;Przekroczenie predkosci od 11 do 20 km/h;2;100
    3;Przekroczenie predkosci od 21 do 30 km/h;4;200
    4;Przekroczenie predkosci od 31 do 40 km/h;6;400
    5;Przekroczenie predkosci od 41 do 50 km/h;8;800
    6;Przekroczenie predkosci od 51 km/h;10;1500
    

    Zadanie 8.1. (0–2) Podaj imię i nazwisko kierowcy, dla którego suma kwot za wszystkie mandaty była największa, oraz podaj tę największą sumę. Jest tylko jeden taki kierowca.

    Zadanie 8.2. (0–2) W którym miesiącu kierowcy otrzymali najmniej punktów karnych (łącznie) za wykroczenia polegające na przekroczeniu dozwolonej prędkości o więcej niż 20 km/h (wykroczenia o identyfikatorach od 3 do 6)? Podaj miesiąc oraz łączną liczbę punktów karnych z tego miesiąca.

    Zadanie 8.3. (0–3) Wykonaj zestawienie numerów rejestracyjnych samochodów wraz z imionami i nazwiskami ich właścicieli, którzy nie figurują w rejestrze wykroczeń. Zestawienie posortuj alfabetycznie według numerów rejestracyjnych samochodów.

    Pokaż odpowiedź

    Najwygodniej rozwiązać SQL-em po zaimportowaniu trzech CSV (separator ;) jako tabel. Poniżej rozwiązania w SQLite / standardowym SQL oraz alternatywa w Pythonie pandas.

    8.1. Kierowca z największą sumą kwot mandatów:

    SELECT k.Imie, k.Nazwisko, SUM(t.Kwota) AS suma_kwot
    FROM rejestr r
      JOIN kierowcy k    ON r.IdOsoby = k.IdOsoby
      JOIN taryfikator t ON r.IdWykroczenia = t.IdWykroczenia
    GROUP BY k.IdOsoby, k.Imie, k.Nazwisko
    ORDER BY suma_kwot DESC
    LIMIT 1;
    

    8.2. Miesiąc z najmniejszą sumą punktów karnych za wykroczenia o id 3–6:

    SELECT strftime('%Y-%m', r.Data) AS miesiac, SUM(t.Punkty) AS pkt
    FROM rejestr r
      JOIN taryfikator t ON r.IdWykroczenia = t.IdWykroczenia
    WHERE t.IdWykroczenia BETWEEN 3 AND 6
    GROUP BY miesiac
    ORDER BY pkt ASC
    LIMIT 1;
    

    8.3. Numery rejestracyjne i właściciele, którzy NIE są w rejestr.txt, sortowane alfabetycznie po NrRejestracyjny:

    SELECT k.NrRejestracyjny, k.Imie, k.Nazwisko
    FROM kierowcy k
    WHERE k.IdOsoby NOT IN (SELECT IdOsoby FROM rejestr)
    ORDER BY k.NrRejestracyjny ASC;
    

    Alternatywa Pythonem (pandas):

    import pandas as pd
    kierowcy    = pd.read_csv("kierowcy.txt",    sep=";")
    taryfikator = pd.read_csv("taryfikator.txt", sep=";")
    rejestr     = pd.read_csv("rejestr.txt",     sep=";", parse_dates=["Data"])
    
    # 8.1.
    d = (rejestr.merge(taryfikator, on="IdWykroczenia")
                .merge(kierowcy,    on="IdOsoby"))
    s = d.groupby(["IdOsoby","Imie","Nazwisko"])["Kwota"].sum().sort_values(ascending=False)
    print("8.1.", s.head(1))
    
    # 8.2.
    d2 = d[d["IdWykroczenia"].between(3,6)]
    d2["miesiac"] = d2["Data"].dt.to_period("M")
    pkt = d2.groupby("miesiac")["Punkty"].sum().sort_values()
    print("8.2.", pkt.head(1))
    
    # 8.3.
    brak = kierowcy[~kierowcy["IdOsoby"].isin(rejestr["IdOsoby"])] \
                .sort_values("NrRejestracyjny")
    brak[["NrRejestracyjny","Imie","Nazwisko"]].to_csv("wyniki8_3.csv", index=False)
    

    Wszystkie odpowiedzi zapisujemy do wyniki8.txt, każdą poprzedzając numerem podzadania.

    ⚠ Typowa pułapka: Najczęstsze błędy: (1) w 8.1 sumowanie punktów zamiast kwot (treść CKE mówi „suma **kwot** za mandaty"); (2) w 8.2 wybór miesiąca z **największą** liczbą punktów zamiast najmniejszą — uważnie czytaj „najmniej"; (3) w 8.2 zignorowanie filtra "od więcej niż 20 km/h" (id 3–6); (4) w 8.3 użycie INNER JOIN zamiast antyjoin — wtedy zwracamy kierowców WYSTĘPUJĄCYCH w rejestrze, czyli odwrotnie; (5) sortowanie ASCII vs alfabetyczne PL — dla numerów rejestracyjnych nie ma znaków diakrytycznych więc zwykłe ORDER BY wystarczy; (6) brak nagłówka w pliku odpowiedzi mimo że dane mają nagłówek.

    Zobacz pełne rozwiązanie krok po kroku →
  5. Matura CKE · maj 2023 · zad. 1 7 pkt drzewo binarne, biblioteczka, algorytm rekurencyjny, struktury danych

    Zadanie 1. Biblioteczka

    Pewna biblioteczka ma strukturę drzewa binarnego z 5 poziomami (poziomy ponumerowane są 0, 1, 2, 3, 4). Na każdym poziomie i znajduje się 2^i przegródek (na poziomie 0 - jedna, na poziomie 4 - szesnaście). Książki wstawia się do biblioteczki zgodnie z następującą zasadą: pierwszą wstawiamy do przegródki o numerze 1 na poziomie 0. Kolejne książki wstawiamy w taki sposób, że jeśli rodzic znajduje się w przegródce B[i, j], to lewe dziecko trafia do B[i+1, 2j-1], a prawe do B[i+1, 2j]. Książkę zawsze wstawiamy do najwyżej położonej i najbardziej z lewej strony wolnej przegródki (przy zachowaniu reguł drzewa).

    Zadanie 1.1. (0–2) Podaj zawartość biblioteczki po wstawieniu do niej kolejno książek o numerach: 14, 18, 12, 9, 20, 15, 17. Numery książek wpisz we właściwe miejsca na schemacie.

    Zadanie 1.2. (0–3) Uzupełnij tabelkę – wpisz, ile minimalnie, a ile maksymalnie musi być półek w biblioteczce, żeby można było umieścić w niej n książek i żeby na ostatniej półce znalazła się co najmniej jedna książka. Wypełnij wiersze dla n = 7, 16, 31, 32 oraz dla n = 2^k − 1 (k > 0).

    Zadanie 1.3. (0–2) Dany jest rekurencyjny algorytm A(i, j):

    • wypisz numer książki z przegródki B[i, j]
    • jeżeli przegródka B[i+1, 2j−1] nie jest pusta, to wykonaj A(i+1, 2j−1)
    • jeżeli przegródka B[i+1, 2j] nie jest pusta, to wykonaj A(i+1, 2j)

    Działanie rozpoczynamy od wywołania A(0, 1). Podaj ciągi liczb wypisane przez algorytm A dla dwóch podanych zawartości biblioteczek (a) i (b).

    Pokaż odpowiedź

    1.1. Wstawianie do drzewa binarnego (kopca BST – zawsze do najwyższego, najbardziej lewego wolnego miejsca):

    • 14 → B[0,1]
    • 18 → B[1,1]
    • 12 → B[1,2]
    • 9 → B[2,1]
    • 20 → B[2,2]
    • 15 → B[2,3]
    • 17 → B[2,4]

    Schemat:

           14
          /  \
        18    12
        /\    /\
       9 20 15 17
    

    1.2. Każda półka i (oprócz ostatniej) musi być w pełni zapełniona; na ostatniej co najmniej 1 książka.

    n min półek max półek
    7 3 7
    16 5 16
    31 5 31
    32 6 32
    2^k − 1 k 2^k − 1

    Uzasadnienie: minimum osiągamy, gdy wypełniamy drzewo od góry (potrzeba ⌈log₂(n+1)⌉ poziomów ⇒ ⌈log₂(n+1)⌉ półek). Maksimum osiągamy układając książki "łańcuszkowo" – każda na osobnej półce, więc max = n (ograniczone do 5 dla biblioteczki z zadania; tu pytamy ogólnie, max = n).

    1.3. Algorytm A wykonuje przejście drzewa w porządku pre-order (najpierw korzeń, potem lewe poddrzewo, potem prawe).

    • a) Drzewo: 9 (korzeń); lewe poddrzewo: 2 → (10, 13); prawe poddrzewo: 12 → (14, 15).
      Ciąg pre-order: 9, 2, 10, 13, 12, 14, 15

    • b) Drzewo: 10 (korzeń); lewe poddrzewo: 8 → (4, 6), (-, -); prawe poddrzewo: 15 → (12, -); węzeł 4 ma dziecko - puste, węzeł 6 ma dziecko 13.
      Odczyt schematu: 10 → 8 → 4 → (lewe puste, prawe puste) → 6 → 13 → ... → 15 → 12.
      Ciąg pre-order: 10, 8, 4, 6, 13, 15, 12

    ⚠ Typowa pułapka: Algorytm A to klasyczne **pre-order** (NLR), a nie in-order ani BFS. Typowy błąd: wypisywanie według poziomów (BFS) zamiast rekurencyjnie w głąb. Druga pułapka 1.2: pomylić "co najmniej jedna na ostatniej półce" z "ostatnia półka pełna" – maksimum to liczba półek równa liczbie książek (każda na osobnej półce), a nie 2^k.

    Zobacz pełne rozwiązanie krok po kroku →
  6. Matura CKE · maj 2023 · zad. 7 8 pkt baza danych, SQL, JOIN, GROUP BY, ranking gier planszowych

    Zadanie 7. Gry planszowe

    Pewien serwis internetowy prowadzi ranking gier planszowych. Baza serwisu została zapisana w trzech plikach.

    Plik gry.txt zawiera informacje o grach planszowych. W każdym wierszu zapisano:

    • id_gry – unikatowy numer gry planszowej
    • nazwa – tytuł gry planszowej
    • kategoria – kategoria, do jakiej została zakwalifikowana gra planszowa; każda gra należy tylko do jednej kategorii.

    Plik gracze.txt zawiera informacje o graczach. W każdym wierszu zapisano:

    • id_gracza – unikatowy numer gracza
    • imie – imię gracza
    • nazwisko – nazwisko gracza
    • wiek – wiek gracza.

    Plik oceny.txt zawiera oceny wystawione grom przez poszczególnych graczy. W każdym wierszu zapisano:

    • id_gry – numer gry planszowej
    • id_gracza – numer gracza
    • stan – zawiera jedną z możliwych wartości: posiada, chce kupic, sprzedal, opisującą, czy użytkownik posiada daną grę, czy ją sprzedał lub czy zamierza ją zakupić
    • ocena – zawiera ocenę gry przez gracza, wyrażoną liczbą całkowitą w zakresie od 0 do 10.

    We wszystkich plikach dane w wierszach są rozdzielone znakami tabulacji, a pierwszy wiersz w każdym pliku jest wierszem nagłówkowym.

    Z wykorzystaniem dostępnych narzędzi informatycznych podaj odpowiedzi do zadań 7.1.–7.4.

    Zadanie 7.1. (0–1) Podaj tytuł gry, która otrzymała najwięcej ocen.

    Zadanie 7.2. (0–2) Dla każdej z 7 kategorii „imprezowa" podaj średnią jej ocen z dokładnością do dwóch miejsc po przecinku. (uwaga: w arkuszu pytanie dotyczy każdej kategorii — średnia ocen tej kategorii)

    Zadanie 7.3. (0–2) Podaj liczbę graczy, którzy nie posiadają żadnej z ocenianych przez siebie gier (nie mają żadnej gry ze stanem "posiada"), a wystawili co najmniej jedną ocenę.

    Zadanie 7.4. (0–3) W ocenianiu gier planszowych uczestniczą osoby w wieku od 10 do 99 lat. Osoby oceniające gry podzielono na dwa kategorie wiekowe: juniorzy (do 19 lat), seniorzy (od 20 do 49 lat) oraz weterani (od 50 lat). Wykonaj zestawienie, w którym dla każdej kategorii wiekowej podaj największą liczbę ocen wystawionych przez jedną osobę, oraz – jeśli okazałoby się, że więcej niż jedna – podaj tytuły wszystkich.

    Pokaż odpowiedź

    Zakładamy że pliki zostały zaimportowane do bazy SQL (np. SQLite) jako tabele gry, gracze, oceny.

    7.1. Tytuł gry z największą liczbą ocen:

    SELECT g.nazwa, COUNT(*) AS ile
    FROM oceny o JOIN gry g ON g.id_gry = o.id_gry
    GROUP BY g.id_gry, g.nazwa
    ORDER BY ile DESC
    LIMIT 1;
    

    7.2. Średnia ocen dla każdej kategorii (zaokrąglona do 2 miejsc):

    SELECT g.kategoria, ROUND(AVG(o.ocena), 2) AS srednia
    FROM oceny o JOIN gry g ON g.id_gry = o.id_gry
    GROUP BY g.kategoria
    ORDER BY g.kategoria;
    

    7.3. Gracze, którzy nigdy nie mają stanu "posiada", ale wystawili co najmniej jedną ocenę:

    SELECT COUNT(DISTINCT o.id_gracza)
    FROM oceny o
    WHERE o.id_gracza NOT IN (
        SELECT id_gracza FROM oceny WHERE stan = 'posiada'
    );
    

    7.4. Najwięcej ocen w każdej kategorii wiekowej. Najpierw policzmy oceny per gracz z przypisaną kategorią:

    WITH ocen_per_gracz AS (
      SELECT
        o.id_gracza,
        CASE
          WHEN gr.wiek <= 19 THEN 'junior'
          WHEN gr.wiek <= 49 THEN 'senior'
          ELSE 'weteran'
        END AS kat_wiek,
        COUNT(*) AS ile
      FROM oceny o
      JOIN gracze gr ON gr.id_gracza = o.id_gracza
      GROUP BY o.id_gracza, kat_wiek
    ),
    max_per_kat AS (
      SELECT kat_wiek, MAX(ile) AS max_ile
      FROM ocen_per_gracz
      GROUP BY kat_wiek
    )
    SELECT opg.kat_wiek, gr.imie, gr.nazwisko, opg.ile
    FROM ocen_per_gracz opg
    JOIN max_per_kat m ON m.kat_wiek = opg.kat_wiek AND m.max_ile = opg.ile
    JOIN gracze gr ON gr.id_gracza = opg.id_gracza;
    

    Wyniki wpisz do wyniki7.txt, każdy poprzedzony numerem zadania (7.1., 7.2., 7.3., 7.4.).

    ⚠ Typowa pułapka: Zad. 7.3 — kluczowe rozróżnienie: "nie posiada żadnej gry" oznacza brak rekordu `stan='posiada'`, ale gracz musi mieć przynajmniej jedną OCENĘ (w pliku oceny.txt). Pominięcie warunku "ma jakąkolwiek ocenę" da błędną liczbę (zliczy też graczy bez ocen z pliku gracze.txt). Zad. 7.4 — granica kategorii: junior do 19 (włącznie), senior 20–49 (włącznie), weteran od 50. Łatwo o off-by-one: 19 to junior, 20 to senior, 49 to senior, 50 to weteran. "Więcej niż jedna osoba z taką liczbą" → wypisz wszystkie tytuły (uwaga: arkusz pyta o "tytuły gier" — to znaczy gier ocenionych przez tych liderów; trzeba dolaczyć tabelę gier i wylistować). Zad. 7.2 — średnia z DOKŁADNOŚCIĄ do 2 miejsc po przecinku (`ROUND(..., 2)`), nie do całości.

    Zobacz pełne rozwiązanie krok po kroku →

Inne działy — informatyka rozszerzona