Need a query in Sqlalchemy with GROUP BY CASE - sql-server

All i need is a simple query with GROUP BY CASE like
SELECT
COUNT([Personnel].[PERSONNEL_ID]) AS value,
CASE
WHEN ([Personnel].[T_FIRSTNAME] = 'John') THEN 'John'
ELSE 'somethingelse'
END AS somelabel
FROM [Personnel]
GROUP BY
CASE
WHEN ([Personnel].[T_FIRSTNAME] = 'John') THEN 'John'
ELSE 'somethingelse'
END
in Sqlalchemy my query looks like this:
case_groups = case(
[(HrPersonnel.T_FIRSTNAME=='John', 'John')],
else_='somethingelse'
)
volunteers = session_mssql.query(
func.count(HrPersonnel.S_HR_PERSONNEL_ID).label('value'),
case_groups.label('somelabel'))\
.group_by(case_groups)\
.all()
mssql server executes the query without any problem (engine.connect().execute(text_query))
but Sqlalchemy ORM query gives me an error.
File
"c:\projects\sa_test\eggs\sqlalchemy-0.9.2-py2.7.egg\sqlalchemy\engine\default.py",
line 425, in do_execute cursor.execute(statement, parameters)
sqlalchemy.exc.ProgrammingError: (ProgrammingError) ('42000', "[42000]
[Microsoft][ODBC SQL Server Driver][SQL Server] Column
'Personnel.T_FIRSTNAME' is invalid in the select list because it is
not contained in either an aggregate function or the GROUP BY clause.
(8120) (SQLExecDirectW); [42000] [Microsoft][ODBC SQL Server
Driver][SQL Server]Statement(s) could not be prepared. (8180)")
u'SELECT count([Personnel].[PERSONNEL_ID]) AS value, CASE WHEN
([Personnel].[T_FIRSTNAME] = ?) THEN ? ELSE ? END AS somelabel \nFROM
[Personnel] GROUP BY CASE WHEN ([Personnel].[T_FIRSTNAME] = ?) THEN ?
ELSE ? END' ('John', 'John', 'somethingelse', 'John', 'John',
'somethingelse')
Why 'Personnel.T_FIRSTNAME' is invalid in the select list?
any suggestions are welcome.
Thanks,

I don't have an answer per se (although future readers may want to check this thread on the SQLAlchemy Google Group), but I've had a similar issue recently and found a workaround.
Instead of executing the query directly, convert it into a string and execute that. For example:
session.execute(
str(query.selectable.compile(compile_kwargs={'literal_binds': True}))
).fetchall()

Related

How to use order_by with UNION in SQLAlchemy

I'm using SQLAlchemy UNION, ORDER_BY is not working, it throwing this
error
Can't resolve label reference for ORDER BY/ GROUP BY/ DISTINCT etc. Textual SQL expression 'LastName' should be explictly declared as text('LastName')
query1 = db.query(Employee.LastName).filter(Employee.Age == 30)
query2 = db.query(Employee.LastName).filter(Employee.Salary > 25000)
result = query1.union(query2).order_by('LastName').all()
Then I tried the below line too, but it didn't work, its throwing this error
(pyodbc.ProgrammingError)('42S22',"[42S22][Microsoft][ODBC Driver 17 for SQL Server][SQL Server] invalid column name 'LastName'. [SQL Server] Statement(s) could not be prepared. (8180)
result = query1.union(query2).order_by(text('LastName').all()

Laravel MS SQL DB::RAW Query returns "Invalid column name"

I'm using SQL Server within my Laravel application. I need to group/sum a table for the month/year. My query looks like this:
$positions = BelegPos::select(
DB::raw("YEAR(Datum) as year"),
DB::raw("MONTH(Datum) as month"),
DB::raw("SUM(Menge) as stunden")
)->groupBy("year", "month")
->get();
I'll get the following error message:
SQLSTATE[42S22]: [Microsoft][ODBC Driver 17 for SQL Server][SQL
Server]Invalid column name 'year'.
I don't know what's wrong, as the query the way I build it works fine with MySQL servers.
Thanks for any hint in the right direction.
UPDATE
I researched a bit more and read about somthing that points out, that the Select Statement isn't available in the GROUP BY section and that you have to add the same query there. so my query looks now like this:
$positions = selectlineBelegPos::select(
DB::raw("YEAR(Datum) as the_year"),
DB::raw("MONTH(Datum) as the_month"),
DB::raw("SUM(Menge) as stunden")
)
->groupBy(DB::raw("YEAR(Datum) as the_year"),DB::raw("MONTH(Datum) as the_month"))
->get();
Still not the solution but the error message has changed to this:
SQLSTATE[42000]: [Microsoft][ODBC Driver 17 for SQL Server][SQL
Server]Incorrect syntax near the keyword (SQL: select
YEAR(Datum) as the_year, MONTH(Datum) as the_month, SUM(Menge) as
stunden from [BELEGP]
So I think there is just one small thing wrong but can't figure out what.
Any ideas?
Solution
Just found the the solution:
$positions = selectlineBelegPos::select(
DB::raw("YEAR(Datum) AS year"),
DB::raw("MONTH(Datum) AS month"),
DB::raw("SUM(Menge) AS stunden")
)
->groupBy(DB::raw("YEAR([Datum])"),DB::raw("MONTH([Datum])"))
->get();
Indeed isn't the dynamically value names (year and month) unavailable in the GROUPBY clause, instead you have to call the DB::raw again but now without generating the key words again.
thank you all for the support!

How to pass a CONTAINSTABLE parameterized query to SQL Server using pyodbc with a condition that contains terms "AND"-ed together?

Background/Problem
According to Microsoft docs, the syntax for a CONTAINSTABLE query uses a CONTAINS search condition which as far as I can tell, must be entirely enclosed in single quotes. The problem occurs when I try to parameterize the query in pyodbc. Any form of quotation that is required for specifying the condition part of the query seems to cause pyodbc/the odbc driver to ignore the parameter markers, resulting in the infamous "The SQL contains x parameter markers, but >x parameters were supplied' error.
What I'm (Trying) to do
I get user search input that I break into a list of words and build a dynamic SQL CONTAINS search condition that puts in parameter markers instead of the actual words that is finally inserted into the rest of the SQL statement string in my cursor.execute() statement.
Ex: User searches for "Backflow prevention device"
Used this answer as the general way to solve the problem of putting multiple words in a fulltext CONTAINS/CONTAINSTABLE query. My dynamic CONTAINSTABLE condition is generated as ? AND ? AND ? which is then inserted into my query (which actually contains this and another fulltext query combined with UNION that uses the NEAR keyword between words instead of AND) via .format().
My Code
def parameterize(search_string, kw):
expanded = search_string.split(maxsplit=14)
sql = ""
for word in expanded:
sql = "{sql}{cond_keyword} ? ".format(sql=sql,
cond_keyword=kw if sql and len(expanded) > 1 else "",
word=word
)
sql = "{sql}".format(sql=sql.lstrip().rstrip())
qargs = tuple(expanded)
return sql, qargs
def ka_search(search_str):
and_cond = parameterize(search_str, "AND")
near_cond = parameterize(search_str, "NEAR")
near_params = near_cond[0]
and_params = and_cond[0]
# build a final tuple of all parameters in order of the individual sub-queries that are union-ed together
args1 = list(and_cond[1])
args2 = [search_str]
args3 = list(near_cond[1])
args = args1 + args2 + args3 + args2
try:
cursor = db_connect()
cursor.execute("""
SELECT Title, CONCAT(RTRIM(LEFT(Bodytext,150)), '...') as BodyText, Keywords, RecId,
rank, CONCAT('/kb/', RecId) AS Link
FROM KnowledgeArticle AS FT_TBL INNER JOIN
CONTAINSTABLE(KnowledgeArticle, (Title, keywords), {and_params}) AS KEY_TBL
ON FT_TBL.RecID = KEY_TBL.[KEY]
WHERE VisibleToCustomerPortal = 'true' AND
Status = 'Published'
UNION
SELECT Title, CONCAT(RTRIM(LEFT(Bodytext,150)), '...') as BodyText, Keywords, RecId,
rank, CONCAT('/kb/', RecId) AS Link
FROM KnowledgeArticle AS FT_TBL INNER JOIN
FREETEXTTABLE(KnowledgeArticle, Title, ?) AS KEY_TBL
ON FT_TBL.RecID = KEY_TBL.[KEY]
WHERE VisibleToCustomerPortal = 'true' AND
Status = 'Published'
UNION
SELECT Title, CONCAT(RTRIM(LEFT(Bodytext,150)), '...') as BodyText, Keywords, RecId,
rank, CONCAT('/kb/', RecId) AS Link
FROM KnowledgeArticle AS FT_TBL INNER JOIN
CONTAINSTABLE(KnowledgeArticle, (Title, keywords), {near_params}) AS KEY_TBL
ON FT_TBL.RecID = KEY_TBL.[KEY]
WHERE VisibleToCustomerPortal = 'true' AND
Status = 'Published'
SELECT Title, CONCAT(RTRIM(LEFT(Bodytext,150)), '...') as BodyText, Keywords, RecId,
rank, CONCAT('/kb/', RecId) AS Link
FROM KnowledgeArticle AS FT_TBL INNER JOIN
FREETEXTTABLE(KnowledgeArticle, Bodytext, ?) AS KEY_TBL
ON FT_TBL.RecID = KEY_TBL.[KEY]
WHERE VisibleToCustomerPortal = 'true' AND
Status = 'Published'
ORDER BY rank DESC
""".format(and_params=and_params, near_params=near_params), args)
The above code manages to pass the search terms into the parameters and the correct number of parameter markers is recognized, but I hit a SQL error when the statement executes:
"('42000', "[42000] [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Incorrect syntax near the keyword 'AND'. (156) (SQLExecDirectW); [42000] [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Incorrect syntax near 'NEAR'. (102); [42000] [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Statement(s) could not be prepared. (8180)")"
The query does work as expected when plugging in the search string in the FREETEXTTABLE sub-queries and the '"[word] AND [word] AND ...' and '"[word] NEAR [word] NEAR ...' conditions directly and running them in SSMS.
It seems like I have mutually exclusive options when trying to make this work as a dynamic T-SQL parameterized query using pyodbc. I attempted to find an answer on here but none of the parameterized query/pyodbc/full text T-SQL questions seem to address my problem. Thank you in advance for the help!
Additional Info
SQL Server 2016
Python 3.6
Running on Windows

Multipart queries in SQL Server with RODBC

I am trying to use GO to get R to pull a multipart query from a SQL Server database but R keeps erroring out on me when I try this. Does anyone know a workaround to get RODBC to run multipart queries?
Example query:
query2 = "IF OBJECT_ID('tempdb..#ATTTempTable') IS NOT NULL
DROP TABLE #ATTTempTable
GO
SELECT
* INTO #ATTTempTable
FROM ETL.ATT.fact_responses fr
WHERE fr.ResponseDateTime > '2015-07-06'
"
channel <- odbcConnect("<host name>", uid="<uid>", pwd="<pwd>")
raw = sqlQuery(channel, query2)
close(channel)
and result
> raw
[1] "42000 102 [Microsoft][ODBC Driver 11 for SQL Server][SQL Server]Incorrect syntax near 'GO'."
[2] "[RODBC] ERROR: Could not SQLExecDirect 'IF OBJECT_ID('tempdb..#ATTTempTable') IS NOT NULL\n DROP TABLE #ATTTempTable\n\nGO\n\nSELECT\n\t* INTO #ATTTempTable\nFROM ETL.ATT.fact_responses fr\nWHERE fr.ResponseDateTime > '2015-07-06'\n'"
>
Because your query contains multiple line with conditional logic it resembles a stored procedure.
Simply save that stored procedure in SQL Server:
CREATE PROCEDURE sqlServerSp #ResponseDateTime nvarchar(10)
AS
IF OBJECT_ID('tempdb..#ATTTempTable') IS NOT NULL
DROP TABLE #ATTTempTable;
GO
-- suppresses affected rows message so RODBC returns a dataset
SET NO COUNT ON;
GO
-- runs make-table action query
SELECT * INTO #ATTTempTable
FROM ETL.ATT.fact_responses fr
WHERE fr.ResponseDateTime > #ResponseDateTime;
GO
And then run the stored procedure in R. You can even pass parameters like the date:
channel <- odbcConnect("<host name>", uid="<uid>", pwd="<pwd>")
raw = sqlQuery(channel, "EXEC sqlServerSp #ResponseDateTime='2015-07-06'")
close(channel)
You can't. See https://msdn.microsoft.com/en-us/library/ms188037.aspx
you have to divide your query into two statements and run them separately.

Doctrine2 issue with running GROUP BY against a MSSQL

I'm trying to run a custom query on my DB with Doctrine2 using the following:
$qb = $em->createQueryBuilder();
$qb->select(array('c', 'count(uc) as numMembers'))
->from('AcmeGroupBundle:Group', 'c')
->leftJoin('c.members', 'uc', 'WITH', 'uc.community = c.id')
->groupBy('c')
->orderBy('numMembers', 'DESC')
->setFirstResult( $offset )
->setMaxResults( $limit );
$entities = $qb->getQuery()->getResult();
This query runs flawlessly on my local MySQL DB. But when I try to run it against my production DB (MSSQL), i get the following error:
SQLSTATE[42000]: [Microsoft][SQL Server Native Client 11.0][SQL Server]Column 'Group.discr' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
I do have a discriminator column because I have classes inheriting from Group.
Any suggestions on how should I change the query to make it compatible with MSSQL?
Thanks!

Resources