Having trouble writting a query with "case" in sqlalchemy - sqlalchemy

In the query below, I keep getting the error "An expression of non boolean type specified in a context where a condition is expected near End". Down below is my code I'm not trying to return the rows where the pk__street_name == NULL in the join. But I get the error listed above. How can I fix this.
result = session.query(
tamDnRangeMap, tamStreet
).join(tamStreet)
.filter(
case(
[(tamDnRangeMap.pk_street_name == NULL, 0)],
else_ = 1
)
).all()

First remark is that you don't want equality comparisons anywhere near NULL in SQL, it is done with IS or IS NOT.
Once you know that, you can use SQLAlchemy's is_ or isnot* operators.
All in all, you're using CASE where you don't really need it, put the IS NOT NULL condition in filter directly.
result = (
session.query(tamDnRangeMap, tamStreet)
.join(tamStreet)
.filter(tamDnRangeMap.pk_street_name.isnot(None))
.all()
)
* NB. isnot has been deprecated and is replaced by is_not since SQLAlchemy 1.4, but the question uses case with list of whens which was also deprecated in 1.4.

Related

Update Statement with filter, sortBy, map and take

I am writing the following update statement against a MySql database:
Orders.filter(_.cookie === cookie).sortBy(_.id.desc).map(_.payTypeId).take(1)
sortBy and take(1) are being used to ensure I'm only updating the most recent order for a particular cookie. This query works fine as a regular SELECT, this issue here is using it as an update statement.
The error I'm getting:
[SlickException: A query for an UPDATE statement must resolve to a comprehension with a single table -- Unsupported shape: Comprehension s2, Some(Apply Function =), None, ConstArray((Path s2.id,Ordering(Desc,NullsDefault))), None, None, Some(LiteralNode 1 (volatileHint=false)), None, false]
If I remove the take(1) clause, everything works fine. Why I am I getting this error?
Github seems to have a discussion about this, with no solution.
Workaround using plain SQL
(This query returns a Vector[Int])
val updateQuery =
sql"""
UPDATE
orders
SET
orders.pay_type_id = $id
WHERE
orders.cart_cookie = $cookie
ORDER BY id desc
LIMIT 1
""".as[Int]

SQLAlchemy - Error on nested CASE

I want to transfer SQL to SQLAlchemy and have the case that I have nested case statements.
The simple case is working:
stmt = sqlalchemy.select([self.tusg_view_specials]).where(
sqlalchemy.case([
(self.tusg_view_specials.c.webo_close_date >= (datetime.date.today() - datetime.timedelta(days=30)), 1),
(self.tusg_view_specials.c.wo_closed_date >= (datetime.date.today() - datetime.timedelta(days=61)), 1)
], else_=0),
)
But when I have a nested case, means that the THEN is a case clause instead of a simple value:
stmt = sqlalchemy.select([self.tusg_view_specials]).where(
sqlalchemy.case([
(self.tusg_view_specials.c.webo_close_date >= (datetime.date.today() - datetime.timedelta(days=30)), 1),
(self.tusg_view_specials.c.wo_closed_date >= (datetime.date.today() - datetime.timedelta(days=61)), 1),
(self.tusg_view_specials.c.work_order_number is None,
sqlalchemy.case([(self.tusg_view_specials.c.flag_is_abw == 1, 1)], else_=0))
], else_=0), <<-- This line is shown to cause the error
)
I get the following error message, I don't know how to deal with it:
sqlalchemy.exc.ArgumentError: Ambiguous literal: False. Use the 'text()' function to indicate a SQL expression literal, or 'literal()' to indicate a bound value.
I can read the text, but don't know how to interpret it. Search results on "nested case" on SQLAlchemy are very little to none.
As also Ilja Everilä wrote, the cause is the "IS None", which needs to be replaced by
table_instance.c.work_order_number.is_(None)
or
table_instance.c.work_order_number == None

What is the use of 'WHERE TRUE' in MySQL

I'm working on my colleague's old project and I found on her code WHERE TRUE AND ID='1' AND STAT='1'.
I've tried to removed TRUE AND so the query become WHERE ID='1' AND STAT='1' and get the same result.
I know we can use TRUEas boolean to search 'where something is true' such as WHERE FLAG = TRUE and this MySQL documentation state that
The constants TRUE and FALSE evaluate to 1 and 0, respectively. The constant names can be written in any lettercase.
I also tried SELECT * FROM MYTABLE WHERE TRUE but it's just the same as SELECT * FROM MYTABLE
what is the purpose of TRUE in her query?
It has no specific functional purpose. Your colleague may have included it if they were adhering to a specific style guide that recommends that all SELECT queries have explicit WHERE clauses. If an explicit WHERE clause is not provided, the default is to select all rows. Adding a WHERE clause that is always true will have the same effect.
Another way this query could have come about is if it was generated by a code generation tool that always had to write in a WHERE clause due to the way it was written.
for example:
myQuery = "SELECT X FROM Y WHERE " + ConditionMet(data)?" AccountID = '" + AcctId + "'" : "1=1";
This means that if ConditionMet(data) is true, then only return rows where AccountID matches the AcctId you are passing in. If it is false, then return all rows.
Adding a "dummy" 1=1 makes the code generator simpler to write. More generally, 1=1 is as legitimate a boolean clause as any other, and can be "dropped" into a conditional expression without having to special-case the query to omit the WHERE clause.
Similarly, adding a WHERE clause that is always false (e.g. "WHERE 1=0") will result in zero rows being returned.
Do note that the example code here is vulnerable to SQL Injection, so it should not be used in cases where you are dealing with AccountID's that you did not produce yourself. There are multiple ways to secure it that are beyond the scope of this answer.
If you're writing your SQLString on runtime, and you might add different "where" clausules but you don't know which of all of them will be the first, it makes it easy as all of them may start with "AND ....."
Example:
SQLString:='SELECT * FROM YOUTABLE WHERE TRUE'
If condition1 THEN SQLString:=SQLString+' AND Whatever=whatever';
If condition2 THEN SQLString:=SQLString+' AND Whatever=whatever';
If condition3 THEN SQLString:=SQLString+' AND Whatever=whatever';
If condition4 THEN SQLString:=SQLString+' AND Whatever=whatever';
otherwhise, you should add the WHERE clause not on the first SQLString:= but on the first condition that happens to be true, which you don't know will it be a priori
it is not as much relevant but if find important where adding dynamic conditions,for example in php.
$condition_stmt="";
if ($start_date !="" && $end_date!="")
{
$condition_stmt="and nos.status_date between '".$start_date."' and '".$end_date."'";
}
else if ($start_date!="")
{
$condition_stmt="and nos.status_date >='".$start_date."'";
}
else
{
$condition_stmt="and nos.status_date <='".$end_date."'";
}
$sql="select * from table where true ".$condition_stmt=.";

Ambiguous literal error when converting SQL Case on SELECT to SQLAlchemy format

I'm having problems translating the following CASE Statement on SELECT to SQLAlchemy ORM. I keep getting the error : "Ambiguous literal: False. Use the 'text()' function to indicate a SQL expression literal, or 'literal()' to indicate a bound value."
The Case statement checks a variable, and then either returns a column on the database or selects a literal. The literal is set prior to running the sql statement.
DECLARE #str_cntry_cd varchar(3)
SELECT COUNTRY_CD,
CASE WHEN #str_cntry_cd IS NOT NULL THEN RESOLUTION_ID
ELSE 10
END AS RESOLUTION_ID
FROM COUNTRY
The SQLAlchemy code looks as follows:
country_tbl = getattr(main_db.Base.classes, "COUNTRY")
c_res = case([(country_code != None, country_tbl.RESOLUTION_ID),],
else_ = literal(10)).label("resolution_id")
programs_sa = main_db.session.query(country_tbl.COUNTRY_CD.label("country_cd"),
c_res).all()
The table is as follows:
CREATE TABLE dbo.COUNTRY
(
COUNTRY_CD varchar(4) NOT NULL,
RESOLUTION_ID int NOT NULL
)
SQLAlchemy .9.9
Windows 7
SQL Server 2008 R2
Python 2.7
country_code != None is causing the problem.
That evaluates to python's True/False, which SQLAlchemy doesn't know how to convert to SQL's true/false as it is ambiguous. So you would need to either add the literal true/false for the SQL you are using, by using SQLAlchemy's text or literal functions. But a better way would be to use the true or false functions.
http://docs.sqlalchemy.org/en/latest/core/sqlelement.html#sqlalchemy.sql.expression.true
http://docs.sqlalchemy.org/en/latest/core/sqlelement.html#sqlalchemy.sql.expression.false
So you would replace the buggy section of the code with:
db.false() if country is None else db.true()
Hope this helps.

Linq to EF not returning all data

I have following query against EF whereby mysql was used:
var query = from r in context.myContext
where r.clmn1.CompareTo("2015-11-19 00:00:00") > 0)
orderby r.someColumn
select r;
return query;
The number of returned rows is as expected. however some values of the property r.clmn2 repeat itself in the result of the query. For example I could not find clmn2 == 220011 because it was "overwritten" by the value 220033 (The value 220033 is correct and expected but should not "overwrite" other values). Strangely enough, when I add this condition to the query I get it in the result (of course then only and only this value) which means that the first condition is also valid for clmn2:
var query = from r in context.myContext
where r.clmn1.CompareTo("2015-11-19 00:00:00") > 0) && r.clmn2.Equals("220011")
orderby r.someColumn
select r;
return query;
The same query (the first one) works at DB-level and returns all values (will not be overwritten)
SELECT * FROM myContext.myTable
WHERE r.clmn1 > ("2015-11-19 00:00:00")
ORDER BY r.someColumn
It should be a problem of EF. I hope someone could help me!
Thanks in Advance.
I have prefixed the column/property clmn2 with [key] atribute in the generated entity class so that it is now a part of the multiple key, i.e., with other columns/properties. It works and i get all values from DB. Maybe cus this property comes from a DB-view, Visual Studio could not recognize it as a primary key as done by other properties.