Django's count fail in Oracle - python

I'm trying to make a query to a oracle database with this model:
class FCSTrunkValidation(Validation):
card_transaction = models.CharField(max_length=10, db_column='card_trnsctn_seq', primary_key=True)
card_number = models.CharField(max_length=16, db_column='card_num')
use_date = models.CharField(max_length=14, db_column='use_date')
device = models.ForeignKey('TrunkDevice', db_column='device_id')
agency_id = models.CharField(max_length=3, db_column='agency_id')
And this query:
# day is a datetime object
qs = FCSTrunkValidation.oracle_objects.all()
qs = qs.filter(use_date__startswith=day.strftime('%Y%m%d'))
When I do qs.count() I got this:
...
File "/home/diegueus9/dev/odm/local/lib/python2.7/site-packages/django/db/models/query.py", line 93, in __repr__
data = list(self[:REPR_OUTPUT_SIZE + 1])
File "/home/diegueus9/dev/odm/local/lib/python2.7/site-packages/django/db/models/query.py", line 108, in __len__
self._result_cache.extend(self._iter)
File "/home/diegueus9/dev/odm/local/lib/python2.7/site-packages/django/db/models/query.py", line 317, in iterator
for row in compiler.results_iter():
File "/home/diegueus9/dev/odm/local/lib/python2.7/site-packages/django/db/models/sql/compiler.py", line 775, in results_iter
for rows in self.execute_sql(MULTI):
File "/home/diegueus9/dev/odm/local/lib/python2.7/site-packages/django/db/models/sql/compiler.py", line 840, in execute_sql
cursor.execute(sql, params)
File "/home/diegueus9/dev/odm/local/lib/python2.7/site-packages/django/db/backends/util.py", line 41, in execute
return self.cursor.execute(sql, params)
File "/home/diegueus9/dev/odm/local/lib/python2.7/site-packages/django/db/backends/oracle/base.py", line 717, in execute
six.reraise(utils.DatabaseError, utils.DatabaseError(*tuple(e.args)), sys.exc_info()[2])
File "/home/diegueus9/dev/odm/local/lib/python2.7/site-packages/django/db/backends/oracle/base.py", line 710, in execute
return self.cursor.execute(query, self._param_generator(params))
DatabaseError: ORA-02395: exceeded call limit on IO usage
However If I execute the query with the squirrel client I got the number, so it's django maybe doing additional querys that raise the error? How I can make the count work with django?
I though of use raw sql but then I got a problem escaping the % in the LIKE part
Packages versions
Python==2.7.4
Django==1.5.4
cx-Oracle==5.1.2
six==1.4.1
Update 20131023
Following the suggestion of #alko, I added a print statement in django.db.backends.oracle.base at line 709 like this:
print query, self._param_generator(params)
Then I executed that query with those params in Squirrel and still got the number of the count.
My manual query was:
select count(*) from TBAAD300 where AGENCY_ID=201 and USE_DATE LIKE '20130930%'
The query that django uses is:
SELECT COUNT(*) FROM "TBAAD300" WHERE ("TBAAD300"."AGENCY_ID" = :arg0 AND "TBAAD300"."USE_DATE" LIKE TRANSLATE(:arg1 USING NCHAR_CS) ESCAPE TRANSLATE('\' USING NCHAR_CS) )
with [u'201', u'20130930%'] as params
Then I executed the same query in Squirrel and the result is 130410, but when django does, the same error is raised, the full queries that were printed by the print statement are:
ALTER SESSION SET NLS_TERRITORY = 'AMERICA' []
ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS' NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF' TIME_ZONE = 'UTC' []
SELECT 1 FROM DUAL WHERE DUMMY LIKE TRANSLATE(:arg0 USING NCHAR_CS) ESCAPE TRANSLATE('\' USING NCHAR_CS) [u'X']
SELECT COUNT(*) FROM "TBAAD300" WHERE ("TBAAD300"."AGENCY_ID" = :arg0 AND "TBAAD300"."USE_DATE" LIKE TRANSLATE(:arg1 USING NCHAR_CS) ESCAPE TRANSLATE('\' USING NCHAR_CS) ) [u'201', u'20130930%']

Is Oracle your prod db? ORA-02395 means IO restrictions, https://forums.oracle.com/thread/655458, seems your count() query leads to a full scan on a large table in db with restrictions on block reads per query.
I'd suggest to reconsider your data model, can you use DateField for use_date field and you replace your query with filter(use_date__gte = ..., use_date___lte = ...)? In this case you can drastically increase performance and IO usage by adding an index on use_date field, since count and alike queries can use it if appropriate.
UPDATE (added here to fine format SQL)
What is your Oracle DBMS version? Are you running statements under the same user credentials? What are resourse limits for your user (this can help).
To check what resource consumtion metrics are, you can also run following command in sql client:
exec runstats_pkg.rs_start;
<YOUR statement>
exec runstats_pkg.rs_stop;
select * from stats where name like '%session logical reads%';
select * from stats where name like '%consistent gets%';
also an execution plan may be handy
set autotrace on;
<YOUR STATEMENT>
set autotrace off;
as described here

Related

SQLServer: query runs on console but not on SQLAlchemy

I'm trying to run a very expensive SQL Server query via Python + SQLAlchemy. It runs just fine on sql server console but it errors out when called via sqlalchemy.
Test run looks like this:
Run query on SQL Server console.
Wait about 15 minutes for it to finish.
Query runs just fine and returns ~50,000 rows.
When running the same query using Python + SQLAlchemy, it looks like this:
Run query.
Wait a long time.
Code errors out and throws a misleading error stating that the query did not return any rows.
I am positive that this error message cannot possibly be right, because I have tested the same query on console and it runs just fine and returns A LOT of rows. Does anyone know what is really happening here?
Query looks like this:
USE DB_NAME;
DROP TABLE IF EXISTS #TB_1;
CREATE TABLE #TB_1 (FIELD_1 BIGINT, FIELD_2 BIGINT);
INSERT INTO #TB_1 VALUES (1, 5), (2, 6), (3, 7);
--------------------------------------------------
DROP TABLE IF EXISTS #TB_2;
SELECT * INTO #TB_2 FROM (
SELECT DISTINCT FIELD_1, FIELD_2
FROM dbo.PRIMARY_TABLE_1 (NOLOCK)
WHERE FIELD_1 IN (SELECT * FROM #TB_1)
) AS TB_2;
--------------------------------------------------
SELECT FIELD_1, FIELD_2 FROM #TB_1
UNION ALL
SELECT FIELD_1, FIELD_2 FROM #TB_2
Code looks like this:
from sqlalchemy.engine import create_engine
engine = create_engine(SQLServer_URI)
with engine.connect() as connection:
connection.execute(huge_query).fetchall()
Here's the Stack Trace:
Traceback (most recent call last):
...
File "path-to-project/src/etl/ETL.py", line 48, in extract
raw_data = connection.execute(query).fetchall()
File "path-to-project\venv\lib\site-packages\sqlalchemy\engine\result.py", line 984, in fetchall
return self._allrows()
File "path-to-project\venv\lib\site-packages\sqlalchemy\engine\result.py", line 398, in _allrows
make_row = self._row_getter
File "path-to-project\venv\lib\site-packages\sqlalchemy\util\langhelpers.py", line 1160, in __get__
obj.__dict__[self.__name__] = result = self.fget(obj)
File "path-to-project\venv\lib\site-packages\sqlalchemy\engine\result.py", line 319, in _row_getter
keymap = metadata._keymap
File "path-to-project\venv\lib\site-packages\sqlalchemy\engine\cursor.py", line 1197, in _keymap
self._we_dont_return_rows()
File "path-to-project\venv\lib\site-packages\sqlalchemy\engine\cursor.py", line 1178, in _we_dont_return_rows
util.raise_(
File "path-to-project\venv\lib\site-packages\sqlalchemy\util\compat.py", line 211, in raise_
raise exception
sqlalchemy.exc.ResourceClosedError: This result object does not return rows. It has been closed automatically.
Yep adding "SET NOCOUNT ON;" as per Gord Thompson's comment, to the front of my query solved it for me. I got the same error running a stored procedure containing multiple queries. So this will do the trick:
data = pd.read_sql("SET NOCOUNT ON; Your SQL Query.....
This answer https://stackoverflow.com/a/55597613/11692538 helped solve it and explain it for me. Basically sqlalchemy (or pyodbc) reads any messages sent in the execution of a query as the result, hence why you get the error "name": "ResourceClosedError", "message": "This result object does not return rows. It has been closed automatically." So setting NOCOUNT prevents sql server sending back count messages during the execution of the query(s).

Python using mysql connector list databases LIKE and then use those databases in order and run query

I'm trying to write a script using pythong and the mysql-connector library. The script should connect to the mysql server do a "SHOW DATABASES LIKE 'pdns_%' and then using the results returned by the query use each database and then run another query while using that database.
Here is the code
import datetime
import mysql.connector
from mysql.connector import errorcode
cnx = mysql.connector.connect (user='user', password='thepassword',
host='mysql.server.com',buffered=True)
cursor = cnx.cursor()
query = ("show databases like 'pdns_%'")
cursor.execute(query)
databases = query
for (databases) in cursor:
cursor.execute("USE %s",(databases[0],))
hitcounts = ("SELECT Monthname(hitdatetime) AS 'Month', Count(hitdatetime) AS 'Hits' WHERE hitdatetime >= Date_add(Last_day(Date_sub(Curdate(), interval 4 month)), interval 1 day) AND hitdatetime < Date_add(Last_day(Date_sub(Curdate(), interval 1 month)), interval 1 day) GROUP BY Monthname(hitdatetime) ORDER BY Month(hitdatetime)")
cursor.execute(hitcounts)
print(hitcounts)
cursor.close()
cnx.close()
When running the script it stops with the following error'd output
Traceback (most recent call last):
File "./mysql-test.py", line 18, in <module>
cursor.execute("USE %s",(databases[0],))
File "/usr/lib/python2.6/site-packages/mysql/connector/cursor.py", line 491, in execute
self._handle_result(self._connection.cmd_query(stmt))
File "/usr/lib/python2.6/site-packages/mysql/connector/connection.py", line 635, in cmd_query
statement))
File "/usr/lib/python2.6/site-packages/mysql/connector/connection.py", line 553, in _handle_result
raise errors.get_exception(packet)
mysql.connector.errors.ProgrammingError: 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''pdns_382'' at line 1
Based on the error I'm guessing there is an issue with how its doing the datbase name from the first query. Any pointers in the correct direction would be very helpful as I'm very much a beginner. Thank you very much.
Alas, the two-args form of execute does not support "meta" parameters, such as names of databases, tables, or fields (roughly, think of identifiers you wouldn't quote if writing the query out manually). So, the failing statement:
cursor.execute("USE %s",(databases[0],))
needs to be re-coded as:
cursor.execute("USE %s" % (databases[0],))
i.e, the single arg form of execute, with a string interpolation. Fortunately, this particular case does not expose you to SQL injection risks, since you're only interpolating DB names coming right from the DB engine.

python-memcached : Unable to memcache a mysql output. ( UnpickleableError: Cannot pickle objects )

For some reason memcache does not seem to like
result
in this following code
db.query("select * from TABLE order by ID desc limit 70")
result = db.store_result()
m.set('1',result,60)
This is the error in apache error_log:
m.set('1',result,60)
File "/usr/lib/python2.6/site-packages/memcache.py", line 466, in set
return self._set("set", key, val, time, min_compress_len)
File "/usr/lib/python2.6/site-packages/memcache.py", line 639, in _set
store_info = self._val_to_store_info(val, min_compress_len)
File "/usr/lib/python2.6/site-packages/memcache.py", line 615, in _val_to_store_info
pickler.dump(val)
UnpickleableError: Cannot pickle objects
Something is probably going on with the "result".
otherwise instead of "result".. something else such as..
m.set('1','test',60)
works just fine.
store_result instructs MySQL to store the result of your query locally, and returns a reference to that "result object". It does not actually return a list of rows.
To actually get the rows:
rows = result.fetch_row(maxrows=0) # Actually fetches all the rows
m.set('1', rows, 60)
Now, it'd be best to instantiate a cursor than to use _mysql directly.

Python pypyodbc how do I insert variables to the execute statement?

I am working with Python 3.3, pypyodbc 1.2.1, and a Quickbooks Enterprise 12 company file being access over Flexquarters QODBC version 14. I'm new to programming and python, so still learning :) I can run a query using the pypyodbc examples just fine, and produces expected results.
Notice the hardcoded email address in the execute. This works as expected:
def get_customer_id(search_col,search_str):
'''(str,str) --> str
>>>get_customer_id(email, foo#foo.com)
80000001-1385782702
'''
cur.execute("SELECT listid FROM CUSTOMER WHERE email='foo#foo.com'")
for row in cur.fetchall():
for field in row:
return field
If I try to do the same thing using the parameters that I am reading from the pypyodbc documentation, I throw an error. I'm having problems with the quotes, and parameter markers I think.
def get_customer_id(search_col,search_str):
'''(str,str) --> str
>>>get_customer_id(email, foo#foo.com)
80000001-1385782702
'''
cur.execute("SELECT listid FROM CUSTOMER WHERE email=?",(search_str,))
for row in cur.fetchall():
for field in row:
return field
Trying to be more pythonistic? I really want to reuse the function to search different columns. Something like:
cur.execute("SELECT listid FROM CUSTOMER WHERE search_str=search_col")
I have looked at a few other threads, and most of them seem to just be dealing with the parameter, and not the column to search. Can anyone help me learn this?
PS forgot to include the traceback:
Traceback (most recent call last):
File "C:\Users\Mike\Documents\Projects\qb_sync\quickbooks.py", line 32, in <module>
print(get_customer_id('email','foo#foo.com'))
File "C:\Users\Mike\Documents\Projects\qb_sync\quickbooks.py", line 27, in get_customer_id
cur.execute("SELECT listid FROM CUSTOMER WHERE email=?",[search_str,])
File "C:\Python\lib\site-packages\pypyodbc.py", line 1457, in execute
self._BindParams(param_types)
File "C:\Python\lib\site-packages\pypyodbc.py", line 1420, in _BindParams
check_success(self, ret)
File "C:\Python\lib\site-packages\pypyodbc.py", line 982, in check_success
ctrl_err(SQL_HANDLE_STMT, ODBC_obj.stmt_h, ret, ODBC_obj.ansi)
File "C:\Python\lib\site-packages\pypyodbc.py", line 960, in ctrl_err
raise Error(state,err_text)
pypyodbc.Error: ('HY004', '[HY004] [Microsoft][ODBC Driver Manager] SQL data type out of range')
[Finished in 1.7s]
I think the use of
cur.execute("""SELECT listid FROM CUSTOMER WHERE ?=?""",[column, email])
can not be accepted by database engine rather than pypyodbc or any other odbc interface. It's the database engine refuse to accept the query for the use of parameter on column names.
Probably you would have to try this instead to reuse the function:
# First construct your dynamic query for the targeted column
sql = """SELECT listid FROM CUSTOMER WHERE %s=?""" %(column)
# Then provide the dynamic value for the dynamic query string
cur.execute(sql, (value,))
Python 3 also has the str.format() method which will do string replacement on {index} items within your string. This is useful if you have many values to inject into your strings like:
myStr = "I like {0} and {1}, but I don't like {2}.".format("apples","bananas","spinach")
myStr
"I like apples and bananas, but I don't like spinach."
# First construct your dynamic query for the targeted column
sql = """SELECT listid FROM CUSTOMER WHERE {0}=?""".format(column)
# Then provide the dynamic value for the dynamic query string
cur.execute(sql, (value,))
It's worth noting that this method of replacing values in a string query can be subject to sql injection.
The safer way to do this would be with parameterized stored procs.
I got 1/2 the answer so far. This works for one parameter, IF I format the string before calling the function;
print(get_custid_email(b'foo#foo.org'))
cur.execute("""SELECT listid FROM CUSTOMER WHERE email=?""",[email])
I still can't get it to do the same thing with column name though.
print(get_custid_email(b'email',b'foo#foo.org'))
cur.execute("""SELECT listid FROM CUSTOMER WHERE ?=?""",[column, email])
That throws a differnt error:
Traceback (most recent call last):
File "C:\Users\Mike\Documents\Projects\qb_sync\quickbooks.py", line 34, in <module>
print(get_custid_email(b'wendy.lindsay#gmail.com'))
File "C:\Users\Mike\Documents\Projects\qb_sync\quickbooks.py", line 29, in get_custid_email
cur.execute("""SELECT listid FROM CUSTOMER WHERE ?=?""",['email',email])
pyodbc.ProgrammingError: ('42S00', '[42S00] [QODBC] Data type of parameter cannot be determined (11023) (SQLPrepare)')

SQLAlchemy IntegrityError

I'm having a problem using SQLAlchemy with PySide(PyQt). I'm trying to pop-up a QtGui.QDialog, but when I do this SQLAlchemy throws an exception:
Traceback (most recent call last):
File "C:\Python27\lib\site-packages\preo\preodb\dbviewandmodel.py", line 32, in rowCount
return len(self.rows())
File "C:\Python27\lib\site-packages\preo\preodb\dbviewandmodel.py", line 30, in rows
return self.tableobj.query.all()
File "C:\Python27\lib\site-packages\sqlalchemy-0.6.6-py2.7.egg\sqlalchemy\orm\query.py", line 1579, in all
return list(self)
File "C:\Python27\lib\site-packages\sqlalchemy-0.6.6-py2.7.egg\sqlalchemy\orm\query.py", line 1688, in __iter__
self.session._autoflush()
File "C:\Python27\lib\site-packages\sqlalchemy-0.6.6-py2.7.egg\sqlalchemy\orm\session.py", line 862, in _autoflush
self.flush()
File "C:\Python27\lib\site-packages\sqlalchemy-0.6.6-py2.7.egg\sqlalchemy\orm\session.py", line 1388, in flush
self._flush(objects)
File "C:\Python27\lib\site-packages\sqlalchemy-0.6.6-py2.7.egg\sqlalchemy\orm\session.py", line 1469, in _flush
flush_context.execute()
File "C:\Python27\lib\site-packages\sqlalchemy-0.6.6-py2.7.egg\sqlalchemy\orm\unitofwork.py", line 302, in execute
rec.execute(self)
File "C:\Python27\lib\site-packages\sqlalchemy-0.6.6-py2.7.egg\sqlalchemy\orm\unitofwork.py", line 446, in execute
uow
File "C:\Python27\lib\site-packages\sqlalchemy-0.6.6-py2.7.egg\sqlalchemy\orm\mapper.py", line 1878, in _save_obj
execute(statement, params)
File "C:\Python27\lib\site-packages\sqlalchemy-0.6.6-py2.7.egg\sqlalchemy\engine\base.py", line 1191, in execute
params)
File "C:\Python27\lib\site-packages\sqlalchemy-0.6.6-py2.7.egg\sqlalchemy\engine\base.py", line 1271, in _execute_clauseelement
return self.__execute_context(context)
File "C:\Python27\lib\site-packages\sqlalchemy-0.6.6-py2.7.egg\sqlalchemy\engine\base.py", line 1302, in __execute_context
context.parameters[0], context=context)
File "C:\Python27\lib\site-packages\sqlalchemy-0.6.6-py2.7.egg\sqlalchemy\engine\base.py", line 1401, in _cursor_execute
context)
File "C:\Python27\lib\site-packages\sqlalchemy-0.6.6-py2.7.egg\sqlalchemy\engine\base.py", line 1394, in _cursor_execute
context)
File "C:\Python27\lib\site-packages\sqlalchemy-0.6.6-py2.7.egg\sqlalchemy\engine\default.py", line 299, in do_execute
cursor.execute(statement, parameters)
sqlalchemy.exc.IntegrityError: (IntegrityError) ('23000', "[23000] [Microsoft][ODBC
SQL Server Driver][SQL Server]Violation of UNIQUE KEY
constraint 'UQ__users__F3DBC5720DAF0CB0'. Cannot insert duplicate key in
object 'dbo.users'. (2627) (SQLExecDirectW); [01000] [Microsoft][ODBC SQL Server
Driver][SQL Server]The statement has been terminated. (3621)") u'INSERT INTO users
(username, fullname, email, passwordmd5) OUTPUT inserted.id VALUES (?, ?, ?, ?)'
(None, None, None, None)
This is particularly troubling because I have no code, anywhere, that even attempts to insert records into SQL; I am only ever attempting to query data from the database. In fact, my DB model is read-only with respect to what PySide/PyQt are doing (i.e., I'm using a QtGui.QTableView model/view and there is no insertRows function in that model).
I have no idea what's going on or how to solve it - again, I have no code to modify SQL records at all, but still SQLAlchemy attempts to be inserting blank records into one of my SQL tables. All I can see, in the background, is the QTableView data model is querying the database A LOT. It just seems that when I popup this QDialog (which does have some code in it to query some table column) this error is thrown. Oddly, this isn't consistent, sometime the popup appears first before the exception, sometimes the popup appears after the exception. Under normal circumstances, the QTableView data model works great, just not when I popup this dialog (and ironically, the popup isn't using any QTableView at all, just standard widgets like QLineEdit, QTextEdit, etc.)
If it helps, I'm using Python 2.7 with SQLAlchemy 0.6.6 (also with Elixir 0.7.1), and PySide 1.0.0 (and PyQt4 4.8.3). I'm on Windows 7 using SQL 2008 R2 (Express). And yes, I've tried rebooting the PC, but the problem still occurs after a reboot. I'm reluctant to post more code because I have a lot of it in this particular project and I can't nail down the problem anything specific.
I'm hoping someone might know of oddities in SQLAlchemy and/or PyQt that might be related to this. I'm also hoping I can continue using SQLAlchemy as I have a large data model built; I'm reluctant, at this point, to abandon this and use PyQt's SQL features.
I've managed to make this problem go away, but it's still not really clear to me why SQLAlchemy was trying to insert rows in my database - that really bothers me, but it's not happening anymore.
At any rate, what was, I think, happening, was related to my SQLAlchemy data model and the way I was accessing it, here is a snippet of that model:
from elixir import *
metadata.bind = 'mssql+pyodbc://username:password/dbname'
metadata.bind.echo = False
class Users(Entity):
using_options(tablename = 'users')
username = Field(String(50), unique=True)
fullname = Field(String(255))
email = Field(String(255))
passwordmd5 = Field(String(32))
def __repr__(self):
return "<Users ({})({})({})>".format(self.username, self.fullname, self.email)
def prettyname(self):
return {'username':'User Name', 'fullname':'Full Name', 'email':'Email Address', 'passwordmd5':'$hidden$'}
In my code I needed a way of getting 'pretty' label names for a GUI without having to hard code this in a GUI (I've been trying to create a dynamic way of building GUI forms). So, I added the 'prettyname' method to my data model to give me some application specific metadata in that data model. All I'm doing is returning a dictionary of items.
I had a secondary problem in that sometimes I needed to get this data from the class instance for Users and sometimes for a query result for Users (for example, Users.get_by(id=1)). As it turned out, retrieving this data had to be done in two ways. In the class instances I had to get the value this way:
prettyname = Users().prettyname()['username']
But when I was using query results it was:
prettyname = queryresult.prettyname()['username']
SQLAlchemy seems to have a real problem when I was using the former method (the class instance method) - as this was being used everytime I was seeing the crash. When I was using the latter instance I was never seeing a crash. Still, I needed access to that metadata in the class instance.
The fix, or should I say what turned out to fix this came from another Stackoverflow article (thank you everyone at Stackoverflow - I'd be nothing without you). I changed the structure of the dbmodel:
class Users(Entity):
using_options(tablename = 'users')
username = Field(String(50), unique=True, info={'prettyname':'User Name'})
fullname = Field(String(255), info={'prettyname':'Full Name'})
email = Field(String(255), info={'prettyname':'Email Address'})
passwordmd5 = Field(String(32), info={'hidden':True})
def __repr__(self):
return "<Users ({})({})({})>".format(self.username, self.fullname, self.email)
This allows me to use a common method of introspection to get the dictionary data in the info argument, regardless if I'm looking at a class instance, or a query result. In this case I use the '.table' method of either the class or query result, then get the column that I need (.c), then use the .info method of that column to return the dictionary.
Whatever the case, now SQLAlchemy no longer tries to arbitrarily insert rows in the database anymore.

Categories

Resources