converting SQL window function to SqlAlchemy - sqlalchemy

I'm new with SqlAlchemy window functions.
I have SQL statement which works fine and i try to convert it to SqlAlchemy code
How it can be done? Thank in advance.
select p.id,
p.submitted_at,
p.user_id,
first_value(p.submitted_at) over (partition by "user_id" order by p.submitted_at DESC) as last_project
from project p join "user" u on p."user_id" = u.id
I start with this one but receive AttributeError Neither Over object Comparator has attribute all:
with get_session() as session:
query = session.query(Project, User)
query = query.join(User, User.id == Project.user_id)
query = func.first_value(Project.submitted_at).over(
partition_by= "user_id", order_by=Project.submitted_at
)
query.all() # AttributeError

Related

SQLAlchemy: Join table with object's children

I have three object types with corresponding tables:
class Order:
suborders = relationship('Suborder', lazy='dynamic')
class Suborder:
...
class PurchaseOrder:
suborder = relationship('Suborder', foreign_keys=[suborder_id], lazy='joined')
Now I need to get all PurchaseOrder instances matching Order's suborders. In pure SQL I'd write something like this:
SELECT po.*
FROM purchase_orders AS po JOIN suborders AS so ON po.suborder_id = so.id
WHERE so.order_id = 'order-007'
How do I do it using SQLAlchemy? I tried this:
o = Order.query.get('order-007')
PurchaseOrder.query.join(o.suborders)
But this gave me an error:
AttributeError: 'AppenderQuery' object has no attribute 'is_selectable'
What is the right way to do it?
Getting required data with just one query:
query = (
PurchaseOrder.query
.join(Suborder)
.filter(Suborder.order_id == 'order-007')
)
If you already have the Order instance o, you can do the following:
o = Order.query.get('order-007') # already have the order instance
query = (
session.query(PurchaseOrder)
.join(Suborder)
.with_parent(o)
)
, but you will still need to join on Suborder.
But again, the first one would correspond to the SQL query you provided as the SQL implementation.

Convert sql query with a join on a subselect to a linq statement

I am trying to convert the following sql query to LINQ statement
SELECT t.*
FROM (
SELECT Unique_Id, MAX(Version) mversion
FROM test
GROUP BY Unique_Id
) m INNER JOIN
test t ON m.Unique_Id = t.Unique_Id AND m.mversion = t.Version
LINQ statement
var testalt = (from altt in CS.test
group altt by altt.Unique_Id into g
join bp in CS.alerts on g.FirstOrDefault().Unique_Id equals bp.Unique_Id
select new ABCBE
{
ABCName= bp.Name,
number = bp.Number,
Unique_Id = g.Key,
Version = g.Max(x=>x.Version)
});
I am getting an error of where clause. Please help
SQL FIDDLE
This is not an easy straight forward conversion but you can accomplish the same thing using linq method syntax. The first query is executed to an expression tree, then you are joining that expression tree from the grouping against CS.alerts. This combines the expression tree from CS.test query into the expression tree of CS.alerts to join the two expression trees.
The expression tree is evaluated to build the query and execute said query upon enumeration. Enumeration in this case is the ToList() call but anything that gets a result from the enumeration will execute the query.
var query1 = CS.test.GroupBy(x => x.Unique_Id);
var joinResult = CS.alerts.Join(query1,
alert => new { ID = alert.Unique_Id, Version = alert.Version },
test => new { ID = test.Key, Version = test.Max(y => y.Version },
(alert, test) => new ABCBE {
ABCName = alert.Name,
number = alert.Number,
Unique_Id = test.Key,
Version = test.Max(y => y.Version)
}).ToList();
Because query1 is still an IQueryable and you are using CS.alerts (which I'm guessing CS is your data context) it should join and build the query to execute upon the ToList() enumeration.

JPA's Map<KEY, VALUE> query by JPQL failed

I am storing a Map in JPA , which stores a keyword translation in each language . such as one object stores Locale.ENGLISH -> "Father" , Locale.CHINESE -> "PaPa". And another object stores Locale.ENGLISH -> "Mother" , Locale.CHINESE -> "MaMa";
Here is my working design :
public class Relation {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#ElementCollection
#MapKeyColumn(name="locale")
#Column(name="value")
#CollectionTable(name = "RelationName", joinColumns = #JoinColumn(name = "relation_id"))
private Map<Locale, String> langMap = new HashMap<>();
// other fields skipped
}
It works well , I can store many keyword translations to DB. But when query with JPQL , it has some problems :
For example , I want to find which Relation has English key with value "Father" :
This is my code :
Relation r = em.createQuery("select r from Relation r join r.langMap m where ( KEY(m) = :locale and VALUE(m) = :value ) " , Relation.class)
.setParameter("locale" , locale)
.setParameter("value" , value)
.getSingleResult();
It generates this strange / not-working SQL :
Hibernate:
select
relation0_.id as id1_18_
from
Relation relation0_
inner join
RelationName langmap1_
on relation0_.id=langmap1_.relation_id
where
langmap1_.locale=?
and (
select
langmap1_.value
from
RelationName langmap1_
where
relation0_.id=langmap1_.relation_id
)=?
00:16:12.038 WARN o.h.e.jdbc.spi.SqlExceptionHelper - SQL Error: 1242, SQLState: 21000
00:16:12.038 ERROR o.h.e.jdbc.spi.SqlExceptionHelper - Subquery returns more than 1 row
I don't know why it generates that strange subquery.
I can solve this problem by Criteria :
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Relation> criteria = builder.createQuery(Relation.class);
Root<Relation> root = criteria.from(Relation.class);
criteria.select(root);
MapJoin<Relation , Locale , String> mapJoin = root.joinMap("langMap");
criteria.where(builder.and(
builder.equal(mapJoin.key(), locale) ,
builder.equal(mapJoin.value() , value))
);
return em.createQuery(criteria).getSingleResult();
It generates correct SQL ( where langmap1_.locale=? and langmap1_.value=? ) and works well.
But I feel Criteria is too complicated. And I wonder why the JPQL failed? How to correct the JPQL ?
Thanks.
Env :
JPA2 , Hibernate 4.2.3 , MySQL dialect
I had the same problem. It looks like accessing map by ref (without VALUE()) already gives you a map entry value, i.e. the next JPQL should be transformed to a valid SQL:
select r from Relation r join r.langMap m where ( KEY(m) = :locale and m = :value )
I had a similar problem using the JPQL VALUE() operator with Hibernate. It seems that Hibernate implements the VALUE() operator like the java.util.Map.values() method in Java. It generates a subquery that returns all values in the map, i.e. all rows of the mapping table that are related to the entity holding the Map attribute. As soon as you have more then one key/value pair in the map, a comparison expression, which expects scalar expressions as operands, will fail.
What you can do is to turn the comparison expression around and convert it to an IN expression.
Instead of:
select r from Relation r join r.langMap m
where ( KEY(m) = :locale and VALUE(m) = :value )
You can write:
select r from Relation r join r.langMap m
where ( KEY(m) = :locale and :value in (VALUE(m)) )
I hope this way your query will work.
The correct JPQL could be like this:
SELECT r FROM Relation r JOIN r.langMap map
WHERE map[:locale] = :value

Making a subrequest in SQLAlchemy

I try to do the follwoing request in SQLAlchemy (ORM) :
SELECT id, ref_prog FROM stepvand_1c_1t.equipment_day_hour
WHERE id IN (SELECT id FROM stepvand_1c_1t.equipment WHERE equipment_type='L')
I did :
subq = session.query(Equipment)
subq = subq.filter(Equipment.equipment_type == "L").subquery()
query = session.query(EquipmentDayHour)
query = query.filter(EquipmentDayHour.id.in_(subq))
But that doesn't work...
Python tells me that the subrequest has too many columns.
I think you should only change one line of your sample code:
# error: includes all columns of Equipment
`subq = session.query(Equipment)`
# correct: include only ID column
`subq = session.query(Equipment.id)`
However, I believe that you can do this without subquery:
query = (session.query(EquipmentDayHour).
# version-1: if you have a relationship between EquipmentDayHour and Equipment
join(Equipment).
# version-2: if you do not have such relationship
#join(Equipment, EquipmentDayHour.id==Equipment.id).
filter(Equipment.equipment_type == "L")
)

Django ORM Creates Phantom Alias in SQL Join

I am executing the following code (names changed to protect the innocent, so the model structure might seem weird):
memberships =
models.Membership.objects.filter(
degree__gt=0.0,
group=request.user.get_profile().group
)
exclude_count =
memberships.filter(
member__officerships__profile=request.user.get_profile()
).count()
if exclude_officers_with_profile:
memberships = memberships.exclude(
member__officerships__profile=request.user.get_profile()
)
total_count = memberships.count()
which at this point results in:
OperationalError at /
(1054, "Unknown column 'U1.id' in 'on clause'")
The SQL generated is:
SELECT
COUNT(*)
FROM
`membership`
WHERE (
`membership`.`group_id` = 2 AND
`membership`.`level` > 0.0 AND
NOT (
`membership`.`member_id`
IN (
SELECT
U2.`member_id`
FROM
`membership` U0 INNER JOIN `officers` U2
ON (U1.`id` = U2.`member_id`)
WHERE U2.`profile_id` = 2
)
)
)
It appears that the SQL Join's ON statement is referencing an alias that hasn't been defined. Any ideas why!? I dropped my MySQL database and re-synced the tables from my models to try and ensure that there weren't any inconsistencies there.
The structure of the models I'm using are:
class Membership(models.Model):
member = models.ForeignKey(Member, related_name='memberships')
group = models.ForeignKey(Group, related_name='memberships')
level = models.FloatField(default=0.0)
class Member(models.Model):
name = models.CharField(max_length=32)
class Officer(models.Model):
member = models.ForeignKey(Member, related_name='officerships')
profile = models.ForeignKey(UserProfile)
class UserProfile(models.Model)
group = models.ForeignKey(Group)
class Group(models.Model)
pass
I think this may be a symptom of:
http://code.djangoproject.com/ticket/9188
which was fixed as of django revision 9589, I think. Now how to figure out which revision I'm working from...
Confirmed. When I implemented the change referenced in the ticket above:
http://code.djangoproject.com/changeset/9589
my error went away.