Peewee select with circular dependency - foreign-key-relationship

I have two models Room and User. Every user is assigned to exactly one room and one of them is the owner.
DeferredUser = DeferredRelation()
class Room(Model):
owner = ForeignKeyField(DeferredUser)
class User(Model):
sessid = CharField()
room = ForeignKeyField(Room, related_name='users')
DeferredUser.set_model(User)
Now, having sessid of the owner I'd like to select his room and all assigned users. But when I do:
(Room.select(Room, User)
.join(User, on=Room.owner)
.where(User.sessid==sessid)
.switch(Room)
.join(User, on=User.room))
It evaluates to:
SELECT "t1".*, "t2".* # skipped column names
FROM "room" AS t1
INNER JOIN "user" AS t2 ON ("t1"."owner_id" = "t2"."id")
INNER JOIN "user" AS t2 ON ("t1"."id" = "t2"."room_id")
WHERE ("t2"."sessid" = ?) [<sessid>]
and throws peewee.OperationalError: ambiguous column name: t2.id as t2 is defined twice.
What I actually need to do is:
room = (Room.select(Room)
.join(User.sessid=sessid)
.get())
users = room.users.execute()
But this is N+1 query and I'd like to resolve it in a single query like:
SELECT t1.*, t3.* FROM room AS t1
INNER JOIN user AS t2 ON t1.owner_id = t2.id
INNER JOIN user as t3 ON t3.room_id = t1.id
WHERE t2.sessid = ?;
Is there a peewee way of doing this or I need to enter this SQL query by hand?

You need to use a model alias when you are using the same table in two different contexts. So:
Owner = User.alias() # Create a model alias.
(Room.select(Room, Owner, User)
.join(Owner, on=(Room.owner == Owner.id))
.where(Owner.sessid == sessid)
.switch(Room)
.join(User, on=User.room))
http://docs.peewee-orm.com/en/latest/peewee/api.html?#Model.alias

Related

how to write a Oracle view with this query?

I am using oracle 11g. I want to query with 3 queries.
First one, it is the main. Select to find condition.
Select role, name from tablerole where name is not null and ID=#param1;
This query will return #role with 2 level (admin and user)
The 2 other sub queries will base on this condition.
Up on the first query.
If role='admin' then select*from tablescreen where name is not null and ID=#param1;
If role='user' then select*from tablescreen where name='2';
#param1 is given when I call this view or when I using.
I consider it will be view or function or procedure best for this query.
How to write this query that can accept an input parameter into the query for looping source and return output where matched condition.
Thanks for your kindness.
Nam hmoob.
As far as I understood the question, that would be a view that consists of two select statements:
one that uses condition for admin
another one for user
The first query you posted will be used in a join with the tablescreen table. Something like this:
create or replace view v_role as
-- select for ADMIN
select r.id, s.whatever
from tablescreen s join tablerole r on r.id = s.id
where s.name is not null
and r.role = 'admin'
union all
-- select for USER
select r.id, s.whatever
from tablescreen s join tablerole r on r.id = s.id
where s.name = '2'
and r.name = 'user';
Or, as Zynon suggested, without union:
select r.id, s.whatever
from tablescreen s join tablerole r on r.id = s.id
where ( s.name is not null
and r.role = 'admin'
)
or ( s.name = '2'
and r.name = 'user'
);
You'd then use it as
select * from v_role v where v.id = &param1;

Rewriting query with table join containing GROUP BY clause

Is it possible to rewrite the following query
SELECT CT.GROUP, CT.EMP_ID, HT.EFF_DT
FROM CURR_TABLE CT
JOIN (SELECT GROUP, EMP_ID, MAX(EFF_DT) AS EFF_DT
FROM HIST_TABLE
WHERE STAT = 'A'
GROUP BY GROUP, EMP_ID) HT ON CT.GROUP = HT.GROUP AND
CT.EMPID = HT.EMP_ID
WHERE CT.GROUP = :1
AND CT.EMP_ID = :2
in a way that is similar to CROSS JOIN style?
SELECT table1.column1, table2.column2...
FROM table1, table2 [, table3 ]
The reason is that I want to create such query in Peoplesoft, and the above can only be achieved by creating a separate view for the selection with the group by clause. I want to do this just in one query without creating additional views.
You may try writing your query as a single level join with an aggregation:
SELECT
CT.GROUP,
CT.EMP_ID,
MAX(HT.EFF_DT) AS EFF_DT
FROM CURR_TABLE CT
LEFT JOIN HIST_TABLE HT
ON CT.GROUP = HT.GROUP AND
CT.EMPID = HT.EMP_ID AND
HT.STAT = 'A'
WHERE
CT.GROUP = :1 AND
CT.EMP_ID = :2
GROUP BY
CT.GROUP,
CT.EMP_ID;
Note that GROUP is a reserved SQL keyword, and you might have to escape it with double quotes to make this query (or the one in your question) work on Oracle.

Oracle - Aggregate CLOB within PIVOT

I have large user data containing their location permissions and groups of these permissions. I need to do a report containing only 1 row for every user.
There are so many permissions per one user in one group of them, I need to use my own aggregate function LISTAGG_CLOB which makes aggregate list but returns CLOB (more than 4000 characters).
This query gives me the list of all users with their permissions but it makes one row for every group of them.
select u.NAME,
lpg.NAME,
LISTAGG_CLOB(l.NAME)
from USERS u
left join LOCATION_PERM lp
on lp.USER_ID = u.ID
left join LOCATION l
on l.ID = lp.LOCATION
left join LOCATION_PERM_GROUP lpg
on lpg.ID = lp.GROUP
group by u.NAME, lpg.NAME
I tried to pivot these data but I can't get it to work, because Oracle doesn't recognize my own aggregate function as aggregate function and none aggregate funcitons (besides LISTAGG which is too small) is meant to aggregate strings.
Samples of what I tried are:
1)
select * from (
select u.NAME as USER,
lpg.NAME as PERM_GROUP,
LISTAGG_CLOB(l.NAME) as PERMISSIONS
from USERS u
left join LOCATION_PERM lp
on lp.USER_ID = u.ID
left join LOCATION l
on l.ID = lp.LOCATION
left join LOCATION_PERM_GROUP lpg
on lpg.ID = lp.GROUP
group by u.NAME, lpg.NAME)
pivot (LISTAGG(PERMISSIONS) within group (order by PERMISSIONS) for PERM_GROUP in ('Global', 'Orders', 'Admin') )
But it produces
Buffer too small for CLOB to CHAR or BLOB to RAW conversion
error
2)
select * from (
select u.NAME as USER,
lpg.NAME as PERM_GROUP,
LISTAGG_CLOB(l.NAME) as PERMISSIONS
from USERS u
left join LOCATION_PERM lp
on lp.USER_ID = u.ID
left join LOCATION l
on l.ID = lp.LOCATION
left join LOCATION_PERM_GROUP lpg
on lpg.ID = lp.GROUP
group by u.NAME, lpg.NAME)
pivot (DISTINCT(PERMISSIONS) for PERM_GROUP in ('Global', 'Orders', 'Admin') )
But it says "missing expression" so I guess I can't use DISTINCT (or UNIQUE) keyword in place of aggregate function.
I also tried MAX and other aggregate functions but they accept only numbers as input.
Any suggestions how to pivot these data?

Linq left outer group by, then left outer the group

I've this query that i'm trying to put as linq:
select *
from stuff
inner join stuffowner so on so.stuffID = stuff.stuffID
left outer join (select min(loanId) as loanId, stuffownerId from loan
where userid = 1 and status <> 2 group by stuffownerId) t on t.stuffownerid = so.stuffownerid
left outer join loan on t.LoanId = loan.LoanId
when this is done, I would like to do a linq Group by to have Stuff has key, then stuffowners + Loan as value.
I can't seem to get to a nice query without sub query (hence the double left outer).
So basically what my query does, is for each stuff I've in my database, bring the owners, and then i want to bring the first loan a user has made on that stuff.
I've tried various linq:
from stuff in Stuffs
join so in StuffOwners on stuff.StuffId equals so.StuffId
join tLoan in Loans on so.StuffOwnerId equals tLoan.StuffOwnerId into tmpJoin
from tTmpJoin in tmpJoin.DefaultIfEmpty()
group tTmpJoin by new {stuff} into grouped
select new {grouped, fluk = (int?)grouped.Max(w=> w.Status )}
This is not good because if I don't get stuff owner and on top of that it seems to generate a lot of queries (LinqPad)
from stuff in Stuffs
join so in StuffOwners on stuff.StuffId equals so.StuffId
join tmpLoan in
(from tLoan in Loans group tLoan by tLoan.StuffOwnerId into g
select new {StuffOwnerId = g.Key, loanid = (from t2 in g select t2.LoanId).Max()})
on so.StuffOwnerId equals tmpLoan.StuffOwnerId
into tmptmp from tMaxLoan in tmptmp.DefaultIfEmpty()
select new {stuff, so, tmptmp}
Seems to generate a lot of subqueries as well.
I've tried the let keyworkd with:
from tstuffOwner in StuffOwners
let tloan = Loans.Where(p2 => tstuffOwner.StuffOwnerId == p2.StuffOwnerId).FirstOrDefault()
select new { qsdq = tstuffOwner, qsdsq= (int?) tloan.Status, kwk= (int?) tloan.UserId, kiwk= tloan.ReturnDate }
but the more info i get from tLoan, the longer the query gets with more subqueries
What would be the best way to achieve this?
Thanks

LINQ vb.net GROUP BY - SELECT JOINED column

I have a very basic SQL query I am trying to reproduce:
SELECT t1.id, t1.name, count(*), min(t2.inserted) as inserted_first, max(t2.inserted) as inserted_last
FROM tbl1 t1
LEFT JOIN tbl2 t2 ON t1.id=t2.tbl1_id
WHERE t2.search=15
GROUP BY t1.id, t1.name
This works perfect for me. It allows me to group by the unique items of t1.id and t1.name, but also get the amount of times this pair appears, and also the min and max value of t2.inserted for the linked table. Problem is, now when I turn this into LINQ I get:
Dim query =
From t1 In ent.TBL1
Join t2 In ent.TBL2 On t1.id Equals t2.tbl1_id
Where (t2.search=15)
Group t1 By t1.id, t1.name Into Group
Select New With {
.id = id,
.name = name,
.count = Group.Count,
.min_value = ???,
.max_Value = ???
}
I am lost as to what i can do to select the min and max. Group.Min would work if it was of the same table as the grouping, however since it is in t2, I am not able to reference it. Also I can not add it to my group by, since its different.
Note that tbl2 links to tbl1 on tbl2.tbl1_id -> tbl1.id. Also this is a dumbed down example of my problem, not the real live schema.
I appreciate any help on this matter
I'm not a VB person, but I think you want:
Group t1, t2 By t1.id, t1.name Into Group
Select New With {
.id = id,
.name = name,
.count = Group.Count,
.min_value = Group.Min(Function(x) x.t2.Inserted),
.max_Value = Group.Max(Function(x) x.t2.Inserted)
}
Note how here each group element contains both t1 and t2, which is how you're able to get to the Inserted property. It's not actually clear whether you even need t1 or any other parts of t2, so you may be able to use:
Group t2.Inserted By t1.id, t1.name Into Group
Select New With {
.id = id,
.name = name,
.count = Group.Count,
.min_value = Group.Min(),
.max_Value = Group.Max()
}
Unfortunately my VB LINQ skills aren't up to the challenge of knowing whether that's right for you. If you'd be happy with it in C# (which you could then convert) I could help you more easily...

Resources