Блог ім. toly

Python рецепти. Дружимо python і MySQL.

Приводом для написання цієї статті стала необхідність гнучкої роботи з досить великим набором однотипних даних. Дані представлені у вигляді CSV-файлу і працювати з ним через його розмір (50 Мб) і формат (пошук одного єдиного потрібного запису може зайняти 1-3 секунди) не дуже зручно. Тому було прийнято рішення імпортувати його в базу даних -MySQL.

Для роботи зMySQL необхідно встановити драйвер для роботи з цією базою даних для пітона: завантажуємо модуль MySQLdb, розпаковуємо та встановлюємо. Так як я використовую Ubuntu (яку і вам раджу), то установка буде виконуватись єдиною командою:

$ sudo apt-get install python-mysqldb

Настав час пояснити принципи роботи з базою даних на пітоні: підключаємося до бази даних, створюємо курсор (об'єкт роботи з базою даних), виконуємо необхідні запити, застосовуємо зміни бази даних, закриваємо з'єднання з БД.

Перейдемо до практики. Є csv-файл, кожен рядок якого має вигляд "name; email; adres; telefon" (наприклад "Сергій Іванович; [email protected]; Москва, вул. Зарічна 5, кв. 109"; 33-76-152). Є база даних mysql bd з таблицею contacts. Наведений нижче скрипт закине вміст файлу до бази даних.

Точно так само можна видаляти і оновлювати дані в базі: змінюється тільки SQL-запит. Дещо інакше йде справа з вибіркою даних, а точніше з обробкою обраної з бази інформацією. Після виконання запиту на вибірку даних за допомогою функції курсору fetchall() можна отримати результат запиту. Він представляється як масиву записів, кожна з яких є масивом вмісту полів. Для того щоб не заплутатися з полями в SQL-запиті необхідно вказувати поля, що вибираються, і в томуж порядку витягувати їх із кожного запису.

Розберемо виконання та обробку SELECT-запитів наприклад: вилучимо перших 10 користувачів з щойно заповненої бази, у яких емейл розташований на Яндексі.

Ще раз нагадаю, що витягувати дані із запису необхідно в тому ж порядку (і кількості), в якому вони записані в SQL-запиті.

Ось мабуть і все. До зустрічі!

#1 by Konstantin on 10 Грудень 2010 - 10:45

Пара питань щодо реалізації: 1) А використовувати модуль CSV для роботи з CSV-файлом не кошерно? Це дозволить працювати з кожним рядком як з dictionary, а не сподіваються на порядок полів у файлі, і не треба буде спеціально відфільтровувати headerline. 2) який виграш у застосуванні бази даних, якщо для запихання в неї проводиться повне зчитування файлу в аркуш?

дякую, що показали LIMIT – знатиму що є такий MySQL specific аналог TOP.

#2 by toly on 10 Грудень 2010 - 10:51

Об'єм файлу – 50 метрів. Здійснювати пошук з найменшим залученням оперативної пам'яті через MySQL - найоптимальніший варіант. В іншому випадку одиничний запуск скрипту буде, хай і на невеликий час, витрачати 50 метрів. З MySQL таких проблем не буде. До того ж скрипт мав бути запущений на дешевій VDS'ці – оперативністю там сильно не розкидаєшся.

Дякую за відгук.

#3 by procool on 17 Березень 2012 - 10:08

мова не про те, добре або погано використовувати базу, а швидше про те, що ви файл для занесення в базу в пам'ять повністю записали.

#4 by bender on 10 Лютого 2012 - 15:46

if string.find(line, "@") > -1 можна легко замінити на: if '@' in line:

#5 by toly on 14 Грудень 2012 - 12:23

згоден. читання коду від цього буде лише краще.

#6 by SoftDed on 30 Березень 2012 - 11:51

Велике дякую за пораду! Переписав половину свого бойового скрипта!

#7 by Сонцеворот on 13 Грудень 2012 - 20:50

А чому в запиті /* sql = """SELECT mail, name FROM eadres WHERE mail LIKE '%yandex.ru' LIMIT 10""" */ стільки лапок?

#8 by toly on 14 Грудень 2012 - 12:22

у пітоні можна використовувати потрійні лапки для позначення рядків

#9 by Andry on 19 Грудень 2012 - 8:53

Дуже дякую за вичерпний приклад!

#10 by alexey on 10 Січень 2013 - 12:27

Чому під час операції INSERT дані не додаються до бази. Хоча у id включена auto_incrment і він вважається, але даних не видно. Помилок не виникає. А оператор select проходить на ура.

#11 by toly on 10 Січня 2013 - 12:45

Думаю, потрібно спробувати виконати той же INSERT в консолі для БД – може помилку яку видасть

#12 by alexey on 12 Січень 2013 - 22:55

Помилки не видає. Дані додаються

#13 by alexey on 12 Січень 2013 - 23:06

Питання зняте!) я забув рядок db.commit()

#14 by Dmitry на 18 Липень 2013 - 12:37

Дякую вам за статтю! У мене виникло таке питання, якщо запити виконуються досить часто і скрипт під час запитів може змінюватися, то як можна уникнути постійних підключень до сервера? Чи є якась команда MySqldb для перевірки коннекту?

#15 by toly on 18 Липень 2013 - 15:04

після завершення роботи з базою необхідно завершувати коннект. в даному випадку на один запуск скрипта буде один коннект. якщо запити виконуються дуже часто, можна використовувати пулл коннектов. з MySQL я вже непрацюю (як і з чистим SQL). Дуже зручний ORM (SQLAlchemy або джанговський) з postgresql. Рекомендую.

#16 by Егор on 21Грудень 2014 - 3:26

Велике дякую за статтю. Довго сидів і не міг зрозуміти, чому не можу додати до БД, а потім натрапив на Вашу статтю і зрозумів, що не зробив commit:) Хоча і в консолі БД перевіряв запит і що струму вже не робив. І ще багато фічів взяв у Вас для оптимізації коду)

#17 by Snowman8526 on 16 Червень 2015 - 18:38

Мене цікавить така тема. Коли щось робиш в phpMyAdmin вам повертається напис запит оброблений за 0.0012 сек. Я планую робити на моєму слабкому сервері навантажений обмін даними і мене цікавитиме як окремі запити, так і загальна робота циклу. Чи можна запитувати виведення цих даних? Чи простіше використовувати засоби phpMyAdmin? Але там не видно, який запит вантажить систему, а мені як новачкові в БД буде важко зрозуміти. Дякую.

#18 до 16 Червень 2015 - 19:01

#19 by Snowman8526 on 17 Червень 2015 - 17:38

Ще одне маленьке питання. Після запиту SELECT він мені обертає певні значення типу ((0,),) або ((u'qwe',),) як можна отримати чисте значення? Намагався позбутися конструкцією типу (stroka.replace(‘((,,))) але потім зрозумів, що це типу кортежу тільки кортеж. Що це за тип даних?

Я розумію, що ви вже SQL не користуєтеся, але може ви зустрічалися з такою проблемою. Дякую.

#20 by toly on 17 Червень 2015 - 17:46

Це кортеж із вкладеним кортежем. Якщо достовірно відомо, що буде повернено одне значення, можна звернутися так In [8]: a = ((1, ), ) In [9]: a[0][0] Out [9]: 1

#21 by Snowman8526 on 17 Червень 2015 - 17:48