MySQL / Python + MySQL холбох

Python + MySQL холбох

Python-оос MySQL өгөгдлийн сантай харилцахын тулд mysql-connector-python package ашиглана. Энэ хичээлд холболт үүсгэх, cursor ашиглах, parameterized query бичих, context manager ашиглах аргыг сурна.

mysql-connector-python суулгах

bash
pip install mysql-connector-python

Энгийн холболт

python
import mysql.connector

conn = mysql.connector.connect(
    host='localhost',
    user='root',
    password='нууц_үг',
    database='myshop'
)

cursor = conn.cursor()
cursor.execute('SELECT * FROM users')

rows = cursor.fetchall()
for row in rows:
    print(row)

cursor.close()
conn.close()

Cursor бол SQL командыг гүйцэтгэж, үр дүнг авах объект юм. Нэг холболт дээр олон cursor үүсгэж болно.

Context Manager — зөв арга

with блок ашиглахад холболт автоматаар хаагдана — close() мартах аюулгүй:

python
import mysql.connector

config = {
    'host': 'localhost',
    'user': 'root',
    'password': 'нууц_үг',
    'database': 'myshop'
}

with mysql.connector.connect(**config) as conn:
    with conn.cursor() as cursor:
        cursor.execute('SELECT * FROM users')
        rows = cursor.fetchall()
        for row in rows:
            print(row)

-- Энд гарахад conn, cursor автоматаар хаагдана

Cursor-ийн төрлүүд

Үндсэн cursor — tuple буцаана

python
cursor = conn.cursor()
cursor.execute('SELECT id, name, email FROM users')

row = cursor.fetchone()   -- нэг мөр
print(row)                -- (1, 'Болд', 'bold@example.com')

rows = cursor.fetchall()  -- бүх мөр

Dictionary cursor — dict буцаана

python
cursor = conn.cursor(dictionary=True)
cursor.execute('SELECT id, name, email FROM users')

row = cursor.fetchone()
print(row['name'])   -- 'Болд'
print(row['email'])  -- 'bold@example.com'

Dictionary cursor ашиглах нь баганыг индексээр биш нэрээр нэвтрэх боломж олгодог тул унших хялбар.

Parameterized Query — SQL Injection-оос хамгаалах

Хэрэглэгчийн оролтыг шууд query-д залгах нь аюултай:

python
-- БУРУУ — SQL Injection-д өртөмтгий!
name = input('Нэр оруул: ')
cursor.execute(f"SELECT * FROM users WHERE name = '{name}'")

Зөв арга — %s placeholder ашиглах:

python
-- ЗӨВ — parameterized query
def find_user_by_name(cursor, name):
    query = 'SELECT * FROM users WHERE name = %s'
    cursor.execute(query, (name,))  -- tuple байх шаардлагатай
    return cursor.fetchall()

-- Ашиглах
with mysql.connector.connect(**config) as conn:
    with conn.cursor(dictionary=True) as cursor:
        users = find_user_by_name(cursor, 'Болд')
        print(users)

Утга нэг байсан ч (name,) гэж tuple хэлбэрт өгнө.

Олон утга:

python
def find_products_in_range(cursor, min_price, max_price):
    query = 'SELECT * FROM products WHERE price BETWEEN %s AND %s'
    cursor.execute(query, (min_price, max_price))
    return cursor.fetchall()

CRUD үйлдлүүд

Мэдээлэл нэмэх — INSERT

python
def create_user(conn, name, email, password_hash):
    with conn.cursor() as cursor:
        query = '''
            INSERT INTO users (name, email, password)
            VALUES (%s, %s, %s)
        '''
        cursor.execute(query, (name, email, password_hash))
    conn.commit()
    return cursor.lastrowid   -- шинэ мөрийн ID

conn.commit() дуудахгүй бол өөрчлөлт хадгалагдахгүй.

Олон мөр нэг дор нэмэх — executemany

python
def insert_products(conn, products):
    with conn.cursor() as cursor:
        query = 'INSERT INTO products (name, price, stock) VALUES (%s, %s, %s)'
        cursor.executemany(query, products)
    conn.commit()
    print(f'{cursor.rowcount} бараа нэмэгдлээ')

-- Ашиглах
products = [
    ('Гар утас', 1_200_000, 50),
    ('Чихэвч', 150_000, 200),
    ('Цэнэгч', 45_000, 500),
]

with mysql.connector.connect(**config) as conn:
    insert_products(conn, products)

Мэдээлэл хайх — SELECT

python
def get_all_products(conn):
    with conn.cursor(dictionary=True) as cursor:
        cursor.execute('SELECT * FROM products ORDER BY price DESC')
        return cursor.fetchall()

def get_product_by_id(conn, product_id):
    with conn.cursor(dictionary=True) as cursor:
        cursor.execute(
            'SELECT * FROM products WHERE id = %s',
            (product_id,)
        )
        return cursor.fetchone()  -- None буцаах боломжтой

Мэдээлэл өөрчлөх — UPDATE

python
def update_stock(conn, product_id, new_stock):
    with conn.cursor() as cursor:
        cursor.execute(
            'UPDATE products SET stock = %s WHERE id = %s',
            (new_stock, product_id)
        )
    conn.commit()

    if cursor.rowcount == 0:
        raise ValueError(f'ID={product_id} бараа олдсонгүй')

    return cursor.rowcount

Мэдээлэл устгах — DELETE

python
def delete_product(conn, product_id):
    with conn.cursor() as cursor:
        cursor.execute(
            'DELETE FROM products WHERE id = %s',
            (product_id,)
        )
    conn.commit()
    return cursor.rowcount > 0

Transaction

python
def place_order(conn, user_id, items):
    try:
        conn.start_transaction()

        with conn.cursor() as cursor:
            -- Захиалга үүсгэх
            cursor.execute(
                'INSERT INTO orders (user_id, total) VALUES (%s, 0)',
                (user_id,)
            )
            order_id = cursor.lastrowid

            total = 0

            -- Мөрүүд нэмэх
            for item in items:
                cursor.execute(
                    '''INSERT INTO order_items
                       (order_id, product_id, quantity, price)
                       VALUES (%s, %s, %s, %s)''',
                    (order_id, item['product_id'], item['quantity'], item['price'])
                )
                total += item['quantity'] * item['price']

            -- Нийт дүн шинэчлэх
            cursor.execute(
                'UPDATE orders SET total = %s WHERE id = %s',
                (total, order_id)
            )

        conn.commit()
        print(f'Захиалга #{order_id} амжилттай')
        return order_id

    except mysql.connector.Error as err:
        conn.rollback()
        print(f'Алдаа: {err}')
        raise

Алдаа зохицуулах

python
import mysql.connector
from mysql.connector import errorcode

try:
    conn = mysql.connector.connect(**config)
except mysql.connector.Error as err:
    if err.errno == errorcode.ER_ACCESS_DENIED_ERROR:
        print('Хэрэглэгчийн нэр эсвэл нууц үг буруу')
    elif err.errno == errorcode.ER_BAD_DB_ERROR:
        print('Өгөгдлийн сан олдсонгүй')
    else:
        print(f'Холболтын алдаа: {err}')
    raise

Бүтэн жишээ: Бараа удирдах

python
import mysql.connector

config = {
    'host': 'localhost',
    'user': 'root',
    'password': 'нууц_үг',
    'database': 'myshop'
}

def get_products(min_price=None):
    with mysql.connector.connect(**config) as conn:
        with conn.cursor(dictionary=True) as cursor:
            if min_price is not None:
                cursor.execute(
                    'SELECT * FROM products WHERE price >= %s ORDER BY price',
                    (min_price,)
                )
            else:
                cursor.execute('SELECT * FROM products ORDER BY price')
            return cursor.fetchall()

def add_product(name, price, stock):
    with mysql.connector.connect(**config) as conn:
        with conn.cursor() as cursor:
            cursor.execute(
                'INSERT INTO products (name, price, stock) VALUES (%s, %s, %s)',
                (name, price, stock)
            )
        conn.commit()
        return cursor.lastrowid

-- Ажиллуулах
products = get_products(min_price=100_000)
for p in products:
    print(f"{p['name']}: {p['price']:,}₮ — {p['stock']} ширхэг")

new_id = add_product('Таблет', 800_000, 30)
print(f'Шинэ бараа нэмэгдлээ, ID: {new_id}')

Дараагийн хичээлд:

MySQL шилдэг туршлага — нэрлэлтийн дүрэм, index стратеги, аюулгүй байдал, backup-ийн зөв хандлагыг үзнэ.