Mysql
 sql >> Baza danych >  >> RDS >> Mysql

Baza danych Python i MySQL:praktyczne wprowadzenie

MySQL to jeden z najpopularniejszych systemów zarządzania bazami danych (DBMS) na rynku. Zajęła drugie miejsce po Oracle DBMS w tegorocznym rankingu DB-Engines. Ponieważ większość aplikacji musi wchodzić w interakcje z danymi w jakiejś formie, języki programowania, takie jak Python, zapewniają narzędzia do przechowywania i uzyskiwania dostępu do tych źródeł danych.

Korzystając z technik omówionych w tym samouczku, będziesz w stanie efektywnie zintegrować bazę danych MySQL z aplikacją Pythona. Opracujesz małą bazę danych MySQL dla systemu oceny filmów i nauczysz się, jak wysyłać zapytania bezpośrednio z kodu Pythona.

Pod koniec tego samouczka będziesz w stanie:

  • Zidentyfikuj unikalne cechy MySQL
  • Połącz swoją aplikację do bazy danych MySQL
  • Zapytaj bazę danych, aby pobrać wymagane dane
  • Obsługuj wyjątki które występują podczas dostępu do bazy danych
  • Stosuj najlepsze praktyki podczas tworzenia aplikacji bazodanowych

Aby jak najlepiej wykorzystać ten samouczek, powinieneś mieć praktyczną wiedzę na temat pojęć Pythona, takich jak for pętle, funkcje, obsługa wyjątków i instalowanie pakietów Pythona za pomocą pip . Powinieneś także mieć podstawową wiedzę na temat systemów zarządzania relacyjnymi bazami danych i zapytań SQL, takich jak SELECT , DROP , CREATE i JOIN .

Bezpłatne pobieranie: Pobierz przykładowy rozdział ze sztuczek Pythona:książki, która pokazuje najlepsze praktyki Pythona z prostymi przykładami, które możesz zastosować natychmiast, aby napisać piękniejszy + Pythonic kod.


Porównywanie MySQL z innymi bazami danych SQL

SQL to skrót od Structured Query Language i jest szeroko stosowanym językiem programowania do zarządzania relacyjnymi bazami danych. Być może słyszałeś o różnych smakach DBMS opartych na SQL. Najpopularniejsze z nich to MySQL, PostgreSQL, SQLite i SQL Server. Wszystkie te bazy danych są zgodne ze standardami SQL, ale z różnym stopniem zgodności.

Bycie otwartym kodem od momentu powstania w 1995 roku MySQL szybko stał się liderem na rynku rozwiązań SQL. MySQL jest również częścią ekosystemu Oracle. Chociaż jego podstawowa funkcjonalność jest całkowicie darmowa, istnieje również kilka płatnych dodatków. Obecnie MySQL jest używany przez wszystkie główne firmy technologiczne, w tym Google, LinkedIn, Uber, Netflix, Twitter i inne.

Oprócz dużej społeczności open source, która udziela wsparcia, istnieje wiele innych powodów sukcesu MySQL:

  1. Łatwa instalacja: MySQL został zaprojektowany tak, aby był przyjazny dla użytkownika. Konfiguracja bazy danych MySQL jest dość prosta, a kilka powszechnie dostępnych narzędzi innych firm, takich jak phpMyAdmin, dodatkowo usprawnia proces konfiguracji. MySQL jest dostępny dla wszystkich głównych systemów operacyjnych, w tym Windows, macOS, Linux i Solaris.

  2. Prędkość: MySQL cieszy się opinią niezwykle szybkiego rozwiązania bazodanowego. Ma stosunkowo mniejszą powierzchnię i jest niezwykle skalowalny na dłuższą metę.

  3. Uprawnienia i bezpieczeństwo użytkownika: MySQL jest dostarczany z skryptem, który pozwala ustawić poziom bezpieczeństwa hasła, przypisywać hasła administratora oraz dodawać i usuwać uprawnienia konta użytkownika. Ten skrypt nie komplikuje procesu administracyjnego dla portalu zarządzania użytkownikami hostingu WWW. Inne DBMS, takie jak PostgreSQL, używają plików konfiguracyjnych, które są bardziej skomplikowane w użyciu.

Chociaż MySQL słynie z szybkości i łatwości użytkowania, dzięki PostgreSQL możesz uzyskać bardziej zaawansowane funkcje. Ponadto MySQL nie jest w pełni zgodny z SQL i ma pewne ograniczenia funkcjonalne, takie jak brak obsługi FULL JOIN klauzule.

Możesz również napotkać pewne problemy z równoczesnym odczytywaniem i pisaniem w MySQL. Jeśli w Twoim oprogramowaniu wielu użytkowników zapisuje dane jednocześnie, PostgreSQL może być bardziej odpowiednim wyborem.

Uwaga: Aby uzyskać bardziej dogłębne porównanie MySQL i PostgreSQL w kontekście rzeczywistym, zapoznaj się z artykułem Dlaczego inżynieria Uber przeszła z Postgres na MySQL.

SQL Server jest również bardzo popularnym systemem DBMS i jest znany ze swojej niezawodności, wydajności i bezpieczeństwa. Jest preferowany przez firmy, szczególnie w domenie bankowej, które regularnie mają do czynienia z dużymi obciążeniami ruchu. Jest to rozwiązanie komercyjne i jest jednym z systemów najbardziej kompatybilnych z usługami Windows.

W 2010 roku, kiedy Oracle nabyło Sun Microsystems i MySQL, wielu martwiło się o przyszłość MySQL. W tym czasie Oracle był największym konkurentem MySQL. Programiści obawiali się, że było to wrogie przejęcie przez Oracle w celu zniszczenia MySQL.

Kilku programistów kierowanych przez Michaela Wideniusa, pierwotnego autora MySQL, stworzyło rozwidlenie bazy kodu MySQL i położyło podwaliny pod MariaDB. Celem było zabezpieczenie dostępu do MySQL i utrzymanie go na zawsze za darmo.

Do tej pory MariaDB pozostaje w pełni na licencji GPL, dzięki czemu pozostaje całkowicie w domenie publicznej. Z drugiej strony niektóre funkcje MySQL są dostępne tylko z płatnymi licencjami. Ponadto MariaDB zapewnia kilka niezwykle przydatnych funkcji, które nie są obsługiwane przez serwer MySQL, takich jak rozproszony SQL i przechowywanie kolumnowe. Więcej różnic między MySQL i MariaDB znajdziesz na stronie MariaDB.

MySQL używa bardzo podobnej składni do standardowego SQL. Istnieją jednak pewne znaczące różnice wymienione w oficjalnej dokumentacji.



Instalacja MySQL Server i MySQL Connector/Python

Teraz, aby rozpocząć pracę z tym samouczkiem, musisz skonfigurować dwie rzeczy:serwer MySQL i złącze MySQL . Serwer MySQL zapewni wszystkie usługi wymagane do obsługi Twojej bazy danych. Po uruchomieniu serwera możesz połączyć z nim swoją aplikację Pythona za pomocą MySQL Connector/Python.


Instalacja serwera MySQL

Oficjalna dokumentacja szczegółowo opisuje zalecany sposób pobrania i zainstalowania serwera MySQL. Znajdziesz instrukcje dla wszystkich popularnych systemów operacyjnych, w tym Windows, macOS, Solaris, Linux i wielu innych.

W przypadku systemu Windows najlepszym sposobem jest pobranie Instalatora MySQL i pozwolenie, aby zajął się całym procesem. Menedżer instalacji pomaga również skonfigurować ustawienia bezpieczeństwa serwera MySQL. Na stronie Konta i role musisz wprowadzić hasło dla roota konto (administratora), a także opcjonalnie dodaj innych użytkowników o różnych uprawnieniach:

Chociaż podczas konfiguracji musisz określić poświadczenia konta root, możesz później zmienić te ustawienia.

Uwaga: Zapamiętaj nazwę hosta, nazwę użytkownika i hasło, ponieważ będą one wymagane do późniejszego nawiązania połączenia z serwerem MySQL.

Chociaż do tego samouczka potrzebujesz tylko serwera MySQL, możesz również skonfigurować inne przydatne narzędzia, takie jak MySQL Workbench, korzystając z tych instalatorów. Jeśli nie chcesz instalować MySQL bezpośrednio w swoim systemie operacyjnym, wdrożenie MySQL w systemie Linux z Dockerem jest wygodną alternatywą.



Instalowanie łącznika MySQL/Pythona

Sterownik bazy danych to oprogramowanie, które umożliwia aplikacji łączenie się i interakcję z systemem bazy danych. Języki programowania, takie jak Python, wymagają specjalnego sterownika, zanim będą mogły komunikować się z bazą danych od określonego dostawcy.

Te sterowniki są zwykle uzyskiwane jako moduły innych firm. Interfejs API bazy danych Pythona (DB-API) definiuje standardowy interfejs, z którym muszą być zgodne wszystkie sterowniki baz danych Pythona. Te szczegóły są udokumentowane w PEP 249. Wszystkie sterowniki baz danych Python, takie jak sqlite3 dla SQLite, psycopg dla PostgreSQL i MySQL Connector/Python dla MySQL, są zgodne z tymi zasadami implementacji.

Uwaga: Oficjalna dokumentacja MySQL używa terminu łącznik zamiast sterownika . Technicznie rzecz biorąc, konektory są związane tylko z łączeniem się z bazą danych, a nie interakcją z nią. Termin ten jest jednak często używany w odniesieniu do całego modułu dostępu do bazy danych zawierającego łącznik i kierowca.

Aby zachować spójność z dokumentacją, zobaczysz termin łącznik gdy wspomina się o MySQL.

Wiele popularnych języków programowania posiada własne API bazy danych. Na przykład Java ma interfejs API Java Database Connectivity (JDBC). Jeśli chcesz połączyć aplikację Java z bazą danych MySQL, musisz użyć łącznika MySQL JDBC, który jest zgodny z interfejsem API JDBC.

Podobnie w Pythonie musisz zainstalować łącznik Python MySQL do interakcji z bazą danych MySQL. Wiele pakietów jest zgodnych ze standardami DB-API, ale najpopularniejszym z nich jest MySQL Connector/Python. Możesz to uzyskać za pomocą pip :

$ pip install mysql-connector-python

pip instaluje łącznik jako moduł innej firmy w aktualnie aktywnym środowisku wirtualnym. Zaleca się skonfigurowanie izolowanego środowiska wirtualnego dla projektu wraz ze wszystkimi zależnościami.

Aby sprawdzić, czy instalacja się powiodła, wpisz następujące polecenie w terminalu Pythona:

>>>
>>> import mysql.connector

Jeśli powyższy kod działa bez błędów, to mysql.connector jest zainstalowany i gotowy do użycia. Jeśli napotkasz jakiekolwiek błędy, upewnij się, że znajdujesz się we właściwym środowisku wirtualnym i używasz właściwego interpretera Pythona.

Upewnij się, że instalujesz prawidłowy mysql-connector-python pakiet, który jest implementacją w czystym Pythonie. Uważaj na podobnie nazwane, ale teraz przestarzałe złącza, takie jak mysql-connector .




Nawiązywanie połączenia z serwerem MySQL

MySQL jest serwerem system zarządzania bazą danych. Jeden serwer może zawierać wiele baz danych. Aby wchodzić w interakcję z bazą danych, musisz najpierw nawiązać połączenie z serwerem. Ogólny przepływ pracy programu w Pythonie, który współdziała z bazą danych opartą na MySQL, wygląda następująco:

  1. Połącz się z serwerem MySQL.
  2. Utwórz nową bazę danych.
  3. Połącz się z nowo utworzoną lub istniejącą bazą danych.
  4. Wykonaj zapytanie SQL i pobierz wyniki.
  5. Poinformuj bazę danych o jakichkolwiek zmianach w tabeli.
  6. Zamknij połączenie z serwerem MySQL.

Jest to ogólny przepływ pracy, który może się różnić w zależności od indywidualnej aplikacji. Ale bez względu na aplikację, pierwszym krokiem jest połączenie bazy danych z aplikacją.


Nawiązywanie połączenia

Pierwszym krokiem w interakcji z serwerem MySQL jest nawiązanie połączenia. Aby to zrobić, potrzebujesz connect() z mysql.connector moduł. Ta funkcja przyjmuje parametry, takie jak host , user i password i zwraca MySQLConnection obiekt. Możesz otrzymać te poświadczenia jako dane wejściowe od użytkownika i przekazać je do connect() :

from getpass import getpass
from mysql.connector import connect, Error

try:
    with connect(
        host="localhost",
        user=input("Enter username: "),
        password=getpass("Enter password: "),
    ) as connection:
        print(connection)
except Error as e:
    print(e)

Powyższy kod wykorzystuje wprowadzone dane logowania do nawiązania połączenia z serwerem MySQL. W zamian otrzymasz MySQLConnection obiekt, który jest przechowywany w connection zmienny. Od teraz będziesz używać tej zmiennej, aby uzyskać dostęp do swojego serwera MySQL.

W powyższym kodzie należy zwrócić uwagę na kilka ważnych rzeczy:

  • Zawsze powinieneś mieć do czynienia z wyjątkami, które mogą pojawić się podczas nawiązywania połączenia z serwerem MySQL. Dlatego używasz tryexcept zablokuj, aby przechwycić i wydrukować wszelkie wyjątki, które możesz napotkać.

  • Zawsze należy zamykać połączenie po zakończeniu uzyskiwania dostępu do bazy danych. Pozostawienie nieużywanych otwartych połączeń może prowadzić do kilku nieoczekiwanych błędów i problemów z wydajnością. Powyższy kod wykorzystuje menedżera kontekstu używającego with , który abstrahuje proces czyszczenia połączenia.

  • Nigdy nie należy zakodować danych logowania na stałe , czyli twoją nazwę użytkownika i hasło bezpośrednio w skrypcie Pythona. Jest to zła praktyka przy wdrażaniu i stanowi poważne zagrożenie bezpieczeństwa. Powyższy kod prosi użytkownika o podanie danych logowania. Używa wbudowanego getpass moduł do ukrycia hasła. Chociaż jest to lepsze niż sztywne kodowanie, istnieją inne, bezpieczniejsze sposoby przechowywania poufnych informacji, takie jak używanie zmiennych środowiskowych.

Udało Ci się nawiązać połączenie między programem a serwerem MySQL, ale nadal musisz utworzyć nową bazę danych lub połączyć się z istniejącą bazą danych na serwerze.



Tworzenie nowej bazy danych

W ostatniej sekcji nawiązałeś połączenie z serwerem MySQL. Aby utworzyć nową bazę danych, musisz wykonać instrukcję SQL:

CREATE DATABASE books_db;

Powyższe oświadczenie utworzy nową bazę danych o nazwie books_db .

Uwaga: W MySQL obowiązkowe jest wstawienie średnika (; ) na końcu instrukcji, co oznacza zakończenie zapytania. Jednak MySQL Connector/Python automatycznie dołącza średnik na końcu zapytań, więc nie ma potrzeby używania go w kodzie Pythona.

Aby wykonać zapytanie SQL w Pythonie, będziesz musiał użyć kursora, który odcina dostęp do rekordów bazy danych. MySQL Connector/Python udostępnia MySQLCursor klasa, która tworzy instancje obiektów, które mogą wykonywać zapytania MySQL w Pythonie. Instancja MySQLCursor klasa jest również nazywana cursor .

cursor obiekty korzystają z MySQLConnection obiekt do interakcji z serwerem MySQL. Tworzenie cursor , użyj .cursor() metoda twojego connection zmienna:

cursor = connection.cursor()

Powyższy kod daje instancję MySQLCursor klasa.

Zapytanie, które musi zostać wykonane, jest wysyłane do cursor.execute() w formacie ciągu. W tej szczególnej okazji wyślesz CREATE DATABASE zapytanie do cursor.execute() :

from getpass import getpass
from mysql.connector import connect, Error

try:
    with connect(
        host="localhost",
        user=input("Enter username: "),
        password=getpass("Enter password: "),
    ) as connection:
        create_db_query = "CREATE DATABASE online_movie_rating"
        with connection.cursor() as cursor:
            cursor.execute(create_db_query)
except Error as e:
    print(e)

Po wykonaniu powyższego kodu będziesz mieć nową bazę danych o nazwie online_movie_rating na twoim serwerze MySQL.

CREATE DATABASE zapytanie jest przechowywane jako ciąg w create_db_query zmienna, a następnie przekazana do cursor.execute() do wykonania. Kod używa menedżera kontekstu z cursor obiekt do obsługi procesu czyszczenia.

W tym miejscu może pojawić się błąd, jeśli baza danych o tej samej nazwie już istnieje na Twoim serwerze. Aby to potwierdzić, możesz wyświetlić nazwy wszystkich baz danych na swoim serwerze. Korzystanie z tego samego MySQLConnection wcześniejszy obiekt, wykonaj SHOW DATABASES oświadczenie:

>>>
>>> show_db_query = "SHOW DATABASES"
>>> with connection.cursor() as cursor:
...     cursor.execute(show_db_query)
...     for db in cursor:
...         print(db)
...
('information_schema',)
('mysql',)
('online_movie_rating',)
('performance_schema',)
('sys',)

Powyższy kod wyświetla nazwy wszystkich baz danych znajdujących się obecnie na serwerze MySQL. SHOW DATABASES polecenie wyświetla również niektóre bazy danych, których nie utworzyłeś na swoim serwerze, na przykład information_schema , performance_schema , i tak dalej. Te bazy danych są generowane automatycznie przez serwer MySQL i zapewniają dostęp do różnych metadanych baz danych oraz ustawień serwera MySQL.

Utworzyłeś nową bazę danych w tej sekcji, wykonując CREATE DATABASE oświadczenie. W następnej sekcji zobaczysz, jak połączyć się z bazą danych, która już istnieje.



Łączenie z istniejącą bazą danych

W ostatniej sekcji utworzyłeś nową bazę danych o nazwie online_movie_rating . Jednak nadal nie masz z nim połączenia. W wielu sytuacjach będziesz już mieć bazę danych MySQL, którą chcesz połączyć z aplikacją Pythona.

Możesz to zrobić za pomocą tej samej connect() funkcja, której użyłeś wcześniej, wysyłając dodatkowy parametr o nazwie database :

from getpass import getpass
from mysql.connector import connect, Error

try:
    with connect(
        host="localhost",
        user=input("Enter username: "),
        password=getpass("Enter password: "),
        database="online_movie_rating",
    ) as connection:
        print(connection)
except Error as e:
    print(e)

Powyższy kod jest bardzo podobny do skryptu połączenia, którego używałeś wcześniej. Jedyną zmianą tutaj jest dodatkowa database parametr, w którym nazwa Twojej bazy danych jest przekazywana do connect() . Po wykonaniu tego skryptu zostaniesz połączony z online_movie_rating baza danych.




Tworzenie, zmienianie i upuszczanie tabeli

W tej sekcji dowiesz się, jak wykonywać podstawowe zapytania DDL, takie jak CREATE , DROP i ALTER z Pythonem. Szybko rzucisz okiem na bazę danych MySQL, której będziesz używać w dalszej części tego samouczka. Utworzysz również wszystkie tabele wymagane dla bazy danych i nauczysz się później dokonywać modyfikacji w tych tabelach.


Definiowanie schematu bazy danych

Możesz zacząć od utworzenia schematu bazy danych dla systemu oceny filmów online. Baza danych będzie się składać z trzech tabel:

  1. movies zawiera ogólne informacje o filmach i ma następujące atrybuty:
    • id
    • title
    • release_year
    • genre
    • collection_in_mil
  2. reviewers zawiera informacje o osobach, które opublikowały recenzje lub oceny, i ma następujące atrybuty:
    • id
    • first_name
    • last_name
  3. ratings zawiera informacje o opublikowanych ocenach i ma następujące atrybuty:
    • movie_id (klucz obcy)
    • reviewer_id (klucz obcy)
    • rating

Rzeczywisty system oceny filmów, taki jak IMDb, musiałby przechowywać kilka innych atrybutów, takich jak e-maile, listy obsady filmów i tak dalej. Jeśli chcesz, możesz dodać więcej tabel i atrybutów do tej bazy danych. Ale te trzy tabele wystarczą do celów tego samouczka.

Poniższy obraz przedstawia schemat bazy danych:

Tabele w tej bazie danych są ze sobą powiązane. movies i reviewers będzie miał wiele do wielu związek, ponieważ jeden film może być recenzowany przez wielu recenzentów, a jeden recenzent może recenzować wiele filmów. ratings tabela łączy movies tabela z reviewers tabela.



Tworzenie tabel za pomocą CREATE TABLE Oświadczenie

Teraz, aby utworzyć nową tabelę w MySQL, musisz użyć CREATE TABLE oświadczenie. Następujące zapytanie MySQL utworzy movies tabela dla Twojego online_movie_rating baza danych:

CREATE TABLE movies(
    id INT AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(100),
    release_year YEAR(4),
    genre VARCHAR(100),
    collection_in_mil INT
);

Jeśli przyglądałeś się już wcześniej wyrażeniom SQL, większość z powyższych zapytań może mieć sens. Istnieją jednak pewne różnice w składni MySQL, o których powinieneś wiedzieć.

Na przykład MySQL ma wiele różnych typów danych do przeglądania, w tym YEAR , INT , BIGINT , i tak dalej. Ponadto MySQL używa AUTO_INCREMENT słowo kluczowe, gdy wartość kolumny ma być automatycznie zwiększana po wstawieniu nowych rekordów.

Aby utworzyć nową tabelę, musisz przekazać to zapytanie do cursor.execute() , który akceptuje zapytanie MySQL i wykonuje zapytanie w podłączonej bazie danych MySQL:

create_movies_table_query = """
CREATE TABLE movies(
    id INT AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(100),
    release_year YEAR(4),
    genre VARCHAR(100),
    collection_in_mil INT
)
"""
with connection.cursor() as cursor:
    cursor.execute(create_movies_table_query)
    connection.commit()

Teraz masz movies tabeli w Twojej bazie danych. Przekazujesz create_movies_table_query do cursor.execute() , który wykonuje wymagane wykonanie.

Uwaga: connection zmienna odnosi się do MySQLConnection obiekt, który został zwrócony po połączeniu z bazą danych.

Zwróć także uwagę na connection.commit() oświadczenie na końcu kodu. Domyślnie łącznik MySQL nie automatycznie zatwierdza transakcji. W MySQL modyfikacje wymienione w transakcji występują tylko wtedy, gdy używasz COMMIT polecenie w końcu. Zawsze wywołuj tę metodę po każdej transakcji, aby dokonać zmian w rzeczywistej tabeli.

Tak jak w przypadku movies tabeli, wykonaj następujący skrypt, aby utworzyć reviewers tabela:

create_reviewers_table_query = """
CREATE TABLE reviewers (
    id INT AUTO_INCREMENT PRIMARY KEY,
    first_name VARCHAR(100),
    last_name VARCHAR(100)
)
"""
with connection.cursor() as cursor:
    cursor.execute(create_reviewers_table_query)
    connection.commit()

W razie potrzeby możesz dodać więcej informacji o recenzencie, na przykład jego identyfikator e-mail lub dane demograficzne. Ale first_name i last_name na razie będzie służył twoim celom.

Na koniec możesz utworzyć ratings tabela za pomocą następującego skryptu:

create_ratings_table_query = """
CREATE TABLE ratings (
    movie_id INT,
    reviewer_id INT,
    rating DECIMAL(2,1),
    FOREIGN KEY(movie_id) REFERENCES movies(id),
    FOREIGN KEY(reviewer_id) REFERENCES reviewers(id),
    PRIMARY KEY(movie_id, reviewer_id)
)
"""
with connection.cursor() as cursor:
    cursor.execute(create_ratings_table_query)
    connection.commit()

Implementacja relacji kluczy obcych w MySQL jest nieco inna i ograniczona w porównaniu ze standardowym SQL. W MySQL zarówno rodzic, jak i dziecko w ograniczeniu klucza obcego muszą używać tego samego silnika pamięci .

Aparat pamięci masowej to podstawowy składnik oprogramowania używany przez system zarządzania bazą danych do wykonywania operacji SQL. W MySQL silniki pamięci masowej są dostępne w dwóch różnych wersjach:

  1. Transakcyjne silniki pamięci masowej są bezpieczne dla transakcji i umożliwiają wycofywanie transakcji za pomocą prostych poleceń, takich jak rollback . Wiele popularnych silników MySQL, w tym InnoDB i NDB, należy do tej kategorii.

  2. Nietransakcyjne silniki pamięci masowej polegać na skomplikowanym ręcznym kodzie, aby cofnąć instrukcje zatwierdzone w bazie danych. MyISAM, MEMORY i wiele innych silników MySQL jest nietransakcyjnych.

InnoDB to domyślny i najpopularniejszy silnik pamięci masowej. Pomaga zachować integralność danych, obsługując ograniczenia klucza obcego. Oznacza to, że każda operacja CRUD na kluczu obcym jest sprawdzana, aby upewnić się, że nie prowadzi do niespójności w różnych tabelach.

Zwróć też uwagę, że ratings tabela używa kolumn movie_id i reviewer_id , oba klucze obce, łącznie jako klucz podstawowy . Ten krok gwarantuje, że recenzent nie może dwukrotnie ocenić tego samego filmu.

Możesz ponownie użyć tego samego kursora do wielu egzekucji. W takim przypadku wszystkie egzekucje stałyby się jedną transakcją niepodzielną, a nie wieloma oddzielnymi transakcjami. Na przykład możesz wykonać wszystkie CREATE TABLE wyciągi z jednym kursorem, a następnie zatwierdź transakcję tylko raz:

with connection.cursor() as cursor:
    cursor.execute(create_movies_table_query)
    cursor.execute(create_reviewers_table_query)
    cursor.execute(create_ratings_table_query)
    connection.commit()

Powyższy kod najpierw wykona wszystkie trzy CREATE sprawozdania. Następnie wyśle ​​COMMIT polecenie do serwera MySQL, który zatwierdza transakcję. Możesz także użyć .rollback() wysłać ROLLBACK polecenie do serwera MySQL i usuń wszystkie zmiany danych z transakcji.



Wyświetlanie schematu tabeli za pomocą DESCRIBE Oświadczenie

Teraz, po utworzeniu wszystkich trzech tabel, możesz spojrzeć na ich schemat za pomocą następującej instrukcji SQL:

DESCRIBE <table_name>;

Aby uzyskać niektóre wyniki z cursor obiekt, musisz użyć cursor.fetchall() . Ta metoda pobiera wszystkie wiersze z ostatniej wykonanej instrukcji. Zakładając, że masz już MySQLConnection obiekt w connection zmienna, możesz wydrukować wszystkie wyniki pobrane przez cursor.fetchall() :

>>>
>>> show_table_query = "DESCRIBE movies"
>>> with connection.cursor() as cursor:
...     cursor.execute(show_table_query)
...     # Fetch rows from last executed query
...     result = cursor.fetchall()
...     for row in result:
...         print(row)
...
('id', 'int(11)', 'NO', 'PRI', None, 'auto_increment')
('title', 'varchar(100)', 'YES', '', None, '')
('release_year', 'year(4)', 'YES', '', None, '')
('genre', 'varchar(100)', 'YES', '', None, '')
('collection_in_mil', 'int(11)', 'YES', '', None, '')

Po wykonaniu powyższego kodu powinieneś otrzymać tabelę zawierającą informacje o wszystkich kolumnach w movies stół. W przypadku każdej kolumny otrzymasz szczegółowe informacje, takie jak typ danych kolumny, czy kolumna jest kluczem podstawowym itd.



Modyfikowanie schematu tabeli za pomocą ALTER Oświadczenie

W movies tabeli, masz kolumnę o nazwie collection_in_mil , który zawiera kolekcję kas filmowych w milionach dolarów. Możesz napisać następującą instrukcję MySQL, aby zmodyfikować typ danych collection_in_mil atrybut z INT na DECIMAL :

ALTER TABLE movies MODIFY COLUMN collection_in_mil DECIMAL(4,1);

DECIMAL(4,1) oznacza liczbę dziesiętną, która może mieć maksymalnie 4 cyfry, z czego 1 jest dziesiętny, np. 120.1 , 3.4 , 38.0 , i tak dalej. Po wykonaniu ALTER TABLE oświadczenie, możesz wyświetlić zaktualizowany schemat tabeli za pomocą DESCRIBE :

>>>
>>> alter_table_query = """
... ALTER TABLE movies
... MODIFY COLUMN collection_in_mil DECIMAL(4,1)
... """
>>> show_table_query = "DESCRIBE movies"
>>> with connection.cursor() as cursor:
...     cursor.execute(alter_table_query)
...     cursor.execute(show_table_query)
...     # Fetch rows from last executed query
...     result = cursor.fetchall()
...     print("Movie Table Schema after alteration:")
...     for row in result:
...         print(row)
...
Movie Table Schema after alteration
('id', 'int(11)', 'NO', 'PRI', None, 'auto_increment')
('title', 'varchar(100)', 'YES', '', None, '')
('release_year', 'year(4)', 'YES', '', None, '')
('genre', 'varchar(100)', 'YES', '', None, '')
('collection_in_mil', 'decimal(4,1)', 'YES', '', None, '')

Jak pokazano w danych wyjściowych, collection_in_mil atrybut jest teraz typu DECIMAL(4,1) . Zauważ również, że w powyższym kodzie wywołujesz cursor.execute() dwa razy. Ale cursor.fetchall() pobiera wiersze tylko z ostatniego wykonanego zapytania, którym jest show_table_query .



Usuwanie tabel za pomocą DROP Oświadczenie

Aby usunąć tabelę, musisz wykonać DROP TABLE oświadczenie w MySQL. Usunięcie tabeli jest nieodwracalne proces. Jeśli wykonasz poniższy kod, będziesz musiał wywołać CREATE TABLE zapytaj ponownie, aby użyć ratings tabela w kolejnych sekcjach.

Aby usunąć ratings table, wyślij drop_table_query do cursor.execute() :

drop_table_query = "DROP TABLE ratings"
with connection.cursor() as cursor:
    cursor.execute(drop_table_query)

Jeśli wykonasz powyższy kod, pomyślnie usuniesz ratings tabela.




Wstawianie rekordów do tabel

W ostatniej sekcji utworzyłeś trzy tabele w swojej bazie danych:movies , reviewers i ratings . Teraz musisz wypełnić te tabele danymi. W tej sekcji omówimy dwa różne sposoby wstawiania rekordów w MySQL Connector for Python.

Pierwsza metoda, .execute() , działa dobrze, gdy liczba rekordów jest niewielka, a rekordy mogą być zakodowane na stałe. Druga metoda, .executemany() , jest bardziej popularny i lepiej nadaje się do rzeczywistych scenariuszy.


Korzystanie z .execute()

Pierwsze podejście wykorzystuje ten sam cursor.execute() metoda, z której korzystałeś do tej pory. Piszesz INSERT INTO zapytanie w ciągu i przekazać je do cursor.execute() . Możesz użyć tej metody, aby wstawić dane do movies tabela.

Dla porównania, movies tabela ma pięć atrybutów:

  1. id
  2. title
  3. release_year
  4. genre
  5. collection_in_mil

Nie musisz dodawać danych dla id jako AUTO_INCREMENT automatycznie oblicza id dla Was. Poniższy skrypt wstawia rekordy do movies tabela:

insert_movies_query = """
INSERT INTO movies (title, release_year, genre, collection_in_mil)
VALUES
    ("Forrest Gump", 1994, "Drama", 330.2),
    ("3 Idiots", 2009, "Drama", 2.4),
    ("Eternal Sunshine of the Spotless Mind", 2004, "Drama", 34.5),
    ("Good Will Hunting", 1997, "Drama", 138.1),
    ("Skyfall", 2012, "Action", 304.6),
    ("Gladiator", 2000, "Action", 188.7),
    ("Black", 2005, "Drama", 3.0),
    ("Titanic", 1997, "Romance", 659.2),
    ("The Shawshank Redemption", 1994, "Drama",28.4),
    ("Udaan", 2010, "Drama", 1.5),
    ("Home Alone", 1990, "Comedy", 286.9),
    ("Casablanca", 1942, "Romance", 1.0),
    ("Avengers: Endgame", 2019, "Action", 858.8),
    ("Night of the Living Dead", 1968, "Horror", 2.5),
    ("The Godfather", 1972, "Crime", 135.6),
    ("Haider", 2014, "Action", 4.2),
    ("Inception", 2010, "Adventure", 293.7),
    ("Evil", 2003, "Horror", 1.3),
    ("Toy Story 4", 2019, "Animation", 434.9),
    ("Air Force One", 1997, "Drama", 138.1),
    ("The Dark Knight", 2008, "Action",535.4),
    ("Bhaag Milkha Bhaag", 2013, "Sport", 4.1),
    ("The Lion King", 1994, "Animation", 423.6),
    ("Pulp Fiction", 1994, "Crime", 108.8),
    ("Kai Po Che", 2013, "Sport", 6.0),
    ("Beasts of No Nation", 2015, "War", 1.4),
    ("Andadhun", 2018, "Thriller", 2.9),
    ("The Silence of the Lambs", 1991, "Crime", 68.2),
    ("Deadpool", 2016, "Action", 363.6),
    ("Drishyam", 2015, "Mystery", 3.0)
"""
with connection.cursor() as cursor:
    cursor.execute(insert_movies_query)
    connection.commit()

The movies table is now loaded with thirty records. The code calls connection.commit() at the end. It’s crucial to call .commit() after preforming any modifications to a table.



Using .executemany()

The previous approach is more suitable when the number of records is fairly small and you can write these records directly into the code. But this is rarely true. You’ll often have this data stored in some other file, or the data will be generated by a different script and will need to be added to the MySQL database.

This is where .executemany() comes in handy. It accepts two parameters:

  1. A query that contains placeholders for the records that need to be inserted
  2. A list that contains all records that you wish to insert

The following example inserts records for the reviewers tabela:

insert_reviewers_query = """
INSERT INTO reviewers
(first_name, last_name)
VALUES ( %s, %s )
"""
reviewers_records = [
    ("Chaitanya", "Baweja"),
    ("Mary", "Cooper"),
    ("John", "Wayne"),
    ("Thomas", "Stoneman"),
    ("Penny", "Hofstadter"),
    ("Mitchell", "Marsh"),
    ("Wyatt", "Skaggs"),
    ("Andre", "Veiga"),
    ("Sheldon", "Cooper"),
    ("Kimbra", "Masters"),
    ("Kat", "Dennings"),
    ("Bruce", "Wayne"),
    ("Domingo", "Cortes"),
    ("Rajesh", "Koothrappali"),
    ("Ben", "Glocker"),
    ("Mahinder", "Dhoni"),
    ("Akbar", "Khan"),
    ("Howard", "Wolowitz"),
    ("Pinkie", "Petit"),
    ("Gurkaran", "Singh"),
    ("Amy", "Farah Fowler"),
    ("Marlon", "Crafford"),
]
with connection.cursor() as cursor:
    cursor.executemany(insert_reviewers_query, reviewers_records)
    connection.commit()

In the script above, you pass both the query and the list of records as arguments to .executemany() . These records could have been fetched from a file or from the user and stored in the reviewers_records list.

The code uses %s as a placeholder for the two strings that had to be inserted in the insert_reviewers_query . Placeholders act as format specifiers and help reserve a spot for a variable inside a string. The specified variable is then added to this spot during execution.

You can similarly use .executemany() to insert records in the ratings tabela:

insert_ratings_query = """
INSERT INTO ratings
(rating, movie_id, reviewer_id)
VALUES ( %s, %s, %s)
"""
ratings_records = [
    (6.4, 17, 5), (5.6, 19, 1), (6.3, 22, 14), (5.1, 21, 17),
    (5.0, 5, 5), (6.5, 21, 5), (8.5, 30, 13), (9.7, 6, 4),
    (8.5, 24, 12), (9.9, 14, 9), (8.7, 26, 14), (9.9, 6, 10),
    (5.1, 30, 6), (5.4, 18, 16), (6.2, 6, 20), (7.3, 21, 19),
    (8.1, 17, 18), (5.0, 7, 2), (9.8, 23, 3), (8.0, 22, 9),
    (8.5, 11, 13), (5.0, 5, 11), (5.7, 8, 2), (7.6, 25, 19),
    (5.2, 18, 15), (9.7, 13, 3), (5.8, 18, 8), (5.8, 30, 15),
    (8.4, 21, 18), (6.2, 23, 16), (7.0, 10, 18), (9.5, 30, 20),
    (8.9, 3, 19), (6.4, 12, 2), (7.8, 12, 22), (9.9, 15, 13),
    (7.5, 20, 17), (9.0, 25, 6), (8.5, 23, 2), (5.3, 30, 17),
    (6.4, 5, 10), (8.1, 5, 21), (5.7, 22, 1), (6.3, 28, 4),
    (9.8, 13, 1)
]
with connection.cursor() as cursor:
    cursor.executemany(insert_ratings_query, ratings_records)
    connection.commit()

All three tables are now populated with data. You now have a fully functional online movie rating database. The next step is to understand how to interact with this database.




Reading Records From the Database

Until now, you’ve been building your database. Now it’s time to perform some queries on it and find some interesting properties from this dataset. In this section, you’ll learn how to read records from database tables using the SELECT oświadczenie.


Reading Records Using the SELECT Statement

To retrieve records, you need to send a SELECT query to cursor.execute() . Then you use cursor.fetchall() to extract the retrieved table in the form of a list of rows or records.

Try writing a MySQL query to select all records from the movies table and send it to .execute() :

>>>
>>> select_movies_query = "SELECT * FROM movies LIMIT 5"
>>> with connection.cursor() as cursor:
...     cursor.execute(select_movies_query)
...     result = cursor.fetchall()
...     for row in result:
...         print(row)
...
(1, 'Forrest Gump', 1994, 'Drama', Decimal('330.2'))
(2, '3 Idiots', 2009, 'Drama', Decimal('2.4'))
(3, 'Eternal Sunshine of the Spotless Mind', 2004, 'Drama', Decimal('34.5'))
(4, 'Good Will Hunting', 1997, 'Drama', Decimal('138.1'))
(5, 'Skyfall', 2012, 'Action', Decimal('304.6'))

The result variable holds the records returned from using .fetchall() . It’s a list of tuples representing individual records from the table.

In the query above, you use the LIMIT clause to constrain the number of rows that are received from the SELECT oświadczenie. Developers often use LIMIT to perform pagination when handling large volumes of data.

In MySQL, the LIMIT clause takes one or two nonnegative numeric arguments. When using one argument, you specify the maximum number of rows to return. Since your query includes LIMIT 5 , only the first 5 records are fetched. When using both arguments, you can also specify the offset of the first row to return:

SELECT * FROM movies LIMIT 2,5;

The first argument specifies an offset of 2 , and the second argument constrains the number of returned rows to 5 . The above query will return rows 3 to 7.

You can also query for selected columns:

>>>
>>> select_movies_query = "SELECT title, release_year FROM movies LIMIT 5"
>>> with connection.cursor() as cursor:
...     cursor.execute(select_movies_query)
...     for row in cursor.fetchall():
...         print(row)
...
('Forrest Gump', 1994)
('3 Idiots', 2009)
('Eternal Sunshine of the Spotless Mind', 2004)
('Good Will Hunting', 1997)
('Skyfall', 2012)

Now, the code outputs values only from the two specified columns:title and release_year .



Filtering Results Using the WHERE Clause

You can filter table records by specific criteria using the WHERE klauzula. For example, to retrieve all movies with a box office collection greater than $300 million, you could run the following query:

SELECT title, collection_in_mil
FROM movies
WHERE collection_in_mil > 300;

You can also use ORDER BY clause in the last query to sort the results from the highest to the lowest earner:

>>>
>>> select_movies_query = """
... SELECT title, collection_in_mil
... FROM movies
... WHERE collection_in_mil > 300
... ORDER BY collection_in_mil DESC
... """
>>> with connection.cursor() as cursor:
...     cursor.execute(select_movies_query)
...     for movie in cursor.fetchall():
...         print(movie)
...
('Avengers: Endgame', Decimal('858.8'))
('Titanic', Decimal('659.2'))
('The Dark Knight', Decimal('535.4'))
('Toy Story 4', Decimal('434.9'))
('The Lion King', Decimal('423.6'))
('Deadpool', Decimal('363.6'))
('Forrest Gump', Decimal('330.2'))
('Skyfall', Decimal('304.6'))

MySQL offers a plethora of string formatting operations like CONCAT for concatenating strings. Often, websites will show the movie title along with its release year to avoid confusion. To retrieve the titles of the top five grossing movies, concatenated with their release years, you can write the following query:

>>>
>>> select_movies_query = """
... SELECT CONCAT(title, " (", release_year, ")"),
...       collection_in_mil
... FROM movies
... ORDER BY collection_in_mil DESC
... LIMIT 5
... """
>>> with connection.cursor() as cursor:
...     cursor.execute(select_movies_query)
...     for movie in cursor.fetchall():
...         print(movie)
...
('Avengers: Endgame (2019)', Decimal('858.8'))
('Titanic (1997)', Decimal('659.2'))
('The Dark Knight (2008)', Decimal('535.4'))
('Toy Story 4 (2019)', Decimal('434.9'))
('The Lion King (1994)', Decimal('423.6'))

If you don’t want to use the LIMIT clause and you don’t need to fetch all the records, then the cursor object has .fetchone() and .fetchmany() methods as well:

  • .fetchone() retrieves either the next row of the result, as a tuple, or None if no more rows are available.
  • .fetchmany() retrieves the next set of rows from the result as a list of tuples. It has a size argument, which defaults to 1 , that you can use to specify the number of rows you need to fetch. If no more rows are available, then the method returns an empty list.

Try retrieving the titles of the five highest-grossing movies concatenated with their release years again, but this time use .fetchmany() :

>>>
>>> select_movies_query = """
... SELECT CONCAT(title, " (", release_year, ")"),
...       collection_in_mil
... FROM movies
... ORDER BY collection_in_mil DESC
... """
>>> with connection.cursor() as cursor:
...     cursor.execute(select_movies_query)
...     for movie in cursor.fetchmany(size=5):
...         print(movie)
...     cursor.fetchall()
...
('Avengers: Endgame (2019)', Decimal('858.8'))
('Titanic (1997)', Decimal('659.2'))
('The Dark Knight (2008)', Decimal('535.4'))
('Toy Story 4 (2019)', Decimal('434.9'))
('The Lion King (1994)', Decimal('423.6'))

The output with .fetchmany() is similar to what you received when you used the LIMIT klauzula. You might have noticed the additional cursor.fetchall() call at the end. You do this to clean all the remaining results that weren’t read by .fetchmany() .

It’s necessary to clean all unread results before executing any other statements on the same connection. Otherwise, an InternalError: Unread result found exception will be raised.




Handling Multiple Tables Using the JOIN Statement

If you found the queries in the last section to be quite straightforward, don’t worry. You can make your SELECT queries as complex as you want using the same methods from the last section.

Let’s look at some slightly more complex JOIN queries. If you want to find out the name of the top five highest-rated movies in your database, then you can run the following query:

>>>
>>> select_movies_query = """
... SELECT title, AVG(rating) as average_rating
... FROM ratings
... INNER JOIN movies
...     ON movies.id = ratings.movie_id
... GROUP BY movie_id
... ORDER BY average_rating DESC
... LIMIT 5
... """
>>> with connection.cursor() as cursor:
...     cursor.execute(select_movies_query)
...     for movie in cursor.fetchall():
...         print(movie)
...
('Night of the Living Dead', Decimal('9.90000'))
('The Godfather', Decimal('9.90000'))
('Avengers: Endgame', Decimal('9.75000'))
('Eternal Sunshine of the Spotless Mind', Decimal('8.90000'))
('Beasts of No Nation', Decimal('8.70000'))

As shown above, Night of the Living Dead and The Godfather are tied as the highest-rated movies in your online_movie_rating database.

To find the name of the reviewer who gave the most ratings, write the following query:

>>>
>>> select_movies_query = """
... SELECT CONCAT(first_name, " ", last_name), COUNT(*) as num
... FROM reviewers
... INNER JOIN ratings
...     ON reviewers.id = ratings.reviewer_id
... GROUP BY reviewer_id
... ORDER BY num DESC
... LIMIT 1
... """
>>> with connection.cursor() as cursor:
...     cursor.execute(select_movies_query)
...     for movie in cursor.fetchall():
...         print(movie)
...
('Mary Cooper', 4)

Mary Cooper is the most frequent reviewer in this database. As seen above, it doesn’t matter how complicated the query is because it’s ultimately handled by the MySQL server. Your process for executing a query will always remain the same:pass the query to cursor.execute() and fetch the results using .fetchall() .



Updating and Deleting Records From the Database

In this section, you’ll be updating and deleting records from the database. Both of these operations can be performed on either a single record or multiple records in the table. You’ll select the rows that need to be modified using the WHERE clause.


UPDATE Command

One of the reviewers in your database, Amy Farah Fowler , is now married to Sheldon Cooper . Her last name has now changed to Cooper , so you need to update your database accordingly. For updating records, MySQL uses the UPDATE statement:

update_query = """
UPDATE
    reviewers
SET
    last_name = "Cooper"
WHERE
    first_name = "Amy"
"""
with connection.cursor() as cursor:
    cursor.execute(update_query)
    connection.commit()

The code passes the update query to cursor.execute() , and .commit() brings the required changes to the reviewers table.

Uwaga: In the UPDATE query, the WHERE clause helps specify the records that need to be updated. If you don’t use WHERE , then all records will be updated!

Suppose you need to provide an option that allows reviewers to modify ratings. A reviewer will provide three values, movie_id , reviewer_id , and the new rating . The code will display the record after performing the specified modification.

Assuming that movie_id = 18 , reviewer_id = 15 , and the new rating = 5.0 , you can use the following MySQL queries to perform the required modification:

UPDATE
    ratings
SET
    rating = 5.0
WHERE
    movie_id = 18 AND reviewer_id = 15;

SELECT *
FROM ratings
WHERE
    movie_id = 18 AND reviewer_id = 15;

The above queries first update the rating and then display it. You can create a complete Python script that establises a connection with the database and allows the reviewer to modify a rating:

from getpass import getpass
from mysql.connector import connect, Error

movie_id = input("Enter movie id: ")
reviewer_id = input("Enter reviewer id: ")
new_rating = input("Enter new rating: ")
update_query = """
UPDATE
    ratings
SET
    rating = "%s"
WHERE
    movie_id = "%s" AND reviewer_id = "%s";

SELECT *
FROM ratings
WHERE
    movie_id = "%s" AND reviewer_id = "%s"
""" % (
    new_rating,
    movie_id,
    reviewer_id,
    movie_id,
    reviewer_id,
)

try:
    with connect(
        host="localhost",
        user=input("Enter username: "),
        password=getpass("Enter password: "),
        database="online_movie_rating",
    ) as connection:
        with connection.cursor() as cursor:
            for result in cursor.execute(update_query, multi=True):
                if result.with_rows:
                    print(result.fetchall())
            connection.commit()
except Error as e:
    print(e)

Save this code to a file named modify_ratings.py . The above code uses %s placeholders to insert the received input in the update_query string. For the first time in this tutorial, you have multiple queries inside a single string. To pass multiple queries to a single cursor.execute() , you need to set the method’s multi argument to True .

If multi is True , then cursor.execute() returns an iterator. Each item in the iterator corresponds to a cursor object that executes a statement passed in the query. The above code runs a for loop on this iterator and then calls .fetchall() on each cursor obiekt.

Uwaga: Running .fetchall() on all cursor objects is important. To execute a new statement on the same connection, you must ensure that there are no unread results from previous executions. If there are unread results, then you’ll receive an exception.

If no result set is fetched on an operation, then .fetchall() raises an exception. To avoid this error, in the code above you use the cursor.with_rows property, which indicates whether the most recently executed operation produced rows.

While this code should solve your purpose, the WHERE clause is a prime target for web hackers in its current state. It’s vulnerable to what is called a SQL injection attack, which can allow malicious actors to either corrupt or misuse your database.

Warning :Don’t try the below inputs on your database! They will corrupt your table and you’ll need to recreate it.

For example, if a user sends movie_id=18 , reviewer_id=15 , and the new rating=5.0 as input, then the output looks like this:

$ python modify_ratings.py
Enter movie id: 18
Enter reviewer id: 15
Enter new rating: 5.0
Enter username: <user_name>
Enter password:
[(18, 15, Decimal('5.0'))]

The rating with movie_id=18 and reviewer_id=15 has been changed to 5.0 . But if you were hacker, then you might send a hidden command in your input:

$ python modify_ratings.py
Enter movie id: 18
Enter reviewer id: 15"; UPDATE reviewers SET last_name = "A
Enter new rating: 5.0
Enter username: <user_name>
Enter password:
[(18, 15, Decimal('5.0'))]

Again, the output shows that the specified rating has been changed to 5.0 . What’s changed?

The hacker sneaked in an update query while entering the reviewer_id . The update query, update reviewers set last_name = "A , changes the last_name of all records in the reviewers table to "A" . You can see this change if you print out the reviewers tabela:

>>>
>>> select_query = """
... SELECT first_name, last_name
... FROM reviewers
... """
>>> with connection.cursor() as cursor:
...     cursor.execute(select_query)
...     for reviewer in cursor.fetchall():
...         print(reviewer)
...
('Chaitanya', 'A')
('Mary', 'A')
('John', 'A')
('Thomas', 'A')
('Penny', 'A')
('Mitchell', 'A')
('Wyatt', 'A')
('Andre', 'A')
('Sheldon', 'A')
('Kimbra', 'A')
('Kat', 'A')
('Bruce', 'A')
('Domingo', 'A')
('Rajesh', 'A')
('Ben', 'A')
('Mahinder', 'A')
('Akbar', 'A')
('Howard', 'A')
('Pinkie', 'A')
('Gurkaran', 'A')
('Amy', 'A')
('Marlon', 'A')

The above code displays the first_name and last_name for all records in the reviewers stół. The SQL injection attack corrupted this table by changing the last_name of all records to "A" .

There’s a quick fix to prevent such attacks. Don’t add the query values provided by the user directly to your query string. Instead, update the modify_ratings.py script to send these query values as arguments to .execute() :

from getpass import getpass
from mysql.connector import connect, Error

movie_id = input("Enter movie id: ")
reviewer_id = input("Enter reviewer id: ")
new_rating = input("Enter new rating: ")
update_query = """
UPDATE
    ratings
SET
    rating = %s
WHERE
    movie_id = %s AND reviewer_id = %s;

SELECT *
FROM ratings
WHERE
    movie_id = %s AND reviewer_id = %s
"""
val_tuple = (
    new_rating,
    movie_id,
    reviewer_id,
    movie_id,
    reviewer_id,
)

try:
    with connect(
        host="localhost",
        user=input("Enter username: "),
        password=getpass("Enter password: "),
        database="online_movie_rating",
    ) as connection:
        with connection.cursor() as cursor:
            for result in cursor.execute(update_query, val_tuple, multi=True):
                if result.with_rows:
                    print(result.fetchall())
            connection.commit()
except Error as e:
    print(e)

Notice that the %s placeholders are no longer in string quotes. Strings passed to the placeholders might contain some special characters. If necessary, these can be correctly escaped by the underlying library.

cursor.execute() makes sure that the values in the tuple received as argument are of the required data type. If a user tries to sneak in some problematic characters, then the code will raise an exception:

$ python modify_ratings.py
Enter movie id: 18
Enter reviewer id: 15"; UPDATE reviewers SET last_name = "A
Enter new rating: 5.0
Enter username: <user_name>
Enter password:
1292 (22007): Truncated incorrect DOUBLE value: '15";
UPDATE reviewers SET last_name = "A'

cursor.execute() will raise an exception if it finds any unwanted characters in the user input. You should use this approach whenever you incorporate user input in a query. There are other ways of preventing SQL injection attacks as well.



DELETE Command

Deleting records works very similarly to updating records. You use the DELETE statement to remove selected records.

Uwaga: Deleting is an irreversible process. If you don’t use the WHERE clause, then all records from the specified table will be deleted. You’ll need to run the INSERT INTO query again to get back the deleted records.

It’s recommended that you first run a SELECT query with the same filter to make sure that you’re deleting the right records. For example, to remove all ratings given by reviewer_id = 2 , you should first run the corresponding SELECT query:

>>>
>>> select_movies_query = """
... SELECT reviewer_id, movie_id FROM ratings
... WHERE reviewer_id = 2
... """
>>> with connection.cursor() as cursor:
...     cursor.execute(select_movies_query)
...     for movie in cursor.fetchall():
...         print(movie)
...
(2, 7)
(2, 8)
(2, 12)
(2, 23)

The above code snippet outputs the reviewer_id and movie_id for records in the ratings table where reviewer_id = 2 . Once you’ve confirmed that these are the records that you need to delete, you can run a DELETE query with the same filter:

delete_query = "DELETE FROM ratings WHERE reviewer_id = 2"
with connection.cursor() as cursor:
    cursor.execute(delete_query)
    connection.commit()

With this query, you remove all ratings given by the reviewer with reviewer_id = 2 from the ratings table.




Other Ways to Connect Python and MySQL

In this tutorial, you saw MySQL Connector/Python, which is the officially recommended means of interacting with a MySQL database from a Python application. There are two other popular connectors:

  1. mysqlclient is a library that is a close competitor to the official connector and is actively updated with new features. Because its core is written in C, it has better performance than the pure-Python official connector. A big drawback is that it’s fairly difficult to set up and install, especially on Windows.

  2. MySQLdb is a legacy software that’s still used in commercial applications. It’s written in C and is faster than MySQL Connector/Python but is available only for Python 2.

These connectors act as interfaces between your program and a MySQL database, and you send your SQL queries through them. But many developers prefer using an object-oriented paradigm rather than SQL queries to manipulate data.

Object-relational mapping (ORM) is a technique that allows you to query and manipulate data from a database directly using an object-oriented language. An ORM library encapsulates the code needed to manipulate data, which eliminates the need to use even a tiny bit of SQL. Here are the most popular Python ORMs for SQL-based databases:

  1. SQLAlchemy is an ORM that facilitates communication between Python and other SQL databases. You can create different engines for different databases like MySQL, PostgreSQL, SQLite, and so on. SQLAlchemy is commonly used alongside the pandas library to provide complete data-handling functionality.

  2. peewee is a lightweight and fast ORM that’s quick to set up. This is quite useful when your interaction with the database is limited to extracting a few records. For example, if you need to copy selected records from a MySQL database into a CSV file, then peewee might be your best choice.

  3. Django ORM is one of the most powerful features of Django and is supplied alongside the Django web framework. It can interact with a variety of databases such as SQLite, PostgreSQL, and MySQL. Many Django-based applications use the Django ORM for data modeling and basic queries but often switch to SQLAlchemy for more complex requirements.

You might find one of these approaches to be more suitable for your application. If you’re not sure which one to use, then it’s best to go with the officially recommended MySQL Connector/Python that you saw in action in this tutorial.



Conclusion

In this tutorial, you saw how to use MySQL Connector/Python to integrate a MySQL database with your Python application. You also saw some unique features of a MySQL database that differentiate it from other SQL databases.

Along the way, you learned some programming best practices that are worth considering when it comes to establishing a connection, creating tables, and inserting and updating records in a database application. You also developed a sample MySQL database for an online movie rating system and interacted with it directly from your Python application.

In this tutorial, you learned how to:

  • Connect your Python app with a MySQL database
  • Bring data from a MySQL database into Python for further analysis
  • Execute SQL queries from your Python application
  • Handle exceptions while accessing the database
  • Prevent SQL injection attacks on your application

If you’re interested, Python also has connectors for other DBMSs like MongoDB and PostgreSQL. For more information, check out Python Database Tutorials.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Używanie mysql concat() w klauzuli WHERE?

  2. Samouczek MySQL — konfiguracja i zarządzanie SSL na serwerze MySQL

  3. JSON_ARRAY() – Utwórz tablicę JSON z listy wartości w MySQL

  4. Czy możesz użyć aliasu w klauzuli WHERE w mysql?

  5. Jak porównywać wydajność MySQL za pomocą SysBench?