SQL select specific rows from subquery and insert into row_to_json - sql

I have a view for which I have created several subqueries, I'm trying to return one of the subqueries as json using -
row_to_json(err.*) as overall_totals
The subquery -
left join
(
SELECT q.id, SUM(q.total) AS total,
jsonb_agg(jsonb_build_object('count', q.total, 'type', q.name)) AS totals
FROM (
SELECT r.id AS id, e.name, COUNT(de.value_id) AS total
FROM table_c de
JOIN tests.error e
ON e.id = de.value_id
JOIN table_a p
ON de.process_id = p.id
Join table_b r on p.root = r.id
GROUP BY e.name, r.id ) q
GROUP BY q.id
) err on err.id = rs.id
This works fine and returns as json, however, I only want to return total and totals of the "err" sub-query and not q.id. I need the q.id in the sub-query so that I can join the query onto the rest of the view but I don't want it to be stored in overall_totals. How can I get around this? Is it possible to select certain values into row_to_json?

If you are fine with returning jsonb instead of json, then you can use the - operator:
to_jsonb(err.*) - 'id' as overall_totals

You can add more subquery, something like this
SELECT row_to_json(err2.*) as overall_totals
FROM
(
SELECT err.total, err.totals
FROM XXX_TABLE RS
left join
(
SELECT q.id, SUM(q.total) AS total, jsonb_agg(jsonb_build_object('count', q.total, 'type', q.name)) AS totals
FROM
(
SELECT r.id AS id, e.name, COUNT(de.value_id) AS total
FROM table_c de
JOIN tests.error e
ON e.id = de.value_id
JOIN table_a p
ON de.process_id = p.id
Join table_b r on p.root = r.id
GROUP BY e.name, r.id
) q
GROUP BY q.id
) err on err.id = rs.id
) err2

Related

How to put a CTE as a subquery

In SQL Server, I have a CTE and a select statement.
The CTE looks like
WITH Recursion AS (
SELECT Value, GroupID, Type
FROM #GroupMembership
WHERE GroupID = #requestingGroupID
UNION ALL
SELECT M.Value, M.GroupID, M.Type
FROM Recursion AS R
INNER JOIN #GroupMembership AS M ON R.Value = M.GroupID AND R.Type = 'Group'
)
SELECT * FROM Recursion
and the select statement is this
select *
from [Right] r
inner join Group on r.groupid = Group.id
inner join (
-- CTE but #requestingGroupID should be Group.id
) C on C.GroupID = Group.id
I want to nest the CTE inside the brackets, but the #requestingGroupID from the CTE should be referencing Group.id from outside the bracket.
When I try to do this, I get syntax errors. Does anyone know how to do it?
Thanks
Without sample data and expected results, it's hard to give a definitive answer.
But basically, you cannot use a recursive CTE as an APPLY (where you push outer references in).
The only way to do this is to put the recursive part into an inline Table Valued Function, then APPLY it to the rest of the query
CREATE OR ALTER FUNCTION GetDescendants (#requestingGroupID int)
RETURNS TABLE AS RETURN
WITH Recursion AS (
SELECT Value, GroupID, Type
FROM GroupMembership
WHERE GroupID = #requestingGroupID
UNION ALL
SELECT M.Value, M.GroupID, M.Type
FROM Recursion AS R
INNER JOIN GroupMembership AS M ON R.Value = M.GroupID AND R.Type = 'Group'
)
SELECT *
FROM Recursion;
select *
from [Right] r
inner join Group g on r.groupid = g.id
CROSS APPLY dbo.GetDescendant ( g.id) C
db<>fiddle
Alternatively, you can re-arrange your query so that the main SELECT is in the anchor part (first part) of the recursion. Admittedly this is not always possible.
WITH Recursion AS (
SELECT r.Id, Value, GroupID, Type
FROM [Right] r
inner join Group g on r.groupid = g.id
JOIN #GroupMembership gm ON gm.GroupID = g.GroupID
UNION ALL
SELECT R.Id, M.Value, M.GroupID, M.Type
FROM Recursion AS R
INNER JOIN #GroupMembership AS M ON R.Value = M.GroupID AND R.Type = 'Group'
)
SELECT *
FROM Recursion
db<>fiddle

How to get distinct id in subquery from fact table

Trying to return all the fields in my sub query minus the duplicated childid's in the fact table cor.score. I don't want the duplicate id's to inflate my count. Every id needs to be counted once.
select distinct cs.childid
from
(select s.sitename, c.primarylanguage, count(Primarylanguage) as 'Count'
from cor.scores cs
left join cor.sites s on s.id = cs.siteid
left join cor.children c on c.id = cs.childid
group by s.sitename, c.primarylanguage)
Error:
Msg 102, Level 15, State 1, Line 7 Incorrect syntax near ')'.
What's the best way to go about this?
select distinct cs_childid
from
(
select s.sitename,cs.childid as cs_childid, c.primarylanguage, count(Primarylanguage) as 'Count'
from cor.scores cs
left join cor.sites s on s.id = cs.siteid
left join cor.children c on c.id = cs.childid
group by s.sitename, c.primarylanguage,cs.childid
)as T
Do you want this:
1. can return any column of subquery
select s.childid,s.[Count]
FROM (
SELECT s.sitename, c.primarylanguage,cs.childid, count(Primarylanguage)OVER(PARTITION BY s.sitename) as 'Count'
FROM cor.scores cs
LEFT join cor.sites s on s.id = cs.siteid
) AS s LEFT join cor.children c on c.id = s.childid
Or this:
2. Only can return group column and aggregate columns
select s.childid,s.[Count]
FROM (
SELECT s.sitename,cs.childid, count(Primarylanguage)OVER(PARTITION BY s.sitename) as 'Count'
FROM cor.scores cs
LEFT join cor.sites s on s.id = cs.siteid
GROUP BY s.sitename,cs.childid
) AS s LEFT join cor.children c on c.id = s.childid

how to join a table on a subquery that uses order by and limit

For each row from table tClass matching a given where clause,
join on the first row in tEv, sorted by time, where tEv.class_id = tClass.class_id
The following code throws the error
ORA-01799: a column may not be outer-joined to a subquery
select
c.class_id,
c.class_name,
e.start_time,
e.ev_id
from
tClass c
left join tEv e on (
e.ev_id = (
select
ss1.ev_id
from (
select
ed.ev_id
from
tEvDisp ed,
tEv e
where
ed.class_id = c.class_id
and ed.viewable = 'Y'
and ed.display_until > localtimestamp
and e.ev_id = ed.ev_id
order by
e.start_time
) ss1
where
rownum = 1
)
)
where
c.is_matching = 'Y';
How can this be rewritten to do what is described?
The above is for oracle, but needs to work in sqlite (substituting where necessary)
No idea about SQLite - that would need to be a separate question if this doesn't work - but for Oracle you could do something like this:
select c.class_id,
c.class_name,
e.start_time,
e.ev_id
from tClass c
left join (
select class_id, ev_id, start_time
from (
select ed.class_id,
ed.ev_id,
e.start_time,
row_number() over (partition by ed.class_id order by e.start_time) as rn
from tEvDisp ed
join tEv e on e.ev_id = ed.ev_id
where ed.viewable = 'Y'
and ed.display_until > localtimestamp
)
where rn = 1
) e on e.class_id = c.class_id
where c.is_matching = 'Y';
This uses a subquery which finds the most tEv data, using an analytic row_number() to identify the latest data for each class_id, which is restricted by the rn = 1 filter.
That subquery, consisting of at most one row per class_id, is then used the left outer join against tClass.
This sort of construct should get you what you need. You can fix the details.
select c.classid
, c.classname
, temp.maxstarttime
from tClass c left join (
select c.classid id
max(e.start_time) maxstarttime
from tClass join tEv on tEv.classId = tClass.ClassId
where whatever
group by c.classid) temp on c.classid = temp.id

JOIN / LEFT JOIN conflict in SQL Server

I have a tricky query. I need to select all recent versions of 2 types of members of administrator groups. Here is the query:
SELECT refGroup.*
FROM tblSystemAdministratorGroups refGroup
JOIN tblGroup refMem ON refGroup.AttributeValue = refMem.ObjectUID
This query will return all the administrator groups. The next step will be getting the members of these groups. Since I have 2 types of memberships (Explicit, Computed), I will have to use a LEFT JOIN to make sure that I am not excluding any rows.
SELECT refGroup.*
FROM tblSystemAdministratorGroups refGroup
-- The JOIN bellow can be excluded but it is here just to clarify the architecture
JOIN tblGroup refMem ON refGroup.AttributeValue = refMem.ObjectUID
LEFT JOIN tblGroup_ComputedMember cm ON refMem.ObjectUID = cm.GroupObjectID
LEFT JOIN tblGroup_ExplicitMember em ON refMem.ObjectUID = em.GroupObjectID
The last piece in the puzzle is to get the latest version of each member. For that I will have to use JOIN to exclude older versions:
JOIN (
SELECT MAX([ID]) MaxId
FROM [OmadaReporting].[dbo].tblGroup_ComputedMember
GROUP BY ObjectID
) MostRecentCM ON MostRecentCM.MaxId = cm.Id
and
JOIN (
SELECT MAX([ID]) MaxId
FROM [OmadaReporting].[dbo].tblGroup_ExplicitMember
GROUP BY ObjectID
) MostRecentEM ON MostRecentEM.MaxId = em.Id
The full query will be:
SELECT refGroup.*
FROM tblSystemAdministratorGroups refGroup
JOIN tblGroup refMem ON refGroup.AttributeValue = refMem.ObjectUID
LEFT JOIN tblGroup_ComputedMember cm ON refMem.ObjectUID = cm.GroupObjectID
JOIN (
SELECT MAX([ID]) MaxId
FROM [OmadaReporting].[dbo].tblGroup_ComputedMember
GROUP BY ObjectID
) MostRecentCM ON MostRecentCM.MaxId = cm.Id
LEFT JOIN tblGroup_ExplicitMember em ON refMem.ObjectUID = em.GroupObjectID
JOIN (
SELECT MAX([ID]) MaxId
FROM [OmadaReporting].[dbo].tblGroup_ExplicitMember
GROUP BY ObjectID
) MostRecentEM ON MostRecentEM.MaxId = em.Id
The issue is clear: The 2 JOIN to exclude old versions are also applied to the select statement and clearly no rows are returned. What would be the best solution to escape such situation and to return the intended values?
SELECT refGroup.*
FROM tblSystemAdministratorGroups refGroup
JOIN tblGroup refMem ON refGroup.AttributeValue = refMem.ObjectUID
LEFT JOIN (
select GroupObjectID, ID, max(ID) over (partition by ObjectID) as maxID
from tblGroup_ComputedMember
) cm ON refMem.ObjectUID = cm.GroupObjectID and cm.ID = cm.maxID
LEFT JOIN (
select GroupObjectID, ID, max(ID) over (partition by ObjectID) as maxID
from tblGroup_ExplicitMember
) em ON refMem.ObjectUID = em.GroupObjectID and em.ID = em.maxID
where cm.ID = cm.MaxID
What about using LEFT join in your last two joins?
LEFT JOIN (
SELECT MAX([ID]) MaxId
FROM [OmadaReporting].[dbo].tblGroup_ComputedMember
GROUP BY ObjectID
) MostRecentCM ON MostRecentCM.MaxId = cm.Id
And then in Where clause filter values as:
WHERE MostRecentCM.MaxId IS NOT NULL
OR
MostRecentEM.MaxId IS NOT NULL

Oracle SQL - Select from two tables

I have two tables which are exactly the same layout, but have different data (one is current, one is history).
How do I pull information from both tables?
PSUDO SQL:
SELECT
T.TRANS_QUAN,
P.PONO, D.ID
FROM
TRANSLOG T, (select * from PO, PO_HIST) P, (SELECT * FROM PO_DETAIL, PO_DETAIL_HIST) D
WHERE
D.PO_ID = P.ID
AND T.SOURCE_ID = D.ID
When I try to actually run that I get "column ambiguously defined"
You can UNION clause.
Try:
SELECT
T.TRANS_QUAN,
P.PONO, D.ID
FROM
TRANSLOG T,
(
SELECT * FROM PO
UNION
SELECT * FROM PO_HIST
) P,
(
SELECT * FROM PO_DETAIL
UNION
SELECT * FROM PO_DETAIL_HIST
) D
WHERE
D.PO_ID = P.ID
AND T.SOURCE_ID = D.ID
(select * from PO, PO_HIST) P
This subquery might make a cross join of tables that share a common column name.
Maybe you want:
Select t.trang_quan
, p.pono
, d.id
from translog t
inner join po_detail d
on d.id = t.source_id
cross join po_detail_hist
inner join po p
on p.id = d.po_id
cross join po_hist p2
Are you sure you want cross joins?