Pandas & NumPy Academy · Lekcja

Wykonywanie zapytań SQL z Pandas

Wykonuj dowolne instrukcje SELECT za pomocą pd.read_sql_query i bezpiecznie parametryzuj zapytania, aby uniknąć SQL injection.

Lekcja 2 z 413 kroki

Wykonywanie zapytań SQL z Pandas to bezpłatna lekcja Pandas & NumPy Academy na CoddyKit. To lekcja 2 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Pandas & NumPy Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Pandas & NumPy Academy zawiera 4 lekcji w sumie.

pd.read_sql: ujednolicony interfejs

Pandas udostępnia trzy funkcje odczytu danych SQL: pd.read_sql() (ogólny wrapper), pd.read_sql_table() (odczytuje pełną tabelę po nazwie) oraz pd.read_sql_query() (wykonuje dowolne zapytanie SQL). W większości procesów analitycznych pd.read_sql_query() jest najbardziej uniwersalna, ponieważ umożliwia napisanie dowolnego polecenia SELECT z filtrowaniem, łączeniem i agregowaniem danych, zanim trafią one do Pandas. Wykorzystanie SQL do wykonywania wymagających operacji, a Pandas do końcowej analizy, jest często wydajniejsze niż załadowanie wszystkiego i filtrowanie w języku Python.

import pandas as pd
import sqlalchemy as sa

engine = sa.create_engine('sqlite:///ecommerce.db')

# Three equivalent patterns
df1 = pd.read_sql('SELECT * FROM orders LIMIT 100', con=engine)
df2 = pd.read_sql_table('orders', con=engine)  # full table
df3 = pd.read_sql_query('SELECT * FROM orders LIMIT 100', con=engine)

print(df3.head())
print(df3.columns.tolist())

Filtrowanie na poziomie bazy danych

Dane należy zawsze filtrować w SQL, zamiast ładować wszystko i filtrować w Pandas. Baza danych z odpowiednimi indeksami może wykonać klauzulę WHERE na milionach wierszy i zwrócić tylko tysiące z nich w ciągu milisekund, podczas gdy Pandas musi najpierw załadować gigabajty danych. Złota zasada brzmi: przenoś predykaty do bazy danych. Używaj WHERE do filtrowania wierszy, SELECT col1, col2 do wyboru kolumn, a LIMIT podczas tworzenia kodu, aby szybko wyświetlać podgląd wyników.

import pandas as pd
import sqlalchemy as sa

engine = sa.create_engine('sqlite:///sales.db')

# Filter and project at SQL level — only fetch what you need
query = '''
    SELECT order_id, customer_id, amount, status
    FROM orders
    WHERE status = 'completed'
      AND order_date >= '2024-01-01'
      AND amount > 50
    LIMIT 1000
'''

df = pd.read_sql_query(query, con=engine)
print(f'Rows: {len(df)}, Columns: {list(df.columns)}')

Agregowanie w SQL a agregowanie w Pandas

W przypadku prostych podsumowań grup dla dużych tabel agregacje SQL są wydajniejsze niż Pandas, ponieważ silnik bazy danych może korzystać z indeksów, wykonywania równoległego i agregacji haszującej na dysku. Gdy tabela jest duża, należy używać SQL do operacji GROUP BY oraz SUM/COUNT/AVG. Zagregowany wynik (niewielki obiekt DataFrame) można załadować do Pandas w celu dalszej analizy, wizualizacji lub połączenia z innymi danymi. W przypadku złożonych, niestandardowych agregacji, których nie da się wyrazić w SQL, należy załadować do Pandas przefiltrowany podzbiór i użyć tam groupby.

import pandas as pd
import sqlalchemy as sa

engine = sa.create_engine('sqlite:///sales.db')

# Aggregate in SQL — returns a small result set
query = '''
    SELECT region,
           COUNT(*) AS order_count,
           ROUND(SUM(amount), 2) AS total_revenue,
           ROUND(AVG(amount), 2) AS avg_order_value
    FROM orders
    WHERE status = 'completed'
    GROUP BY region
    ORDER BY total_revenue DESC
'''

df = pd.read_sql_query(query, con=engine)
print(df)

Operacje JOIN w zapytaniach SQL

Operacje SQL typu JOIN są wydajniejsze niż merge() w Pandas w przypadku łączenia dużych tabel, ponieważ baza danych może korzystać z wyszukiwania za pomocą indeksów. Należy napisać operację łączenia w SQL i odebrać w Pandas wstępnie połączony, potencjalnie przefiltrowany wynik. W analizie wielu tabel pojedyncze zapytanie SQL z wieloma operacjami JOIN jest zwykle szybsze niż odczyt każdej tabeli osobno i łączenie ich w Pandas, szczególnie gdy jedna z tabel zawiera miliony wierszy, a operacja łączenia znacznie zmniejsza wynik.

import pandas as pd
import sqlalchemy as sa

engine = sa.create_engine('sqlite:///ecommerce.db')

query = '''
    SELECT o.order_id,
           c.customer_name,
           c.country,
           p.product_name,
           o.amount
    FROM orders o
    JOIN customers c ON o.customer_id = c.id
    JOIN products p ON o.product_id = p.id
    WHERE o.status = 'completed'
    LIMIT 500
'''

df = pd.read_sql_query(query, con=engine)
print(df.head())

Używanie zmiennych języka Python w zapytaniach

Aby bezpiecznie wstawiać zmienne języka Python do zapytań SQL, należy używać SQLAlchemy text() z nazwanymi parametrami. Symbole zastępcze należy definiować za pomocą :param_name w ciągu zapytania, a następnie przekazać słownik do argumentu params funkcji read_sql_query. Działa to zarówno dla pojedynczych wartości, jak i — w przypadku niektórych baz danych — dla list. Należy unikać używania f-stringów lub formatowania za pomocą % do budowania ciągów zapytań ze zmiennych; są one niebezpieczne nawet w zastosowaniach wewnętrznych.

import pandas as pd
import sqlalchemy as sa

engine = sa.create_engine('sqlite:///sales.db')

# Python variables to inject
min_amount = 200.0
start_date = '2024-01-01'
end_date = '2024-12-31'

query = sa.text('''
    SELECT * FROM orders
    WHERE amount > :min_amount
      AND order_date BETWEEN :start_date AND :end_date
''')

with engine.connect() as conn:
    df = pd.read_sql_query(query, con=conn,
                           params={'min_amount': min_amount,
                                   'start_date': start_date,
                                   'end_date': end_date})
print(f'{len(df)} orders found')

Używanie CTE i podzapytań

Złożone analizy często wymagają użycia wyrażeń tablicowych (Common Table Expressions, CTE) lub podzapytań. Są one w pełni obsługiwane przez pd.read_sql_query — wystarczy przekazać cały wieloklauzulowy kod SQL jako ciąg zapytania. CTE (wprowadzane za pomocą słowa kluczowego WITH) zwiększają czytelność złożonych zapytań, nadając nazwy wynikom pośrednim. Jest to przydatne przy obliczaniu sum narastających, tworzeniu rankingów w grupach oraz wieloetapowym filtrowaniu, które w Pandas wymagałoby obszernego kodu.

import pandas as pd
import sqlalchemy as sa

engine = sa.create_engine('sqlite:///sales.db')

query = '''
    WITH monthly_revenue AS (
        SELECT strftime('%Y-%m', order_date) AS month,
               SUM(amount) AS revenue
        FROM orders
        WHERE status = 'completed'
        GROUP BY month
    )
    SELECT month,
           revenue,
           revenue - LAG(revenue) OVER (ORDER BY month)
               AS month_over_month_change
    FROM monthly_revenue
    ORDER BY month
'''

df = pd.read_sql_query(query, con=engine)
print(df.tail())

Odczytywanie z indeksem DatetimeIndex

Podczas odczytywania danych szeregów czasowych z bazy danych należy ustawić kolumnę ze znacznikiem czasu jako indeks obiektu DataFrame, przekazując do funkcji read_sql_query argumenty index_col='date_column' i parse_dates=['date_column']. Dzięki temu bezpośrednio uzyskuje się DatetimeIndex, co umożliwia stosowanie w Pandas wycinania opartego na czasie (df['2024-01']), resamplingu i obliczeń kroczących bez dodatkowego przetwarzania. Argument parse_dates informuje Pandas o konieczności przekonwertowania kolumny na typ datetime64.

import pandas as pd
import sqlalchemy as sa

engine = sa.create_engine('sqlite:///metrics.db')

df = pd.read_sql_query(
    'SELECT recorded_at, metric_value FROM daily_metrics ORDER BY recorded_at',
    con=engine,
    index_col='recorded_at',
    parse_dates=['recorded_at']
)
print(df.index.dtype)   # datetime64[ns]
print(df['2024-06'])    # Slice by month directly

Profilowanie wolnych zapytań

Gdy zapytanie działa wolno, należy dodać słowo kluczowe SQL EXPLAIN (lub EXPLAIN QUERY PLAN w SQLite) przed instrukcją SELECT, aby wyświetlić plan wykonania zapytania przez bazę danych. Należy zwrócić uwagę na pełne skanowanie tabel ('SCAN TABLE') w miejscach, w których oczekiwane jest wyszukiwanie za pomocą indeksu ('SEARCH TABLE'). Brak indeksów dla kolumn używanych w WHERE i JOIN jest najczęstszą przyczyną wolnego działania zapytań. Należy utworzyć odpowiedni indeks w bazie danych i ponownie sprawdzić plan za pomocą EXPLAIN przed ponownym uruchomieniem potoku Pandas.

import sqlalchemy as sa

engine = sa.create_engine('sqlite:///sales.db')

# Check if the query uses an index
with engine.connect() as conn:
    plan = conn.execute(sa.text(
        'EXPLAIN QUERY PLAN SELECT * FROM orders WHERE customer_id = 42'
    )).fetchall()
    for row in plan:
        print(row)
    # Look for 'SEARCH TABLE orders USING INDEX' — not 'SCAN TABLE'

Stronicowanie dużych zbiorów wyników

Podczas interaktywnego przetwarzania dużego zbioru wyników (na przykład przetwarzania jednej strony wyników naraz) należy użyć elementów SQL LIMIT i OFFSET, aby zaimplementować stronicowanie. Należy pobierać po N wierszy, przetwarzać je, a następnie pobierać kolejne N. Chociaż takie rozwiązanie jest mniej wydajne niż podejście z użyciem chunksize (które utrzymuje kursor), stronicowanie jest przydatne, gdy wiersze muszą być wyświetlane stopniowo w raporcie lub gdy wyniki wielu zapytań są łączone.

import pandas as pd
import sqlalchemy as sa

engine = sa.create_engine('sqlite:///sales.db')
page_size = 10000
offset = 0

while True:
    query = sa.text(
        'SELECT * FROM orders ORDER BY order_id LIMIT :limit OFFSET :offset'
    )
    with engine.connect() as conn:
        df = pd.read_sql_query(query, con=conn,
                               params={'limit': page_size, 'offset': offset})
    if len(df) == 0:
        break
    print(f'Page at offset {offset}: {len(df)} rows')
    offset += page_size

Łączenie zapytań SQL z logiką Pandas

Najbardziej zaawansowanym wzorcem jest potok hybrydowy: SQL służy do zgrubnego filtrowania i agregowania, a Pandas do szczegółowych przekształceń, które trudno wyrazić w SQL (tabele przestawne, analizowanie ciągów znaków, funkcje apply, okna kroczące). Należy odczytać z SQL zarządzalny zbiór wyników (tysiące wierszy), a następnie łączyć operacje Pandas na wynikowej strukturze DataFrame. Pozwala to połączyć zalety obu narzędzi, utrzymując przepływ danych w jednym procesie języka Python.

import pandas as pd
import sqlalchemy as sa

engine = sa.create_engine('sqlite:///sales.db')

# SQL: coarse filter and join
df = pd.read_sql_query('''
    SELECT o.customer_id, o.amount, o.order_date, c.country
    FROM orders o JOIN customers c ON o.customer_id = c.id
    WHERE o.status = 'completed'
''', con=engine, parse_dates=['order_date'])

# Pandas: rolling 30-day revenue per country
df = df.sort_values('order_date')
df['rolling_30d'] = (
    df.groupby('country')['amount']
    .transform(lambda x: x.rolling('30D').sum())
)
print(df.head())

Obsługa błędów zapytań do bazy danych

Zapytania do bazy danych mogą kończyć się niepowodzeniem z powodu przekroczenia limitu czasu połączenia sieciowego, błędów składni lub utraty połączenia. Należy umieszczać wywołania bazy danych w blokach try-except, które przechwytują sqlalchemy.exc.OperationalError w przypadku problemów z połączeniem oraz sqlalchemy.exc.ProgrammingError w przypadku błędów składni SQL. Należy rejestrować błąd wraz z kontekstem (zapytaniem i parametrami), a następnie ponawiać próbę z wykładniczym zwiększaniem opóźnienia albo bezpiecznie zakończyć działanie. W potokach produkcyjnych kluczowe jest rozróżnianie błędów przejściowych (które można ponowić) od trwałych (wymagających poprawienia SQL).

import pandas as pd
import sqlalchemy as sa

engine = sa.create_engine('sqlite:///sales.db')

try:
    df = pd.read_sql_query(
        'SELECT * FROM nonexistent_table',
        con=engine
    )
except sa.exc.OperationalError as e:
    print(f'Connection or table error: {e}')
except sa.exc.ProgrammingError as e:
    print(f'SQL syntax error: {e}')
except Exception as e:
    print(f'Unexpected error: {type(e).__name__}: {e}')

Szybki test

Sprawdź swoją znajomość zagadnień analizy danych omówionych w tej lekcji.

Podsumowanie lekcji

W tej lekcji dowiedziałeś się, że: pd.read_sql_query() wykonuje dowolne zapytanie SQL SELECT i zwraca strukturę DataFrame, przenoszenie filtrowania i agregowania do SQL jest wydajniejsze niż wczytywanie całych tabel do Pandas, a potoki hybrydowe łączą SQL do zgrubnej redukcji danych z Pandas do szczegółowych, niestandardowych przekształceń. Następnie dowiesz się, jak zapisywać struktury DataFrame z powrotem do tabel w bazie danych.

Bezpłatny start

Ucz się Python dzięki korepetycjom AI — za darmo

Pisz i uruchamiaj kod w przeglądarce, otrzymuj natychmiastową pomoc od korepetytora AI dostępnego 24/7 i kontynuuj naukę w sieci lub w aplikacji.

Kursy
30
Lekcje
120

Często zadawane pytania

Czy lekcja „Wykonywanie zapytań SQL z Pandas” jest bezpłatna?

Tak — pełny tekst „Wykonywanie zapytań SQL z Pandas” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Pandas & NumPy Academy, przejdź na CoddyKit PRO. Kurs Pandas & NumPy Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Wykonywanie zapytań SQL z Pandas”?

Wykonuj dowolne instrukcje SELECT za pomocą pd.read_sql_query i bezpiecznie parametryzuj zapytania, aby uniknąć SQL injection. Ćwiczysz Pandas & NumPy Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Pandas & NumPy Academy?

Nie wymagamy żadnego doświadczenia. Pandas & NumPy Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 4.

Ile czasu zajmuje lekcja „Wykonywanie zapytań SQL z Pandas”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Pandas & NumPy Academy?

Tak. Każda lekcja Pandas & NumPy Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Łączenie z bazą danych za pomocą SQLAlchemy
  2. Wykonywanie zapytań SQL z Pandas
  3. Zapisywanie obiektów DataFrame w tabelach bazy danych
  4. Pandas a SQL: wybór odpowiedniego narzędzia
← Powrót do Pandas & NumPy Academy