-
Notifications
You must be signed in to change notification settings - Fork 27
Quick Start
Provide a connection string to the mssql-python connect() method to get a connection object. After establishing the connection, you can request a Cursor from it.
Example:
from mssql_python import connect
# Specify connection string
conn_str = "SERVER=<your_server_name>;DATABASE=<your_database_name>;UID=<your_user_name>;PWD=<your_password>;Encrypt=yes;"
# Create connection object
conn = Connection(conn_str)
# Connection object will return cursor
cursor = conn.cursor()TODO: There are lots of options when connecting the database through connect() method. Refer to Connecting to Databases section for more details.
SQL statements are executed through the Cursor's execute() function. When a SELECT statement returns rows, one of the Cursor's fetch functions can be used to retrieve those records. The following fetch functions are currently supported: fetchone(), fetchall(), and fetchmany(). If there are no rows, fetchone() will return None, while fetchall() and fetchmany() will return empty lists.
cursor.execute("SELECT col1, col2 FROM T1")
# Using fetchone() method
row = cursor.fetchone()
if row:
print(row)
# Using fetchall() method
rows = cursor.fetchall()
print("Rows:", rows)In the following sample, row objects can be accessed both by column index and by column name, providing flexibility in handling query results. Essentially, row objects are similar to tuples but provide additional functionality by allowing access to columns by their names.
cursor.execute("select col_1, col_2 from T1")
row = cursor.fetchone()
print('name:', row[1]) # access by column index (zero-based)
print('name:', row.col_1) # access by nameThe fetchone() function fetch the next row of a query result set, returning a single sequence, or None when no more data is available.
while True:
# fetch the next row from the query result set. If there are no more rows to fetch, row will be None.
row = cursor.fetchone()
if not row:
break
print('id:', row.col_1)The fetchall() function is used to retrieve all remaining rows from the result set of a query. When called, it returns these rows as a list of sequences, typically a list of tuples. Each tuple represents a row from the result set, with each element in the tuple corresponding to a column in that row.
cursor.execute("select col_1, col_2 from T1")
rows = cursor.fetchall()
for row in rows:
print(row.col_1, row.col_2)Since fetchall() retrieves all remaining rows at once, it stores them in memory. This means that if the result set is very large, it could consume a significant amount of memory, potentially leading to memory exhaustion. Therefore, it's important to be cautious when using fetchall() with large datasets.
If there are no rows left to fetch, fetchall() will return an empty list. This can be useful for checking if the query returned any results without having to handle None values.
mssql-python supports the use of parameters in SQL queries by using question marks as placeholders as per DBAPI specifications. You can provide the values for these placeholders by passing them after the SQL statement:
cursor.execute("""
select col1_1, col_2 from T1 where col_predicate < ? and bill_overdue = ? """, datetime.date(2001, 1, 1), 'y')This is safer than putting the values into the string because the parameters are passed to the database separately, protecting against SQL injection attacks. It is also more efficient if you execute the same SQL repeatedly with different parameters. The SQL will be "prepared" only once. (pyodbc keeps only the last prepared statement, so if you switch between statements, each will be prepared multiple times.)
The Python DB API specifies that parameters should be passed as a sequence, so this is also supported by pyodbc:
cursor.execute("""
select user_id, user_name
from users
where last_logon < ?
and bill_overdue = ?
""", [datetime.date(2001, 1, 1), 'y'])To insert data, pass the insert SQL to Cursor execute(), along with any parameters necessary:
cursor.execute("insert into products(id, name) values ('pyodbc', 'awesome library')")
cnxn.commit()or, parameterized:
cursor.execute("insert into products(id, name) values (?, ?)", 'pyodbc', 'awesome library')
cnxn.commit()Note the calls to cnxn.commit(). You must call commit (or set autocommit to True on the connection) otherwise your changes will be lost!
Updating and deleting work the same way, pass the SQL to execute. However, you often want to know how many records were affected when updating and deleting, in which case you can use the Cursor rowcount attribute:
cursor.execute("delete from products where id <> ?", 'pyodbc')
print(cursor.rowcount, 'products deleted')
cnxn.commit()Since execute() always returns the cursor, you will sometimes see code like this (notice .rowcount on the end).
deleted = cursor.execute("delete from products where id <> 'pyodbc'").rowcount
cnxn.commit()Note the calls to cnxn.commit(). You must call commit (or set autocommit to True on the connection) otherwise your changes will be lost!
Since single quotes are valid in SQL, use double quotes to surround your SQL:
deleted = cursor.execute("delete from products where id <> 'pyodbc'").rowcountIt's also worthwhile considering using 'raw' strings for your SQL to avoid any inadvertent escaping (unless you really do want to specify control characters):
cursor.execute("delete from products where name like '%bad\name%'") # Python will convert \n to 'new line'!
cursor.execute(r"delete from products where name like '%bad\name%'") # no escapingSome databases (e.g. SQL Server) do not generate column names for calculated fields, e.g. COUNT(*). In that case you can either access the column by its index, or use an alias on the column (i.e. use the "as" keyword).
row = cursor.execute("select count(*) as user_count from users").fetchone()
print('%s users' % row.user_count)Long SQL statements are best encapsulated using the triple-quote string format. Doing so does create a string with lots of blank space on the left, but whitespace should be ignored by database SQL engines. If you still want to remove the blank space on the left, you can use the dedent() function in the built-in textwrap module. For example:
import textwrap
sql = textwrap.dedent("""
select p.date_of_birth,
p.email,
a.city
from person as p
left outer join address as a on a.address_id = p.address_id
where p.status = 'active'
and p.name = ?
""")
rows = cursor.execute(sql, 'John Smith').fetchall()If you are selecting a single value you can use the fetchval convenience method. If the statement generates a row, it returns the value of the first column of the first row. If there are no rows, None is returned:
maxid = cursor.execute("select max(id) from users").fetchval()Most databases support COALESCE or ISNULL which can be used to convert NULL to a hardcoded value, but note that this will not cause a row to be returned if the SQL returns no rows. That is, COALESCE is great with aggregate functions like max or count, but fetchone is better when attempting to retrieve the value from a particular row:
cursor.execute("select coalesce(max(id), 0) from users").fetchone()[0]
cursor.execute("select coalesce(count(*), 0) from users").fetchone()[0]However, fetchval is a better choice if the statement can return an empty set:
# Careful!
cursor.execute("""
select create_timestamp
from photos
where user_id = 1
order by create_timestamp desc
limit 1
""").fetchone()[0]
# Preferred
cursor.execute("""
select create_timestamp
from photos
where user = 1
order by create_timestamp desc
limit 1
""").fetchval()The first example will raise an exception if there are no rows for user_id 1. The fetchone()
call returns None. Python then attempts to apply [0] to the result (None[0]) which is not
valid.
The fetchval method was created just for this situation - it will detect the fact that there
are no rows and will return None.