sqlalchemy - How to select count from a union query - python

My goal is that I have two tables, each describes a relationship between a user and either a "team" or "company"
If there are any rows that says "admin" in the tables for the userid, then I want to know that, otherwise if no rows then i need to know that as well.
What I have so far is as follows (UserTeamLink and UserCompanyLink) are Orm tabels.
obj = UserTeamLink
q1 = session.query(func.count(obj.type).label("cnt")).filter(obj.type == 'admin').filter(obj.user_id == id) ; str(q1) ; q1.all()
obj = UserCompanyLink
q2 = session.query(func.count(obj.type).label("cnt")).filter(obj.type == 'admin').filter(obj.user_id == id) ; str(q2) ; q2.all()
my_union = q1.union_all(q2)
query = select([func.sum(my_union.c.cnt).label("total_cnt")], from_obj=my_union)
query.all()
however, the line "query = select([func.sum(my_union.c.cnt).label("total_cnt")], from_obj=my_union)" breaks with:
AttributeError: 'BaseQuery' object has no attribute 'c'
The entire output is as follows:
>>> obj = UserTeamLink
>>> q1 = session.query(func.count(obj.type).label("cnt")).filter(obj.type == 'admin').filter(obj.user_id == id) ; str(q1) ; q1.all()
'SELECT count(user_team.type) AS cnt \nFROM user_team \nWHERE user_team.type = :type_1 AND user_team.user_id = :user_id_1'
[(0L,)]
>>> obj = UserCompanyLink
>>> q2 = session.query(func.count(obj.type).label("cnt")).filter(obj.type == 'admin').filter(obj.user_id == id) ; str(q2) ; q2.all()
'SELECT count(user_company.type) AS cnt \nFROM user_company \nWHERE user_company.type = :type_1 AND user_company.user_id = :user_id_1'
[(0L,)]
>>>
>>> my_union = q1.union_all(q2)
>>> query = select([func.sum(my_union.c.cnt).label("total_cnt")], from_obj=my_union)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'BaseQuery' object has no attribute 'c'
Is there a way to fix this?
In summary what I'm attempting to do is select counts from both tables, then union into another table and then add them.
Thank you.

You need to use my_union as subquery
my_union = q1.union_all(q2).subquery()
query = select([func.sum(my_union.c.cnt).label("total_cnt")], from_obj=my_union)

Here's my own problem solved, I used a slightly different approach than #r-m-n's however I think both should work:
q_union = q1.union_all(q2)
q_union_cnt = db.session.query(func.sum(q_union.subquery().columns.cnt).label("total_cnt"))
admin_points = q_union_cnt.scalar()

Related

SQLalchemy attribute error when dynamically creating both table and columns names when using string variables

I'm having an issue where I need to create a new tables and a specific columns because I need to do SELECT INTO from a regular data table into a mapping table. The table names and two columns names are dynamic due to the nature of the data that I'm working with in the database.
I tried using an example in Use variable column headings in SQLAlchemy, but I'm still getting errors being raised. Here's the basic code:
RecIndex = # some string key value that changes
tableData = "Data-" + str(i)
tableName = f'Mapping{idx}'
colName = f'Voltage{i}-V{idx}'
col_list = ['Reading', 'Date', colName]
t_list = {TableData: [RecIndex, colName], tableName: [RecIndex, colName]}
table_list = []
for t_name, col_name in t_list.items():
t = Table(
t_name, metadata,
Column('Reading', Integer),
Column('Date', Date),
*[Column(name, Integer) for name in col_name]
)
table_list.append(t)
t1 = table_list[0] # Mapping table
t2 = table_list[1] # Data table
sel = t1.insert().from_select(col_list, t2.select().where(t2.c.colName > 0)) # FAILS HERE
However, when I try to build the sel variable, it fails and I get this error message:
Traceback (most recent call last):
File "C:\Python\Python\lib\site-packages\sqlalchemy\sql\base.py", line 1201, in getattr
return self._index[key]
KeyError: 'colName'
sel = t1.insert().from_select(col_list, t2.select().where(t2.c.colName > 0))
File "C:\Python\Python\lib\site-packages\sqlalchemy\sql\base.py", line 1203, in getattr
util.raise_(AttributeError(key), replace_context=err)
File "C:\Python\Python\lib\site-packages\sqlalchemy\util\compat.py", line 207, in raise_
raise exception
AttributeError: colName
Anyone have any idea on why it isn't working? I would appreciate any help
In your example code you are referencing the column by the name of the variable, colName instead of the contents of the variable, ie. "Voltage0-V2". Try something like this:
sel = t1.insert().from_select(col_list, t2.select().where(getattr(t2.c, colName) > 0))
This is just the built-in getattr:
getattr
I think dict style lookups are also supported ie. t2.c[colName], so this might work too:
sel = t1.insert().from_select(col_list, t2.select().where(t2.c[colName] > 0))

Parse SQL to extract column and table names using python

I want to write a code that will extract table and column names from a query that does not have JOIN keyword. Instead, the cartesian join (,) is used as below:
SELECT suppliers.supplier_name, subquery1.total_amt
FROM suppliers
,
(SELECT supplier_id, SUM(orders.amount) AS total_amt
FROM orders
GROUP BY supplier_id) subquery1
WHERE subquery1.supplier_id = suppliers.supplier_id;"""
I tried using the below code but its not working in python 2.7 as i'm getting the error : Bool object not callable at line 21:
import itertools
import sqlparse
from sqlparse.sql import IdentifierList, Identifier
from sqlparse.tokens import Keyword, DML
def is_subselect(parsed):
if not parsed.is_group():
return False
for item in parsed.tokens:
if item.ttype is DML and item.value.upper() == 'SELECT':
return True
return False
def extract_from_part(parsed):
from_seen = False
print 'hi'
for item in parsed.tokens:
if item.is_group():
print 'group'
for x in extract_from_part(item):
yield x
if from_seen:
print 'from'
if is_subselect(item):
for x in extract_from_part(item):
yield x
elif item.ttype is Keyword and item.value.upper() in ['ORDER', 'GROUP', 'BY', 'HAVING']:
from_seen = False
StopIteration
else:
yield item
if item.ttype is Keyword and item.value.upper() == 'FROM':
from_seen = True
def extract_table_identifiers(token_stream):
for item in token_stream:
if isinstance(item, IdentifierList):
for identifier in item.get_identifiers():
value = identifier.value.replace('"', '').lower()
yield value
elif isinstance(item, Identifier):
value = item.value.replace('"', '').lower()
yield value
def extract_tables(sql):
# let's handle multiple statements in one sql string
extracted_tables = []
statements = (sqlparse.parse(sql))
for statement in statements:
# print statement.get_type()
if statement.get_type() != 'UNKNOWN':
stream = extract_from_part(statement)
print stream
extracted_tables.append(set(list(extract_table_identifiers(stream))))
return list(itertools.chain(*extracted_tables))
# strsql = """
# SELECT p.product_name, inventory.quantity
# FROM products p join inventory
# ON p.product_id = inventory.product_id;
# """
strsql = """SELECT suppliers.supplier_name, subquery1.total_amt
FROM suppliers
,
(SELECT supplier_id, SUM(orders.amount) AS total_amt
FROM orders
GROUP BY supplier_id) subquery1
WHERE subquery1.supplier_id = suppliers.supplier_id;"""
extract_tables(strsql)
Error : this is the traceback:
Traceback (most recent call last):
File "4.py", line 77, in <module>
extract_tables(strsql)
File "4.py", line 60, in extract_tables
extracted_tables.append(set(list(extract_table_identifiers(stream))))
File "4.py", line 40, in extract_table_identifiers
for item in token_stream:
File "4.py", line 21, in extract_from_part
if item.is_group():
TypeError: 'bool' object is not callable
Thanks to #Gphilo for the answer:
From the traceback it seems is_group is actually not a function, but a simple bool attribute. Try replacing item.is_group() with item.is_group and see if things improve

Python - MySQLdb parametrized query

I'm trying to create a simple script for changing some mysql data, but I'm little bit confused about parametrized queries. The code look like this:
reader_id = sys.argv[1]
cmd_id = sys.argv[2]
conn = mysql.connect(user='...', passwd='...', host='...', db='...')
curs = conn.cursor()
mysql_data = {
"reader_id": int(reader_id),
"cmd_id": int(cmd_id)
}
curs.execute("UPDATE `reader_config_hw` SET `changed_` = '1' "
"WHERE `object_id` = %(reader_id)d AND `id` = %(cmd_id)d;", mysql_data)
curs.execute("UPDATE `reader` SET `check_for_command` = '1' WHERE `id` = %(reader_id)d", mysql_data)
and the error result is
Traceback (most recent call last):
File "./main.py", line 25, in <module>
"WHERE `object_id` = %(reader_id)d AND `id` = %(cmd_id)d;", mysql_data)
File "/usr/lib64/python3.6/site-packages/MySQLdb/cursors.py", line 210, in execute
query = query % args
TypeError: %d format: a number is required, not str
It says that number is required but I already changed the type of the variable by int(reader_id), is it right?
Try changing
"WHERE 'object_id' = %(reader_id)d AND 'id' = %(cmd_id)d;"
to
"WHERE 'object_id' = %(reader_id)s AND 'id' = %(cmd_id)s;"
I think the 'd' after your variable calls indicates a "digit" whereas you need to use a string.
EDIT: to clarify, strings can be used in place of integers in MySQL queries

How do you compare two SQLAlchemy Column objects correctly with Python's unittest?

I wrote a unit test that is supposed to compare a Column object returned from my method. I'm creating one in the test, then getting one from my class. My test code looks like this:
def test_getting_correct_sa_object_from_column(self):
table = self.database.get_table_by_name('ESEventlogMain')
column = table.get_column_by_name('eventdata')
sa_column = sqlalchemy.Column('eventdata', sqlalchemy.Integer, primary_key=False)
self.assertEqual(sa_column, column.get_sa_object())
And the method of my class is returning this:
def get_sa_object(self):
if self.type == 'int':
sa_type = sqlalchemy.Integer
elif self.type == 'varchar':
sa_type = sqlalchemy.String(self.length)
return sqlalchemy.Column(self.name, sa_type, primary_key=self.is_primary_key)
When I run the test, it fails with this output:
Failure
Traceback (most recent call last):
File "C:\Users\tames.mctigue\PycharmProjects\db_setup_wizard\tests.py", line 107, in test_getting_correct_sa_object_from_column
self.assertEqual(sa_column, column.get_sa_object())
AssertionError: Column('eventdata', Integer(), table=None) != Column('eventdata', Integer(), table=None)
The AssertionError shows what looks like identical data so I'm stuck on what to look for. Is there a different comparison I should be using besides "assertEqual"?
Try to use direct column, like:
def test_getting_correct_sa_object_from_column(self):
table = self.database.get_table_by_name('ESEventlogMain')
column = table.get_column_by_name('eventdata')
sa_column = table.eventdata # <- Change this column
self.assertEqual(sa_column, column.get_sa_object())

Problem Using Variable in Sqlite3 Statement

I have a problem with a Python 2.7 project.
I'm trying to set a variable to a value retrieved from an sqlite3 database, but I'm having trouble. Here is my code thus far, and the error I'm receiving. Yes, the connection opens just fine, and the table, columns, and indicated row are there as they should be.
import sqlite3 import Trailcrest
conn = sqlite3.connect('roster.paw')
c = conn.cursor()
def Lantern(AI):
"""Pulls all of the data for the selected user."""
Trailcrest.FireAutoHelp = c.execute("""select fireautohelp
from roster
where index = ?;""", (AI,) ).fetchall()
The error is:
> Traceback (most recent call last):
> File "<pyshell#4>", line 1, in <module> Lantern(1)
> File "C:\Users\user\MousePaw Games\Word4Word\PYM\Glyph.py", line 20,
> in Lantern
> Trailcrest.FireAutoHelp = c.execute("""select fireautohelp from roster where index = ?;""", (AI,)).fetchall()
> OperationalError: near "index": syntax error
As Thomas K mentions in a comment, index is a SQL keyword.
You can either rename that column, or enclose in backticks:
Trailcrest.FireAutoHelp = c.execute("""select fireautohelp
from roster
where `index` = ?;""", (AI,) ).fetchall()

Categories

Resources