Appending field from sub-query - sql

I have a rather complex query that orders the results based on a sub-query. This works fine. The query is below.
SELECT enrollments.*, users.*
FROM enrollments
INNER JOIN users
ON enrollments.user_id = users.id
WHERE enrollments.preview = FALSE
ORDER BY (
SELECT COUNT(progress_tracker)
FROM progress_tracker
WHERE progress_tracker.enrollment_id = enrollments.id
AND progress_tracker.completed = TRUE
)
Here I'm selecting the fields from the User and Enrollment table. I ideally want one more field in the sql results. This field represents the results from the ORDER BY sql:
SELECT COUNT(progress_tracker)
FROM progress_tracker
WHERE progress_tracker.enrollment_id = enrollments.id
AND progress_tracker.completed = TRUE
Is it possible to create a field from this query and append it to the results for each row? The name of the row would be appropriately termed as enrollment_progress_tracker_completed_count. If so, how can I do this?

Add the subquery to the select and then use it for ordering:
SELECT e.*, u.*,
(SELECT COUNT(*)
FROM progress_tracker pt
WHERE pt.enrollment_id = e.id AND pt.completed = TRUE
) as newcol
FROM enrollments e INNER JOIN
users u
ON e.user_id = u.id
WHERE e.preview = FALSE
ORDER BY newcol;

Use a temporary table
with tempCounts as(
SELECT enrollment_id, COUNT(progress_tracker) as countValue
FROM progress_tracker
WHERE progress_tracker.completed = TRUE
group by enrollment_id)
SELECT enrollments.*, users.*,tempCounts.countValue
FROM enrollments, users.*,tempCounts
INNER JOIN users
ON enrollments.user_id = users.id
WHERE enrollments.preview = FALSE
and enrollments.id = tempCounts.enrollment_id
ORDER BY tempCounts.countValue

Related

subquery must return only one column error occurs when trying to run sql script

Im trying to set the "open jobs" column in user to the result of a select query
this is what i've tried but and error occurs
UPDATE
"user"
SET
open_jobs = (
SELECT
u."_id_",
COUNT(*) FILTER (WHERE jc.status IN ('Pending', 'Work Started')) AS open_cnt
FROM
job_card jc
LEFT JOIN "user" u ON u."_id_" = jc.technicians_fk
GROUP BY
u."_id_")
I would suggest removing the FILTER entirely and you want a correlation clause instead of a JOIN:
update "user" u
set open_jobs = (SELECT COUNT(*) AS open_cnt
FROM job_card jc
WHERE u."_id_" = jc.technicians_fk AND
jc.status IN ('Pending', 'Work Started')
);
The WHERE clause may be more efficient. If there are no matches, the COUNT(*) returns 0.
Your version of the subquery is almost what you want . . . if you move it to a FROM clause:
UPDATE "user" u
SET open_jobs = COALESCE(jc.cnt)
FROM (SELECT u."_id_", COUNT(*) as cnt FILTER (WHERE jc.status IN ('Pending', 'Work Started')) AS open_cnt
FROM "user" u LEFT JOIN
job_card jc
ON u."_id_" = jc.technicians_fk
GROUP BY u."_id_"
) jc
WHERE jc."_id_" = u."_id_";
One important difference is that "user" is the first table for the LEFT JOIN, to ensure that you get all users.
Your subquery has multiple columns and giving multiple rows. Use the following query:
UPDATE "user" SET OPEN_JOBS =
( SELECT COUNT(*) FILTER ( -- removed the u."_id_"
WHERE JC.STATUS IN (
'Pending',
'Work Started'
) ) AS OPEN_CNT
FROM JOB_CARD JC -- user table is not required here
WHERE U."_id_" = JC.TECHNICIANS_FK) -- this will make sure that your query is correlated

How To use Where instead of Group by?

I wrote a query , that gives me this Output :
(This is Just a sample obviously the Output Table contains 300000 rows approximatly)
And This is my Query :
proc sql;
create Table Output as
select ID_User, Division_ID, sum(conta) as Tot_Items, max(Counts) as Max_Item
from (select c.ID_User , c.Div_ID as Division_ID, ro.code as Mat, count(*) as Counts
from Ods.R_Ordini o
inner join DMC.Cust_Dupl c
on User_ID = ID_User
inner join ods.R_Nlines ro
on ro.Orders_Id = o.Id_Orders AND RO.SERVICE = 0
inner join ods.R_Mat m
on ro.Mat_Id = Id_Mat and flag = 0
group by
ID_User,
C.Division_ID,
Ro.Code
Having Counts > 1
)
group by
Id_User,
Division_ID
Order by
Tot_Item DESC
;
quit;
So , What i want is to re-write this Query , but instead of the Group by i want to use the Where Condition , (WHERE=(DIVISION_ID=3)) this is the condition.
I tried several attempts , with some i got errors , and with others i did got an output , but the output was not like the original one.
any help would be much appreciated , thank you.
The SAS data set option (where=(<where-expression>)) can only be coded adjacent to a data set name. So the option would have to be applied to the data set containing the column div_id that is the basis for computed column division_id. That would be table alias c
DMC.Cust_Dupl(where=(div_id=3)) as c
Or just use a normal SQL where clause
…
)
where division_id=3
group by …
Just use WHERE DIVISION_ID=3 before group by.
select ID_User, Division_ID, sum(conta) as Tot_Items, max(Counts) as Max_Item from (select c.ID_User , c.Div_ID as Division_ID, ro.code as Mat, count(*) as Counts from Ods.R_Ordini o inner join DMC.Cust_Dupl c on User_ID = ID_User inner join ods.R_Nlines ro on ro.Orders_Id = o.Id_Orders AND RO.SERVICE = 0 inner join ods.R_Mat m on ro.Mat_Id = Id_Mat and flag = 0 WHERE DIVISION_ID=3 group by ID_User, C.Division_ID, Ro.Code Having Counts > 1 ) group by Id_User, Division_ID Order by Tot_Item DESC

Set boolean value to FALSE for each row in query with 'HAVING'?

(Sorry about the phrasing of the title.)
I have the following SQL query that retrieves all the rows I want:
SELECT
app_activity.name
FROM
app_chatmessage
JOIN
app_activity ON app_chatmessage.activity_id = app_activity.id
GROUP BY
app_activity.name
HAVING
COUNT(app_chatmessage.owner_id) = 1;
Now, the table app_chatmessage also has a column seen. I'd like to set this column to FALSE for all the rows returned by the aforementioned query. How do I do that?
Try this
Update app_chatmessage set seen='FALSE' where
activity_id in
(
SELECT
app_activity.id
FROM
app_chatmessage
JOIN
app_activity ON app_chatmessage.activity_id = app_activity.id
GROUP BY
app_activity.name
HAVING
COUNT(app_chatmessage.owner_id) = 1
);
try below one
WITH CTE AS ( SELECT
app_activity.name, SEEN, COUNT(app_chatmessage.owner_id) over(partition by app_activity.name order by app_activity.name) CNT
FROM
app_chatmessage
JOIN
app_activity ON app_chatmessage.activity_id = app_activity.id
)
UPDATE CTE
SET SEEN = FALSE
WHERE CNT =1

JPQL query - order by count

I need to implement a named query, that returns a list of events sorted by number of friends who go to the event.
For example, this query return a list of events sorted by number of all people who go to event:
SELECT r.event
FROM RSVP AS r
JOIN r.event.rsvpList rr
WHERE r.profile.user.uuid = :userUuid
AND r.rsvpStatus = RSVPStatus.ATTENDING
GROUP BY r.event
ORDER BY COUNT(rr) DESC
Or other example, list of friends events:
SELECT r.event
FROM RSVP AS r
WHERE r.profile IN
( SELECT f.profileTo
FROM Friend f
WHERE f.profileFrom.user.uuid = :userUuid
)
AND r.rsvpStatus = :rsvpStatus
GROUP BY r.event
I tried to do the following:
SELECT r.event
FROM RSVP AS r
JOIN r.event.rsvpList rr
WHERE r.profile.user.uuid = :userUuid
AND r.rsvpStatus = RSVPStatus.ATTENDING
AND rr.profile IN
(
SELECT f.profileTo
FROM Friend f
WHERE f.profileFrom.user.uuid = :userUuid
)
GROUP BY r.event
ORDER BY COUNT(rr) DESC
But this query returned the events that go on friends and skip other events.
This is a simplified diagram of my DB:
You didn't post your entities so i could not try, but i think this should work:
SELECT r.event, (
SELECT count(*)
FROM RSVP rr
WHERE rr.event = r.event
AND rr.profile IN
(
SELECT f.profileTo
FROM Friend f
WHERE f.profileFrom.user.uuid = :userUuid
)
) count
FROM RSVP AS r
WHERE r.profile.user.uuid = :userUuid
AND r.rsvpStatus = RSVPStatus.ATTENDING
ORDER BY 2 DESC
The thing here is HQL does not support subqueries in the ORDER BY clause, so you have to select one more column just for ordering.

Limit join to one row

I have the following query:
SELECT sum((select count(*) as itemCount) * "SalesOrderItems"."price") as amount, 'rma' as
"creditType", "Clients"."company" as "client", "Clients".id as "ClientId", "Rmas".*
FROM "Rmas" JOIN "EsnsRmas" on("EsnsRmas"."RmaId" = "Rmas"."id")
JOIN "Esns" on ("Esns".id = "EsnsRmas"."EsnId")
JOIN "EsnsSalesOrderItems" on("EsnsSalesOrderItems"."EsnId" = "Esns"."id" )
JOIN "SalesOrderItems" on("SalesOrderItems"."id" = "EsnsSalesOrderItems"."SalesOrderItemId")
JOIN "Clients" on("Clients"."id" = "Rmas"."ClientId" )
WHERE "Rmas"."credited"=false AND "Rmas"."verifyStatus" IS NOT null
GROUP BY "Clients".id, "Rmas".id;
The problem is that the table "EsnsSalesOrderItems" can have the same EsnId in different entries. I want to restrict the query to only pull the last entry in "EsnsSalesOrderItems" that has the same "EsnId".
By "last" entry I mean the following:
The one that appears last in the table "EsnsSalesOrderItems". So for example if "EsnsSalesOrderItems" has two entries with "EsnId" = 6 and "createdAt" = '2012-06-19' and '2012-07-19' respectively it should only give me the entry from '2012-07-19'.
SELECT (count(*) * sum(s."price")) AS amount
, 'rma' AS "creditType"
, c."company" AS "client"
, c.id AS "ClientId"
, r.*
FROM "Rmas" r
JOIN "EsnsRmas" er ON er."RmaId" = r."id"
JOIN "Esns" e ON e.id = er."EsnId"
JOIN (
SELECT DISTINCT ON ("EsnId") *
FROM "EsnsSalesOrderItems"
ORDER BY "EsnId", "createdAt" DESC
) es ON es."EsnId" = e."id"
JOIN "SalesOrderItems" s ON s."id" = es."SalesOrderItemId"
JOIN "Clients" c ON c."id" = r."ClientId"
WHERE r."credited" = FALSE
AND r."verifyStatus" IS NOT NULL
GROUP BY c.id, r.id;
Your query in the question has an illegal aggregate over another aggregate:
sum((select count(*) as itemCount) * "SalesOrderItems"."price") as amount
Simplified and converted to legal syntax:
(count(*) * sum(s."price")) AS amount
But do you really want to multiply with the count per group?
I retrieve the the single row per group in "EsnsSalesOrderItems" with DISTINCT ON. Detailed explanation:
Select first row in each GROUP BY group?
I also added table aliases and formatting to make the query easier to parse for human eyes. If you could avoid camel case you could get rid of all the double quotes clouding the view.
Something like:
join (
select "EsnId",
row_number() over (partition by "EsnId" order by "createdAt" desc) as rn
from "EsnsSalesOrderItems"
) t ON t."EsnId" = "Esns"."id" and rn = 1
this will select the latest "EsnId" from "EsnsSalesOrderItems" based on the column creation_date. As you didn't post the structure of your tables, I had to "invent" a column name. You can use any column that allows you to define an order on the rows that suits you.
But remember the concept of the "last row" is only valid if you specifiy an order or the rows. A table as such is not ordered, nor is the result of a query unless you specify an order by
Necromancing because the answers are outdated.
Take advantage of the LATERAL keyword introduced in PG 9.3
left | right | inner JOIN LATERAL
I'll explain with an example:
Assuming you have a table "Contacts".
Now contacts have organisational units.
They can have one OU at a point in time, but N OUs at N points in time.
Now, if you have to query contacts and OU in a time period (not a reporting date, but a date range), you could N-fold increase the record count if you just did a left join.
So, to display the OU, you need to just join the first OU for each contact (where what shall be first is an arbitrary criterion - when taking the last value, for example, that is just another way of saying the first value when sorted by descending date order).
In SQL-server, you would use cross-apply (or rather OUTER APPLY since we need a left join), which will invoke a table-valued function on each row it has to join.
SELECT * FROM T_Contacts
--LEFT JOIN T_MAP_Contacts_Ref_OrganisationalUnit ON MAP_CTCOU_CT_UID = T_Contacts.CT_UID AND MAP_CTCOU_SoftDeleteStatus = 1
--WHERE T_MAP_Contacts_Ref_OrganisationalUnit.MAP_CTCOU_UID IS NULL -- 989
-- CROSS APPLY -- = INNER JOIN
OUTER APPLY -- = LEFT JOIN
(
SELECT TOP 1
--MAP_CTCOU_UID
MAP_CTCOU_CT_UID
,MAP_CTCOU_COU_UID
,MAP_CTCOU_DateFrom
,MAP_CTCOU_DateTo
FROM T_MAP_Contacts_Ref_OrganisationalUnit
WHERE MAP_CTCOU_SoftDeleteStatus = 1
AND MAP_CTCOU_CT_UID = T_Contacts.CT_UID
/*
AND
(
(#in_DateFrom <= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateTo)
AND
(#in_DateTo >= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateFrom)
)
*/
ORDER BY MAP_CTCOU_DateFrom
) AS FirstOE
In PostgreSQL, starting from version 9.3, you can do that, too - just use the LATERAL keyword to achieve the same:
SELECT * FROM T_Contacts
--LEFT JOIN T_MAP_Contacts_Ref_OrganisationalUnit ON MAP_CTCOU_CT_UID = T_Contacts.CT_UID AND MAP_CTCOU_SoftDeleteStatus = 1
--WHERE T_MAP_Contacts_Ref_OrganisationalUnit.MAP_CTCOU_UID IS NULL -- 989
LEFT JOIN LATERAL
(
SELECT
--MAP_CTCOU_UID
MAP_CTCOU_CT_UID
,MAP_CTCOU_COU_UID
,MAP_CTCOU_DateFrom
,MAP_CTCOU_DateTo
FROM T_MAP_Contacts_Ref_OrganisationalUnit
WHERE MAP_CTCOU_SoftDeleteStatus = 1
AND MAP_CTCOU_CT_UID = T_Contacts.CT_UID
/*
AND
(
(__in_DateFrom <= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateTo)
AND
(__in_DateTo >= T_MAP_Contacts_Ref_OrganisationalUnit.MAP_KTKOE_DateFrom)
)
*/
ORDER BY MAP_CTCOU_DateFrom
LIMIT 1
) AS FirstOE
Try using a subquery in your ON clause. An abstract example:
SELECT
*
FROM table1
JOIN table2 ON table2.id = (
SELECT id FROM table2 WHERE table2.table1_id = table1.id LIMIT 1
)
WHERE
...