In [94]:
%load_ext sql
%sql sqlite:///chinook.db


The sql extension is already loaded. To reload it, use:
  %reload_ext sql
Out[94]:
'Connected: None@chinook.db'

Передача переменных python в sql

Можно передать переменную из python в sql


In [95]:
a = 'Pop'

%sql select * from genres where Name = :a


Done.
Out[95]:
GenreId Name
9 Pop

Можно присвоить результат запроса в переменную


In [96]:
a = %sql select * from genres


Done.

In [92]:
type(a)


Out[92]:
sql.run.ResultSet

In [93]:
print(a)


+---------+--------------------+
| GenreId |        Name        |
+---------+--------------------+
|    1    |        Rock        |
|    2    |        Jazz        |
|    3    |       Metal        |
|    4    | Alternative & Punk |
|    5    |   Rock And Roll    |
|    6    |       Blues        |
|    7    |       Latin        |
|    8    |       Reggae       |
|    9    |        Pop         |
|    10   |     Soundtrack     |
|    11   |     Bossa Nova     |
|    12   |   Easy Listening   |
|    13   |    Heavy Metal     |
|    14   |      R&B/Soul      |
|    15   | Electronica/Dance  |
|    16   |       World        |
|    17   |    Hip Hop/Rap     |
|    18   |  Science Fiction   |
|    19   |      TV Shows      |
|    20   |  Sci Fi & Fantasy  |
|    21   |       Drama        |
|    22   |       Comedy       |
|    23   |    Alternative     |
|    24   |     Classical      |
|    25   |       Opera        |
+---------+--------------------+

Другой способ соединения

использование библиотеки sqlite3


In [97]:
import sqlite3

In [84]:
# Создаем БД в RAM
db=sqlite3.connect(':memory:')
# После окончания работы не забываем закрыть соединение
db.close()

In [85]:
# Создаем или открываем бд 
db=sqlite3.connect('testdb')
# Закрываем бд
db.close()

Создание (CREATE) и Удаление (DROP) таблиц.

Для того, чтобы выполнить любую операцию с базой данных необходимо создать объект cursor и передать SQL-выражение в объект cursor, чтобы вызвать его. В конце необходимо выполнить выполнить commit (заметьте, что commit выполняется для db объекта, а не cursor объекта)


In [98]:
db=sqlite3.connect('testdb')

In [99]:
# Получить cursor объекта
cursor = db.cursor()
cursor.execute('''
    DROP TABLE IF EXISTS users
''');

cursor.execute('''
    CREATE TABLE users(id INTEGER PRIMARY KEY, name TEXT,
                       phone TEXT, email TEXT UNIQUE, password TEXT);
''')
db.commit()

Вставка (INSERT) данных в базу данных

Для вставки данных мы используем cursor для выполнения запроса. Если требуется вставка данных из python, то можно использовать "?". Не используйте строчные операторы или конкатенацию для создания запросов, потому что это не безопасно.


In [100]:
cursor=db.cursor()
name1 = 'Andrew'
phone1 = '123232'
email1 = 'user@example.com'
password1 = '12345'

name2 = 'John'
phone2 = '234241'
email2 = 'john@example.com'
password2 = 'abcdef'

# Insert user 1
cursor.execute('''INSERT INTO users(name, phone, email, password)
                  VALUES(?,?,?,?)''', (name1, phone1, email1, password1))
print('First user inserted')

# Insert user 2
cursor.execute('''INSERT INTO users(name, phone, email, password)
                  VALUES(?,?,?,?)''', (name2, phone2, email2, password2))
print('Second user inserted')

db.commit()


First user inserted
Second user inserted

Значения переменных python подставляются через кортеж. Другой способ - через словарь, используя ':'


In [101]:
name3 = 'Nikita'
phone3 = '323232'
email3 = 'nikita@example.com'
password3 = '123'

cursor = db.cursor()
cursor.execute('''INSERT INTO users(name, phone, email, password)
                  VALUES(:name, :phone, :email, :password)''',
              {'name':name3, 'phone':phone3, 'email':email3, 'password':password3})

print('Third user inserted')

db.commit()


Third user inserted

In [103]:
name3 = 'Nikita'
phone3 = '323232'
email3 = 'nikita@example.com'
password3 = '123'

cursor = db.cursor()
cursor.execute('''INSERT INTO users(name, phone, email, password)
                  VALUES(:name3, :phone3, :email3, :password3)''')

print('Third user inserted')

db.commit()


ERROR:root:An unexpected error occurred while tokenizing input
The following traceback may be corrupted or invalid
The error message is: ('EOF in multi-line string', (1, 62))

---------------------------------------------------------------------------
ProgrammingError                          Traceback (most recent call last)
<ipython-input-103-7f6f65d5dd1d> in <module>()
      6 cursor = db.cursor()
      7 cursor.execute('''INSERT INTO users(name, phone, email, password)
----> 8                   VALUES(:name3, :phone3, :email3, :password3)''')
      9 
     10 print('Third user inserted')

ProgrammingError: Incorrect number of bindings supplied. The current statement uses 4, and there are 0 supplied.

Если вы хотите вставить нескольо пользователей в таблицу, используйте executemany и список из кортежей


In [104]:
name4 = 'Ann'
phone4 = '490904'
email4 = 'ann@example.com'
password4 = '345'

name5 = 'Jane'
phone5 = '809908'
email5 = 'jane@example.com'
password5 = '785'

users = [(name4, phone4, email4, password4),
         (name5, phone5, email5, password5)]
cursor.executemany('''INSERT INTO users(name, phone, email, password) VALUES (?,?,?,?)''', users)
db.commit()

Если вам требуется получить ид строки, которую вы только что добавили, используйте lastrowid


In [46]:
id = cursor.lastrowid
print('Last row id: %d' % id)


Last row id: 3

Получение данных (SELECT) с SQLite

Чтобы получить данные, необходимо выполнить fetchone для выбора одной строки или fetchall для всех строк


In [47]:
cursor.execute('''SELECT name, email, phone FROM users''')
user1 = cursor.fetchone() # получить одну строку
print(user1[0])
all_rows = cursor.fetchall()
for row in all_rows:
    # row[0] возращает первый столбец - name, row[1] - email, row[2] - phone
    print('{0} : {1}, {2}'.format(row[0], row[1], row[2]))


Andrew
John : john@example.com, 234241
Nikita : nikita@example.com, 323232
Ann : ann@example.com, 490904
Jane : jane@example.com, 809908

Объект cursor работает как итератор, вызывая fetchall() автоматически


In [48]:
cursor.execute('''SELECT name, email, phone FROM users''')
for row in cursor:
    print('{0} : {1}, {2}'.format(row[0], row[1], row[2]))


Andrew : user@example.com, 123232
John : john@example.com, 234241
Nikita : nikita@example.com, 323232
Ann : ann@example.com, 490904
Jane : jane@example.com, 809908

Чтобы получить данные с условиями, используйте '?'


In [51]:
user_id=3
cursor.execute('''SELECT name, email, phone FROM users WHERE id=?''', (user_id,))
user=cursor.fetchone()
print (user[0], user[1], user[2])


Nikita nikita@example.com 323232

Обновление (UPDATE) и удаление (DELETE) данных

Процедура аналогична вставке данных


In [55]:
# Обновить пользователя с id = 1
newphone = '77777'
userid = 1
cursor.execute('''UPDATE users SET phone = ? WHERE id = ?''', (newphone, userid))

# Удалить пользователя с id = 2
delete_userid = 2
cursor.execute('''DELETE FROM users WHERE id = ?''', (delete_userid,))

db.commit()

Использование SQLite транзакций

Транзакции очень важное свойство баз данных. Они обеспечивают атомарность БД. Используйте commit для сохранения изменений.


In [56]:
cursor.execute('''UPDATE users SET phone = ? WHERE id = ? ''', (newphone, userid))
db.commit()

Или rollback для отмены изменений


In [58]:
cursor.execute('''UPDATE users SET phone = ? WHERE id = ?''', (newphone, userid))
db.rollback()

Помните, что всегда требуется сохранить изменения. Если вы закроете соединение или соединение будет потеряно, то ваши изменения не будут внесены


In [60]:
db.close()

Исключения SQLite

Для best practices всегда оборачивайте операции баз данных в try или context manager


In [105]:
import sqlite3
try:
    db=sqlite3.connect('testdb')
    cursor=db.cursor()
    cursor.execute('''CREATE TABLE users(id INTEGER PRIMARY KEY, name TEXT 
                                         email TEXT unique, password TEXT)''')
    db.commit()
except Exception as e:
    db.rollback()
    print('we are here')
    raise e 
finally:
    db.close()


ERROR:root:An unexpected error occurred while tokenizing input
The following traceback may be corrupted or invalid
The error message is: ('EOF in multi-line string', (1, 74))

we are here
---------------------------------------------------------------------------
OperationalError                          Traceback (most recent call last)
<ipython-input-105-891f0997594b> in <module>()
      9     db.rollback()
     10     print('we are here')
---> 11     raise e
     12 finally:
     13     db.close()

<ipython-input-105-891f0997594b> in <module>()
      4     cursor=db.cursor()
      5     cursor.execute('''CREATE TABLE users(id INTEGER PRIMARY KEY, name TEXT 
----> 6                                          email TEXT unique, password TEXT)''')
      7     db.commit()
      8 except Exception as e:

OperationalError: table users already exists

В этом примере мы используем try/except/finally для того, чтобы "поймать" исключение в коде. Служебное слово finally - очень важно, потому что благодаря ему коннект к бд закрывается корректно. Более подробно здесь.

Используя except as Exception, мы ловим все исключения. Обычно в production коде необходимо "ловить" определенное исключение. Ссылка

Можно использовать объект Connection для автоматического commit'а и rollback'а


In [106]:
name1 = 'Andres'
phone1 = '333658'
email1 = 'user@example.com'
password1 = '12345'

try:
    db=sqlite3.connect('testdb')
    with db:
        db.execute('''INSERT INTO users(name, phone, email, password)
                      VALUES(?,?,?,?)''', (name1, phone1, email1, password1))
except sqlite3.IntegrityError:
    print('Record already exists')
finally:
    db.close()


Record already exists

В пример выше, если insert вызывает исключение, для транзакции будет совершен откат и сообщение будет написано, иначе транзакция будет выполнена. Заметьте, что в данном случае мы вызываем execute на db объект.


In [77]:
%%sql
select company
FROM invoices 
    join customers
    ON invoices.customerid = customers.customerid
WHERE customers.company <> 'None'
group by customers.customerid, customers.company
having count(*)
in
(
select min(cnt) from 
(
select count(*) as cnt
FROM invoices
group by customerid
) A 

UNION ALL

select max(cnt) from 
(
select count(*) as cnt
FROM invoices
group by customerid
) A 
)


Done.
Out[77]:
Company
Embraer - Empresa Brasileira de Aeronáutica S.A.
JetBrains s.r.o.
Woodstock Discos
Banco do Brasil S.A.
Riotur
Telus
Rogers Canada
Google Inc.
Microsoft Corporation
Apple Inc.

In [ ]: