Przejdź do treści
Błędy na maturze z informatyki
Najczęstsze błędy na maturze z informatyki — z przykładami kodu

Najczęstsze błędy na maturze z informatyki — z przykładami kodu

W skrócie — pięć rzeczy, które kosztują najwięcej punktów
  • Wczytanie pliku: linia z pliku to tekst ze znakiem nowej linii na końcu. Bez int() i bez strip() całe zadanie liczy się na napisach, a nie na liczbach — i wynik jest cichy, ale zły.

  • Zakres pętli: range(1, 100) nie obejmuje setki. Zdecydowana większość „prawie dobrych" wyników to przesunięcie o jeden na początku albo na końcu zakresu.

  • Dzielenie: / zawsze daje typ float, nawet gdy dzieli się bez reszty. Do indeksów, liczby dni i wyciągania cyfr używasz //, do średniej — / plus świadome zaokrąglenie.

  • Format wyjścia: CKE porównuje odpowiedź dosłownie. Dwa miejsca po przecinku znaczy dwa również wtedy, gdy drugie to zero, a kolejność kolumn jest częścią treści zadania.

  • Excel i SQL: formuła bez dolarów rozjeżdża się przy kopiowaniu w dół, WYSZUKAJ.PIONOWO bez ostatniego argumentu zwraca „prawie trafienia", a warunek na funkcji agregującej należy do HAVING, nie do WHERE.

Na maturze z informatyki rzadko traci się punkty przez to, że ktoś nie zna algorytmu. Traci się je przez rzeczy, które na przykładowych trzech liniach danych w ogóle nie dają o sobie znać: napis zamiast liczby, pętlę kończącą się o jeden obieg za wcześnie, wynik 2.0 tam, gdzie w kluczu jest 2. Program działa, nie wyrzuca błędu, a odpowiedź jest zła.

Poniżej lista takich pułapek — każda z kodem, który uczniowie realnie piszą, wyjaśnieniem, dlaczego to nie działa, i wersją poprawną.

Wczytanie pliku: trzy błędy, które psują całą resztę zadania#

Zadania praktyczne zaczynają się od pliku tekstowego. Jeśli wczytasz go źle, wszystkie podpunkty — a jest ich zwykle cztery lub pięć — liczą się na zepsutych danych. To najdroższy pojedynczy błąd na egzaminie, bo kaskaduje.

Błąd 1: linia z pliku to tekst, nie liczba

Co uczeń pisze

plik = open("dane.txt")
liczby = []
for linia in plik:
  liczby.append(linia)

print(max(liczby))
💡
Żadnego błędu nie będzie. Wypisze się 999, choć w pliku jest 100000 — max() porównuje napisy znak po znaku.

Napisy porównują się leksykograficznie: "999" jest większe od "100000", bo decyduje pierwszy znak. Python nie ma powodu protestować — napisy wolno porównywać, więc po prostu to robi.

Wersja poprawna

with open("dane.txt") as plik:
  liczby = [int(linia) for linia in plik]

print(max(liczby))
💡
int() zamienia tekst na liczbę i sam ignoruje białe znaki na brzegach, więc strip() nie jest tu konieczny — ale nic nie kosztuje.

Błąd 2: pozostawiony znak nowej linii przy porównywaniu tekstu

int() wybacza białe znaki. Porównanie napisów — nie. W pliku z kolumną TAK / NIE każda linia poza ostatnią kończy się znakiem nowej linii, więc warunek równości z "TAK" nigdy nie jest spełniony.

Co uczeń pisze

licznik = 0
with open("odpowiedzi.txt") as plik:
  for linia in plik:
      if linia == "TAK":
          licznik += 1

print(licznik)
💡
Licznik zostaje na zerze: z pliku przychodzi napis TAK ze znakiem nowej linii, a to nie to samo co TAK.

Wersja poprawna

licznik = 0
with open("odpowiedzi.txt") as plik:
  for linia in plik:
      linia = linia.strip()
      if linia == "TAK":
          licznik += 1

print(licznik)
💡
strip() usuwa spacje, tabulatory i znaki nowej linii z obu końców. Zrób to od razu po wczytaniu linii.

Objaw: wypisana wartość wygląda dobrze, a warunek i tak jest fałszywy. Wypisz wtedy repr(linia) — zobaczysz dokładnie, co siedzi w zmiennej.

Błąd 3: split ze spacją zamiast split bez argumentu

Co uczeń pisze

for linia in plik:
  czesci = linia.split(" ")
  imie = czesci[0]
  punkty = int(czesci[1])
💡
Jeśli kolumny są oddzielone tabulatorem albo dwiema spacjami, wynik się rozjeżdża: split ze spacją zwraca dodatkowe puste napisy, a int('') kończy się wyjątkiem ValueError.

Wersja poprawna

for linia in plik:
  czesci = linia.split()
  imie = czesci[0]
  punkty = int(czesci[1])
💡
split() bez argumentu traktuje dowolny ciąg białych znaków jako jeden separator i sam pomija znak nowej linii. To domyślny wybór.

Wyjątek: gdy separatorem jest średnik albo przecinek, podajesz go jawnie — linia.split(";") — i wtedy znak nowej linii zostaje w ostatniej kolumnie. W takim pliku strip() przed rozbiciem jest obowiązkowy.

ZapisKiedy używaćCo robi ze znakiem nowej linii
linia.split()kolumny rozdzielone spacjami lub tabulatoramipomija go sam
linia.split(";")plik z jawnym separatorem, np. średnikiemzostawia w ostatniej kolumnie
linia.strip().split(";")j.w., wersja bezpiecznausuwa przed rozbiciem
linia.split(" ")praktycznie nigdyzostawia i dodaje puste napisy
Regułą jest split() bez argumentu; wersję z argumentem stosujesz tylko wtedy, gdy separator naprawdę nie jest białym znakiem.

Wniosek: zaraz po wczytaniu wypisz trzy pierwsze wiersze zbudowanej struktury i sprawdź typ jednego elementu funkcją type(). Trzydzieści sekund tutaj ratuje całe zadanie.

Zakres pętli: przesunięcie o jeden i zapomniany ostatni element#

Błąd o jeden (off-by-one) daje wynik prawie dobry — różniący się od klucza o jeden element, jedną liczbę, jeden dzień. Na oko nie da się go zauważyć. Punkt wyjścia: range(a, b) obejmuje a, nie obejmuje b. Zawsze.

ZapisCo dajeTypowe zastosowanie
range(n)0, 1, ..., n-1indeksy listy n-elementowej
range(1, n)1, 2, ..., n-1UWAGA: n nie wchodzi
range(1, n + 1)1, 2, ..., nliczby od 1 do n włącznie
range(2, int(n ** 0.5) + 1)dzielniki do pierwiastka włącznietest pierwszości
range(len(t) - 1)0, ..., len(t)-2porównywanie sąsiadów t[i] i t[i+1]
range(len(t) - 1, -1, -1)od ostatniego indeksu do 0przejście listy od końca
Cztery ostatnie wiersze to zapisy, które warto umieć odtworzyć z pamięci — pojawiają się niemal w każdym zadaniu z częścią programistyczną.

Co uczeń pisze — polecenie mówi: liczby od 1 do 1000

ile = 0
for i in range(1, 1000):
  if i % 7 == 0:
      ile += 1

print(ile)
💡
Tysiąc nigdy nie zostanie sprawdzony — a akurat dzieli się przez 7 bez reszty, więc wynik różni się od klucza o jeden.

Wersja poprawna

ile = 0
for i in range(1, 1001):
  if i % 7 == 0:
      ile += 1

print(ile)
💡
Górna granica z treści zadania jest zwykle domknięta, więc do range trafia wartość o jeden większa.

Drugi wariant tego samego błędu: porównywanie sąsiednich elementów listy.

Co uczeń pisze

t = [3, 7, 7, 2, 9]
pary = 0
for i in range(len(t)):
  if t[i] == t[i + 1]:
      pary += 1
💡
W ostatnim obiegu t[i + 1] wychodzi poza listę i program kończy się wyjątkiem IndexError. To akurat błąd głośny; gorszy jest wariant odwrotny, cicho gubiący ostatnią parę.

Wersja poprawna

t = [3, 7, 7, 2, 9]
pary = 0
for i in range(len(t) - 1):
  if t[i] == t[i + 1]:
      pary += 1

print(pary)
💡
Skoro sięgasz po element o indeks dalej, pętla musi kończyć się o jeden wcześniej. Par sąsiadów w liście n-elementowej jest n-1.

Wniosek: przed uruchomieniem podstaw skrajne wartości. Ile obiegów wykona pętla dla listy jednoelementowej? A dla pustej? Jeśli wychodzi „minus jeden", masz błąd, zanim naciśniesz uruchom. Do przećwiczenia: quizy programistyczne.

Dzielenie: kiedy ukośnik, kiedy podwójny, i skąd nagle bierze się kropka#

W Pythonie pojedynczy ukośnik zawsze zwraca liczbę zmiennoprzecinkową — także przy dzieleniu bez reszty. Stąd dwa osobne problemy: zły format wypisania i błąd przy użyciu wyniku jako indeksu.

Cztery zachowania, które trzeba znać na pamięć

print(7 / 2)      # 3.5   -> float
print(10 / 5)     # 2.0   -> float, mimo braku reszty
print(7 // 2)     # 3     -> int, zaokraglenie w dol
print(7.0 // 2)   # 3.0   -> float, bo jeden argument byl float

x = -7
print(x // 2)     # -4    -> w dol, czyli dalej od zera
print(int(x / 2)) # -3    -> obciecie czesci ulamkowej
💡
Podwójny ukośnik zaokrągla w dół, czyli dla liczb ujemnych oddala się od zera. int() obcina w stronę zera — to nie to samo.

Co uczeń pisze

suma = 100
ile = 50
print("Wynik:", suma / ile)
💡
Wypisze 2.0, a w kluczu jest 2. Porównanie jest znak po znaku, więc kropka i zero to różnica.

Wersja poprawna

suma = 100
ile = 50
print("Wynik:", suma // ile)   # 2
💡
Gdy wynik ma być całkowity, używasz podwójnego ukośnika. Gdy dzielenie może dać resztę — zaokrąglasz świadomie i wiesz, w którą stronę.

Najczęstsze miejsca, w których to boli:

  • środek tablicysrodek = len(t) / 2 daje float i lista odmówi indeksowania (TypeError); poprawnie len(t) // 2,
  • wyciąganie cyfr liczbyn % 10 daje ostatnią cyfrę, a n = n // 10 usuwa ją; z pojedynczym ukośnikiem po kilku obiegach dostajesz liczbę zmiennoprzecinkową i reszta z dzielenia przestaje być całkowita,
  • przeliczanie jednostek — liczba pełnych godzin z liczby minut to minuty // 60, a nie minuty / 60,
  • średnia — tu odwrotnie: suma // ile uciąłoby część ułamkową, więc używasz pojedynczego ukośnika i dopiero na końcu formatujesz wynik.

Wniosek: zapytaj, czy ta wartość może mieć część ułamkową. Jeśli nie — podwójny ukośnik. Jeśli tak, a wypisujesz ją do pliku — patrz sekcja o formacie wyjścia.

Porównywanie liczb zmiennoprzecinkowych znakiem równości#

Liczby ułamkowe zapisane są binarnie i większość z nich nie ma dokładnej reprezentacji — tak samo jak jedna trzecia nie ma skończonego rozwinięcia dziesiętnego. Warunek prawdziwy matematycznie bywa więc w kodzie fałszywy.

Co uczeń pisze

a = 0.1
b = 0.2
if a + b == 0.3:
  print("rowne")
else:
  print("rozne")   # to sie wypisze
💡
Warunek jest fałszywy: suma wynosi 0.30000000000000004.

Wersja poprawna

EPS = 1e-9

a = 0.1
b = 0.2
if abs((a + b) - 0.3) < EPS:
  print("rowne")
💡
Zamiast o równość pytasz, czy różnica jest dostatecznie mała. Epsilon dobierasz do skali danych; na maturze jedna milionowa wystarcza z zapasem.
Warunek zapisujesz jako ab<ε|a - b| < \varepsilon, gdzie ε\varepsilon to przyjęta tolerancja.

Skuteczniejszy sposób: nie używaj liczb zmiennoprzecinkowych tam, gdzie nie musisz. Kwoty trzymaj jako całkowitą liczbę groszy, a dzielenie odkładaj na koniec. Zadanie o sumie transakcji liczone na groszach nie ma jak się rozjechać.

Pułapka dodatkowa: round() nie zawsze zaokrągla w górę
  • Reguła połowy do parzystej: round(2.5) daje 2, a round(3.5) daje 4. Python zaokrągla wartość dokładnie w połowie do liczby parzystej, a nie zawsze w górę.

  • Gdy klucz wymaga zaokrąglania w górę: użyj math.ceil() albo zapisu (a + b - 1) // b dla liczb całkowitych dodatnich — to klasyczny sposób na policzenie „ile pełnych pudełek potrzeba".

  • Do wypisania na ekran: nie zaokrąglaj funkcją round(), tylko sformatuj napis — o tym w dalszej części wpisu.

Modyfikowanie listy w trakcie iterowania po niej#

Błąd złośliwy, bo program się nie wywala, a wynik jest częściowo poprawny — usuwa się część elementów, ale nie wszystkie.

Co uczeń pisze

liczby = [1, 0, 0, 2, 0, 3]
for x in liczby:
  if x == 0:
      liczby.remove(x)

print(liczby)   # [1, 0, 2, 3]  -- jedno zero zostalo
💡
Pętla trzyma wewnętrzny licznik pozycji. Po usunięciu elementu reszta przesuwa się w lewo, a licznik i tak rośnie — element, który wskoczył na zwolnione miejsce, zostaje pominięty.

Są trzy poprawne wyjścia; wybór zależy od tego, czy wolno podmienić listę na nową.

Wersja poprawna 1: nowa lista (najczytelniejsza)

liczby = [1, 0, 0, 2, 0, 3]
liczby = [x for x in liczby if x != 0]

print(liczby)   # [1, 2, 3]
💡
Wyrażenie listowe buduje nową listę z elementów spełniających warunek. Oryginał nie jest ruszany w trakcie. To domyślny wybór na egzaminie.

Wersja poprawna 2: iterowanie po kopii

liczby = [1, 0, 0, 2, 0, 3]
for x in liczby[:]:
  if x == 0:
      liczby.remove(x)

print(liczby)   # [1, 2, 3]
💡
Zapis z dwukropkiem w nawiasie kwadratowym tworzy kopię listy. Pętla chodzi po kopii, usuwanie dotyczy oryginału, więc numeracja się nie zmienia.

Wersja poprawna 3: przejście od końca

liczby = [1, 0, 0, 2, 0, 3]
for i in range(len(liczby) - 1, -1, -1):
  if liczby[i] == 0:
      del liczby[i]

print(liczby)   # [1, 2, 3]
💡
Idąc od końca, usuwasz elementy już minięte — przesunięcie dotyczy tylko części listy za bieżącą pozycją, więc niczego nie gubisz.

To samo dotyczy słowników: usunięcie klucza podczas iteracji kończy się wyjątkiem RuntimeError. Rozwiązanie analogiczne — zbierz klucze do usunięcia na osobną listę i usuń je po pętli.

Wniosek: w trakcie pętli po kolekcji nie zmieniaj jej rozmiaru. Zmiana wartości elementu przez indeks jest bezpieczna, dodawanie i usuwanie — nie.

Złożoność: pętla w pętli tam, gdzie wystarczy jedna#

Pliki z danymi bywają duże — tysiące, czasem dziesiątki tysięcy wierszy. Algorytm kwadratowy, który na dwudziestu przykładowych liniach kończy się natychmiast, na właściwych danych potrafi liczyć się dłużej niż trwa egzamin. Punkty przepadają nie za zły wynik, tylko za brak wyniku.

Co uczeń pisze — szukanie duplikatów

powtorzone = []
for i in range(len(dane)):
  for j in range(i + 1, len(dane)):
      if dane[i] == dane[j] and dane[i] not in powtorzone:
          powtorzone.append(dane[i])
💡
Dwie zagnieżdżone pętle po tej samej liście dają liczbę porównań rosnącą z kwadratem liczby elementów.

Wersja poprawna — jedno przejście i zbiór

widziane = set()
powtorzone = set()
for x in dane:
  if x in widziane:
      powtorzone.add(x)
  widziane.add(x)
💡
Sprawdzenie przynależności do zbioru jest niemal natychmiastowe, inaczej niż w liście, gdzie operator in przegląda wszystko po kolei.
Wersja z dwiema pętlami ma złożoność O(n2)O(n^2), wersja ze zbiorem O(n)O(n). Dla n=100,000n = 100,000 to różnica rzędu 10,10,10^,10, kontra 10510^5 operacji.

Sprawdzanie dzielników do n zamiast do pierwiastka

Drugi klasyk: test pierwszości albo szukanie dzielników liczby.

Co uczeń pisze

def pierwsza(n):
  if n < 2:
      return False
  for d in range(2, n):
      if n % d == 0:
          return False
  return True
💡
Pętla przechodzi przez wszystkie liczby aż do n — dla liczb rzędu miliarda to miliard obiegów na jedną sprawdzaną wartość.

Wersja poprawna

def pierwsza(n):
  if n < 2:
      return False
  d = 2
  while d * d <= n:
      if n % d == 0:
          return False
      d += 1
  return True
💡
Jeśli n ma dzielnik większy od pierwiastka, ma też odpowiadający mu mniejszy — wystarczy sprawdzić zakres do pierwiastka WŁĄCZNIE. Pominięcie tej równości sprawia, że dziewiątka wychodzi jako liczba pierwsza.

Zapis while d * d <= n jest bezpieczniejszy niż range(2, int(n ** 0.5) + 1), bo nie korzysta z liczb zmiennoprzecinkowych — pierwiastek liczony na float dla bardzo dużych liczb potrafi wyjść minimalnie za mały.

ZadaniePodejście naiwnePodejście, którego oczekuje klucz
czy liczba jest pierwszapętla do npętla do pierwiastka z n
ile razy występuje każdy elementpętla w pętli z licznikiemsłownik zliczający w jednym przejściu
czy element jest w zbiorze danychoperator in na liścieoperator in na zbiorze (set)
największa i najmniejsza wartośćdwa osobne przejściajedno przejście, dwie zmienne
suma z przedziału powtarzana wielokrotniepętla przy każdym zapytaniutablica sum prefiksowych
Reguła kciuka: jeśli piszesz drugą pętlę po tych samych danych, zatrzymaj się i sprawdź, czy nie da się tego zrobić słownikiem albo zbiorem.

Wniosek: przed uruchomieniem na właściwym pliku oszacuj liczbę operacji. Miliony — w porządku. Miliardy — czas przemyśleć algorytm.

Format wyjścia: CKE porównuje wynik dosłownie#

Odpowiedź zapisujesz w pliku wynikowym i to ten plik jest sprawdzany. Porównanie jest dosłowne — poprawna liczba w złym formacie to brak punktu. Najbardziej frustrująca kategoria strat, bo algorytm jest dobry.

Co uczeń pisze

srednia = 3.0999999
print("Srednia:", round(srednia, 2))   # Srednia: 3.1
💡
round() zwraca liczbę, nie napis. Liczba 3.10 nie istnieje, istnieje 3.1 — wypisze się jedna cyfra zamiast dwóch. Przy wyniku 5 wypisze się samo 5.

Wersja poprawna

srednia = 3.0999999
print("Srednia: {:.2f}".format(srednia))   # Srednia: 3.10

wynik = 5
print("Srednia: {:.2f}".format(wynik))     # Srednia: 5.00
💡
Formatowanie napisem wymusza dokładnie dwa miejsca po przecinku, także gdy są to zera. Jedyny sposób dający przewidywalny format.

Separator dziesiętny

Python wypisuje kropkę. Jeśli treść albo plik wzorcowy używa przecinka — a przy danych przenoszonych do arkusza zwykle tak jest — musisz go podmienić jawnie.

Zamiana separatora

wartosc = 1234.5
tekst = "{:.2f}".format(wartosc).replace(".", ",")
print(tekst)   # 1234,50
💡
Zamiana na gotowym napisie, na samym końcu — na napisie z przecinkiem nie policzysz już nic.

Kolejność kolumn i separator między nimi

Jeśli polecenie mówi „wypisz nazwisko, klasę i liczbę punktów", ta kolejność jest częścią zadania. Zamiana dwóch kolumn miejscami to zero punktów, mimo poprawnych danych.

Zapis pliku wynikowego

with open("wyniki.txt", "w") as out:
  for nazwisko, klasa, punkty in dane:
      out.write(nazwisko + " " + klasa + " " + str(punkty) + "\n")
💡
Separator kolumn dobierasz zgodnie z treścią. Znak nowej linii musi być na końcu każdego wiersza, inaczej cały wynik sklei się w jedną linię.
Format wyjścia — lista kontrolna
  • Liczba miejsc po przecinku: zgodna z treścią, wymuszona formatowaniem napisu, nie funkcją round().

  • Separator dziesiętny: kropka czy przecinek — sprawdź, czego używa plik z danymi wejściowymi i trzymaj się tego samego.

  • Numer podpunktu: jeżeli treść każe opisać odpowiedzi (np. „Zadanie 4.1."), opis musi być w pliku — sam wynik bez etykiety bywa nieuznawany.

  • Typ całkowity: liczba sztuk, dni czy osób nie powinna mieć kropki i zera na końcu. Jeśli ma — użyłeś pojedynczego ukośnika przy dzieleniu.

  • Puste linie i spacje na końcu: nie dodawaj ich „dla czytelności". Wypisz dokładnie to, o co proszono.

Excel: adresowanie względne kontra bezwzględne i pułapka WYSZUKAJ.PIONOWO#

Zadania z arkusza wygrywa się jedną umiejętnością: napisaniem formuły raz, tak by dało się ją skopiować na całą kolumnę. Niemal wszystkie straty punktów w tej części to formuła, która po skopiowaniu przestaje wskazywać właściwe komórki.

Formuła rozjeżdżająca się przy kopiowaniu w dół

Adresy względne przesuwają się razem z formułą. Odwołanie do wiersza z danymi ma się przesuwać — odwołanie do stałej komórki (kursu, stawki, progu) nie.

ZapisCo się dzieje przy kopiowaniu w dółCo przy kopiowaniu w prawo
B2zmienia się na B3, B4, ...zmienia się na C2, D2, ...
BB2nie zmienia sięnie zmienia się
B$2nie zmienia się (wiersz zablokowany)zmienia się na C2,D2, D2
$B2zmienia się na B3,B3, B4nie zmienia się (kolumna zablokowana)
Klawisz F4 przełącza zaznaczony adres kolejno przez cztery powyższe warianty — to najszybszy sposób ustawienia blokad podczas egzaminu.

Co uczeń pisze

C2:  =B2*F1
C3:  =B3*F2      (po skopiowaniu)
C4:  =B4*F3      (po skopiowaniu)
💡
Kurs leży w F1. Po skopiowaniu odwołanie zmienia się na F2, F3 — a tam są puste komórki. Wyniki od drugiego wiersza w dół to zera albo błędy.

Wersja poprawna

C2:  =B2*$F$1
C3:  =B3*$F$1    (po skopiowaniu)
C4:  =B4*$F$1    (po skopiowaniu)
💡
Adres kursu zablokowany dolarami zostaje ten sam we wszystkich wierszach, a adres wartości przesuwa się razem z formułą.

Adresowanie mieszane przydaje się w tabelach liczonych w dwie strony — na przykład w tabliczce mnożenia albo macierzy cen. Formuła =$A2*B$1 skopiowana na cały prostokąt działa w każdej komórce, bo w każdym kierunku zablokowany jest inny wymiar.

WYSZUKAJ.PIONOWO i czwarty argument

To najczęściej mylona funkcja arkusza. Ostatni, opcjonalny argument określa tryb wyszukiwania — a domyślnie ustawiony jest ten niebezpieczny.

Co uczeń pisze

=WYSZUKAJ.PIONOWO(A2; Dane!A:D; 3)
💡
Bez czwartego argumentu funkcja działa w trybie przybliżonym: zakłada zakres posortowany rosnąco i zwraca ostatnią wartość mniejszą lub równą szukanej. Na niesortowanej liście wynik jest przypadkowy, ale wygląda poprawnie.

Wersja poprawna

=WYSZUKAJ.PIONOWO(A2; Dane!$A$2:$D$500; 3; FAŁSZ)
💡
FAŁSZ wymusza dopasowanie dokładne. Brak wartości daje wtedy N/D, czyli użyteczną informację, zamiast cichego zwrócenia złego wiersza. Zakres w dolarach nie rozjedzie się przy kopiowaniu.

Trzy dodatkowe rzeczy, na których łatwo się potknąć:

  • Numer kolumny liczysz od początku zakresu, nie od kolumny A arkusza. W zakresie C2:F100 kolumna numer 1 to C, a nie A.
  • Szukana wartość musi być w pierwszej kolumnie zakresu. Wstecz ta funkcja nie działa; jeśli klucz leży po prawej stronie wyniku, potrzebujesz kombinacji INDEKS z PODAJ.POZYCJĘ.
  • Typy muszą się zgadzać. Numer zapisany raz jako liczba, a raz jako tekst, nie zostanie dopasowany — to najczęstsza przyczyna błędu N/D przy danych, które gołym okiem wyglądają identycznie.

Wniosek: czwarty argument FAŁSZ wpisuj odruchowo, a zakres przeszukiwany zawsze blokuj dolarami. Pozostałe funkcje — SUMA.JEŻELI, LICZ.JEŻELI, tabele przestawne — omawia dział arkusza kalkulacyjnego.

SQL: WHERE kontra HAVING, COUNT z wartościami NULL i UPDATE bez warunku#

Błędy w bazach dzielą się na dwie grupy: takie z komunikatem (łatwe) i takie, które dają poprawnie wyglądającą tabelę z niepoprawnymi liczbami. Ta druga grupa kosztuje punkty.

WHERE kontra HAVING

WHERE filtruje pojedyncze wiersze przed grupowaniem. HAVING filtruje gotowe grupy po grupowaniu. Funkcji agregującej nie wolno użyć w WHERE — w tym momencie ona jeszcze nie istnieje.

Co uczeń pisze

SELECT klasa, COUNT(*) AS ilu
FROM uczniowie
WHERE COUNT(*) > 20
GROUP BY klasa;
💡
Zapytanie zostaje odrzucone. W chwili wykonywania warunku WHERE grupy jeszcze nie powstały, więc nie ma czego zliczać.

Wersja poprawna

SELECT klasa, COUNT(*) AS ilu
FROM uczniowie
GROUP BY klasa
HAVING COUNT(*) > 20
ORDER BY ilu DESC;
💡
Warunek na funkcji agregującej trafia do HAVING, wykonywanego po GROUP BY. Kolejność klauzul jest sztywna: FROM, WHERE, GROUP BY, HAVING, ORDER BY.
WarunekGdzie go umieścićDlaczego
rok urodzenia większy niż 2006WHEREdotyczy pojedynczego wiersza
liczba uczniów w klasie większa niż 20HAVINGdotyczy grupy, używa COUNT
średnia ocen grupy powyżej 4HAVINGdotyczy grupy, używa AVG
ocena różna od NULLWHEREdotyczy pojedynczego wiersza
oba narazWHERE i HAVING jednocześnienajpierw odsiewasz wiersze, potem grupy

COUNT i wartości NULL

Różnica jest subtelna, a wraca w arkuszach niemal co sesję.

Trzy warianty COUNT, trzy różne wyniki

SELECT
COUNT(*)               AS wszystkie_wiersze,
COUNT(ocena)           AS wiersze_z_ocena,
COUNT(DISTINCT ocena)  AS roznych_ocen
FROM oceny;
💡
COUNT z gwiazdką liczy wiersze i nie patrzy na zawartość. COUNT z nazwą kolumny pomija wiersze z wartością pustą. COUNT DISTINCT liczy różne wartości, też pomijając puste.

To samo dotyczy SUM, AVG, MIN i MAXpomijają wartości puste. Konsekwencja jest istotna: AVG dzieli przez liczbę wypełnionych komórek, a nie przez liczbę wszystkich wierszy. Jeśli zadanie każe traktować brak oceny jako zero, trzeba to zapisać jawnie.

Pułapka porównania z NULL

-- ZLE: zawsze zwroci zero wierszy
SELECT * FROM uczniowie WHERE ocena = NULL;

-- DOBRZE
SELECT * FROM uczniowie WHERE ocena IS NULL;
💡
Wartość pusta nie równa się niczemu, nawet innej pustej. Do sprawdzania pustości służą wyłącznie IS NULL i IS NOT NULL.

UPDATE i DELETE bez WHERE

Zapytanie modyfikujące bez warunku dotyczy wszystkich wierszy tabeli. Na egzaminie nie ma cofnięcia jednym klawiszem, a plik z bazą jest jeden.

Co uczeń pisze

UPDATE uczniowie SET ocena = ocena + 1;
💡
Ta instrukcja podnosi ocenę w całej tabeli. Baza wykonuje ją bez ostrzeżenia, informując jedynie o liczbie zmienionych wierszy.

Wersja poprawna, z krokiem kontrolnym

-- krok 1: sprawdzenie zakresu zmiany
SELECT * FROM uczniowie WHERE klasa = '3A';

-- krok 2: dopiero teraz modyfikacja
UPDATE uczniowie SET ocena = ocena + 1 WHERE klasa = '3A';
💡
Najpierw SELECT z dokładnie tym samym warunkiem — sprawdzasz, czy wybrał właściwe wiersze. Dopiero potem zamieniasz SELECT na UPDATE.

Zanim cokolwiek zmodyfikujesz, zrób kopię pliku z bazą. Kilkanaście sekund, a chroni przed jedyną kategorią błędu, której na egzaminie nie da się naprawić. Złączenia, podzapytania i grupowanie powtórzysz w dziale baz danych.

Checklista: zanim oddasz plik#

Ostatnie dziesięć minut przeznacz na przejście tej listy, a nie na dopisywanie kolejnego podpunktu. Punkt odzyskany za format jest wart tyle samo co punkt za nowe zadanie, a kosztuje kilka razy mniej czasu.

Dane i pliki

  1. Czy pliki wynikowe mają nazwy dokładnie takie, jak podano w treści, i leżą w katalogu zdającego? Zła nazwa to niesprawdzona odpowiedź.
  2. Czy zapisałeś pliki źródłowe programu razem z wynikami? Kod jest oceniany osobno i bywa punktowany nawet wtedy, gdy wynik jest błędny.
  3. Czy plik z bazą danych albo arkuszem został zapisany po ostatniej zmianie i zamknięty?

Program

  1. Czy każda wczytana linia przeszła przez strip() albo int(), i czy sprawdziłeś typ jednego elementu funkcją type()?
  2. Czy każdy range obejmuje granicę, o którą chodzi w treści? Sprawdź osobno pierwszy i ostatni element wyniku.
  3. Czy tam, gdzie wynik ma być całkowity, użyłeś podwójnego ukośnika?
  4. Czy w kodzie nie ma porównania liczb ułamkowych znakiem równości?
  5. Czy któraś pętla nie usuwa elementów z kolekcji, po której właśnie idzie?
  6. Czy program kończy się w rozsądnym czasie na pełnym pliku, a nie tylko na przykładzie z treści?

Format odpowiedzi

  1. Czy liczba miejsc po przecinku zgadza się z poleceniem — również gdy ostatnią cyfrą jest zero?
  2. Czy separator dziesiętny i separator kolumn są takie, jak w treści?
  3. Czy kolejność kolumn odpowiada kolejności z polecenia?
  4. Czy każdy wynik jest opisany numerem podpunktu, jeśli tego wymagano?

Arkusz i baza

  1. Czy formuła skopiowana do ostatniego wiersza nadal wskazuje na właściwą komórkę stałą? Kliknij ostatnią komórkę kolumny i przeczytaj formułę.
  2. Czy WYSZUKAJ.PIONOWO ma czwarty argument FAŁSZ i zablokowany zakres?
  3. Czy w kolumnie wyników nie ma błędów N/D, DZIEL/0 ani ARG?
  4. Czy każde zapytanie UPDATE i DELETE ma klauzulę WHERE?
  5. Czy warunki na funkcjach agregujących trafiły do HAVING, a nie do WHERE?
Co zrobić z tą listą przed egzaminem
  • Przećwicz ją na starych arkuszach. Rozwiąż zadanie, a potem przejdź listę punkt po punkcie i policz, ile pozycji faktycznie wyłapało u ciebie błąd. Zwykle powtarzają się dwie lub trzy — to twoje osobiste słabe miejsca i na nich skup powtórkę. Zestawy do ćwiczeń znajdziesz w dziale arkuszy maturalnych.

  • Zrób sobie plik szablonowy. Wczytanie danych ze strip(), zapis wyniku z formatowaniem do dwóch miejsc, funkcja sprawdzająca pierwszość z pętlą do pierwiastka. Przepisanie tego z pamięci na egzaminie zajmuje minutę, którą lepiej wykorzystać na algorytm.

  • Testuj na danych brzegowych. Pusty plik, jeden wiersz, wszystkie wartości równe, wartość ujemna, zero w mianowniku. Cztery z sześciu błędów opisanych w tym wpisie wychodzą właśnie na takich danych.

  • Jeśli te same błędy wracają mimo ćwiczeń, zwykle nie chodzi o wiedzę, tylko o brak nawyku sprawdzania. To da się ustawić w kilka spotkań — napisz do nas przez formularz kontaktowy i opisz, na czym najczęściej się wykładasz.

Podoba Ci się ten artykuł? 💻

To tylko wycinek wiedzy! Na naszych korepetycjach omawiamy te tematy jeszcze dokładniej. Zapisz się na lekcję próbną i zdaj egzamin na 100%.

🚀 Zadzwoń i umów darmową konsultację

Autor: Alan Ostrowski