Count left join and use it in where - sql

I currently have the following sql (Generated by typeorm).
SELECT
"media"."permissionOwner" AS "media_permissionOwner",
"media"."permissionGroup" AS "media_permissionGroup",
"media"."permissionOther" AS "media_permissionOther",
"media"."id" AS "media_id",
"media"."filename" AS "media_filename",
"media"."mime" AS "media_mime",
"media"."description" AS "media_description",
"media"."length" AS "media_length",
(
SELECT
1
FROM
"user" "user"
WHERE
"user"."id" = $1 LIMIT 1
)
AS "user"
FROM
"media_object" "media"
LEFT JOIN
"media_object_groups_group" "media_groups"
ON "media_groups"."mediaObjectId" = "media"."id"
LEFT JOIN
"group" "groups"
ON "groups"."id" = "media_groups"."groupId"
LEFT JOIN
"media_object_owner_user" "media_owner"
ON "media_owner"."mediaObjectId" = "media"."id"
LEFT JOIN
"user" "owner"
ON "owner"."id" = "media_owner"."userId"
WHERE
(
"media"."permissionGroup" >= $2
AND
COUNT("groups") = 0 -- How can I accomplish this
)
OR
(
"media"."permissionGroup" >= $3
AND groups # > user_groups
)
OR
(
owner # > ARRAY[user]
AND "media"."permissionOwner" >= $4
)
OR "media"."permissionOther" >= $5 -- PARAMETERS: [23,4,4,4,4]
The problem is, I can't use HAVING, because it doesn't seem like it support this type of condition making like WHERE does.
I tried it with a subquery at the COUNT part, but than I can't access the "groups" alias inside the sub select count query.
I am not even sure if the other part of the query works, but first I want to solve the "count" issue.

Related

Aggregate by another table after annotate

I have next annotate
qs.annotate(
goods_with_sales=Count('goods', filter=Q(goods__history__sales__gt=0)),
)
Same goods_with_sales_percent=goods_with_sales / sum_sales * 100
I need get percent of goods_with_sales to sum of all goods__history__sales. I try it with Window but not happiness...
This is raw sql query:
SELECT
"wb_brand"."id",
"wb_brand"."name",
COUNT("wb_good"."id") FILTER (
WHERE
"wb_stockshistory"."sales" > 0
) AS "goods_with_sales"
FROM
"wb_brand"
LEFT OUTER JOIN "wb_good" ON (
"wb_brand"."id" = "wb_good"."brand_id"
)
LEFT OUTER JOIN "wb_stockshistory" ON (
"wb_good"."id" = "wb_stockshistory"."good_id"
)
GROUP BY
"wb_brand"."id"
Also I tried this with CTE, but not happiness also.
How can I solve it with Django ORM (prefer) or with SQL ?
P.S. Also must be CASE/WHEN condition for divisizon by zero if sum_sales == 0.

How to show count_apply in field from count the data in PostgreSQL with Lumen

I want to ask again about PostgreSQL with framework Lumen. I have a table with implementation clone in the same table. For the difference I use parent_id for the clone row the true data reference on id. I count the data from another table but only counting the clone data not the true data.
Here is the image of result query:
Clone data have the count_apply but I want that count_apply also show in the true data. I want the result like this image:
How to query that's data? Here's my code.
select
"requisitions"."id",
"requisitions"."title",
"requisitions"."parent_id",
(select count(id) from requisition_users where requisition_id = requisitions.id) AS count_apply,
"requisitions"."status"
from
"requisitions"
inner join "users" on "requisitions"."created_by" = "users"."id"
inner join "companies" on "requisitions"."company_id" = "companies"."id"
-- inner join "requisition_users" on "requisitions"."id" = "requisition_users"."requisition_id"
where
"requisitions"."entity_id" = 1
-- and "requisitions"."parent_id" is not null
-- and "requisitions"."status" >= 5
-- and (select count(id) from requisition_users where requisition_id = requisitions.id) > 0
-- and "requisitions"."parent_id" = "requisitions"."id"
and "requisitions"."deleted_at" is null
Since I don't know the data structure of your tables I have write an outer query to get you the result you want. Please try this and let me know if you face any difficulties:
select
"id",
"title",
"parent_id",
(case when count_apply>0 then count_apply else max(count_apply)over(partition by "title") end)count_apply,
"status"
from
(select
"requisitions"."id",
"requisitions"."title",
"requisitions"."parent_id",
(select count(id) from requisition_users where requisition_id = requisitions.id) AS count_apply,
"requisitions"."status"
from
"requisitions"
inner join "users" on "requisitions"."created_by" = "users"."id"
inner join "companies" on "requisitions"."company_id" = "companies"."id"
-- inner join "requisition_users" on "requisitions"."id" = "requisition_users"."requisition_id"
where
"requisitions"."entity_id" = 1
-- and "requisitions"."parent_id" is not null
-- and "requisitions"."status" >= 5
-- and (select count(id) from requisition_users where requisition_id = requisitions.id) > 0
-- and "requisitions"."parent_id" = "requisitions"."id"
and "requisitions"."deleted_at" is null)t

Why can't I access a field defined as "Select 1" from a subquery in the outer query?

I have this subquery:
LEFT JOIN (SELECT 1 as exist
, MAX (ev.EventDate) as eventdate
, evt.EventCode
, CCaseID
FROM stg.Event ev
JOIN stg.EventTemplate evt
ON ev.EventTemplateID = evt.ID
WHERE evt.EventCode = 'UN002'
Group by CCaseID, evt.EventCode) as un002
ON un002.CCaseID = ev.CCaseID
WHERE evt.EventCode = 'UN001'
AND (un002.eventdate < ev.eventdate OR un002.eventdate IS NULL)
Group by ev.CCaseID, evt.EventCode) as un001
ON cc.ID = un001.CCaseID
I am now trying to access the exist field in the outer query as per un001.exist but SQL Server tells me that it is an invalid field. What am I missing?
un001 doesnt have exist that field belong to un002 subquery.
Also you have a GROUP BY and the and ON so there is some missing code there.
You should simplify the code and use CTE to make it easy to read and debug.
Something like this :
WITH un001 as ( SELECT ... ),
un002 as ( SELECT ...)
SELECT *
FROM un001
JOIN un002
ON un001 .CCaseID = un002.CCaseID

Symfony2 doctrine DQL Join - no results for some cases, but results with sql

I got verry strange problem with dql.
I have two tables connecter with foreign key:
== Table incident ==
id, cycle_nr, ...
1, 1
2, 3
== Table incident_active ==
id, incident_id, user_id, ...
1, 1, 1
...
I need to show active incidents for some cycles and everything is fine if I do mySQL query lilke this:
SELECT * FROM `incident_active` LEFT JOIN incident ON incident.id = `incident_active`.incident_id WHERE cycle_nr <= 2 and user_id = 1
Same query in DQL works too, but only for cycle_nr != 2
SELECT incidentActive, incident
FROM AccountingBundle:IncidentActive incidentActive
JOIN incidentActive.incident incident
WHERE incidentActive.company_id = 1 AND incident.cycle_nr <= 2
For cycle_nr <= 2 i get an empty result. I guess because of the abscense of incident for this cycle, but I'm asking <=2 and not == 2. Any Ideas?
The default join in DQL is INNER JOIN. Those queries are not equivalent.
SELECT * FROM `incident_active`
LEFT JOIN incident ON incident.id = `incident_active`.incident_id
WHERE cycle_nr <= 2 and user_id = 1
This SQL query is written in DQL like this (assuming the user is an entity)
SELECT incidentActive, incident
FROM AccountingBundle:IncidentActive incidentActive
LEFT JOIN incidentActive.incident incident
WHERE incident.cycle_nr <= :cycleNr
AND incidentActive.user = :userId
Also, when you have conditions just for the join and don't want it to affect the results from "main" table, you should add them to that join conditions only, like this.
SELECT incidentActive, incident
FROM AccountingBundle:IncidentActive incidentActive
LEFT JOIN incidentActive.incident incident WITH incident.cycle_nr <= :cycleNr
WHERE incidentActive.user = :userId
With bound parameters it should look like this, given the above query is in $dql.
$result = $em->createQuery($dql)
->setParameter('cycleNr', 2)
->setParameter('userId', 1)
->getResult();

Get "latest" row after GROUP BY over multiple tables

I'd preferably like to first query listed below and just group by stories.id, but I get the following error:
ERROR: column "u.first_name" must appear in the GROUP BY clause or be used in an aggregate function LINE 1: SELECT "s".*, "u"."first_name", "u"."last_name", ("i"."filen...
The second query works but does not group by stories.id and generates the wrong results. Is it possible to select from multiple tables and not group by all of them?
The table panels also has a column updated_at. I would like to get the newest file per story according to panels.updated_at.
SELECT
"s".*,
"u"."first_name",
"u"."last_name",
("i"."filename" || '.' || "i"."extension") AS "file"
FROM
"stories" "s"
LEFT JOIN "panels" "p" ON("p"."story_id" = "s"."id")
LEFT JOIN "users" "u" ON("s"."user_id" = "u"."uid")
LEFT JOIN "images" "i" ON ("p"."image_id" = "i"."id")
WHERE
"s"."complete" = false AND
"s"."created_by" = 205700489
GROUP BY
"s"."id",
ORDER BY
"s"."created_at" DESC
SELECT
"s".*,
"u"."first_name",
"u"."last_name",
("i"."filename" || '.' || "i"."extension") AS "file"
FROM
"stories" "s"
LEFT JOIN "panels" "p" ON("p"."story_id" = "s"."id")
LEFT JOIN "users" "u" ON("s"."user_id" = "u"."uid")
LEFT JOIN "images" "i" ON ("p"."image_id" = "i"."id")
WHERE
"s"."complete" = false AND
"s"."created_by" = 205700489
GROUP BY
"s"."id",
"u"."first_name",
"u"."last_name", "i"."filename",
"i"."extension"
ORDER BY
"s"."created_at" DESC
Updated after clarification of the question:
SELECT DISTINCT ON (s.created_at, s.id)
s.*
,u.first_name
,u.last_name
,concat_ws('.', i.filename, i.extension) AS file
FROM stories s
LEFT JOIN users u ON u.uid = s.user_id
LEFT JOIN panels p ON p.story_id = s.id
LEFT JOIN images i ON i.id = p.image_id
WHERE s.complete = false
AND s.created_by = 205700489
ORDER BY s.created_at DESC, s.id, p.updated_at DESC;
Grouping by primary key requires PostgreSQL 9.1.
I use concat_ws(), because I don't know which columns might be NULL. If both i.filename and i.extension are defined NOT NULL, you can simplify.
Effect of the additional ORDER BY item p.updated_at DESC is that the "newest" file will be picked per story. The query technique is explained in full under this related question:
Select first row in each GROUP BY group?
You can write something like:
SELECT
"s".*,
(SELECT "u"."first_name"
FROM "users" "u"
WHERE "s"."user_id" = "u"."uid"
LIMIT 1) ,
(SELECT "u"."last_name"
FROM "users" "u"
WHERE "s"."user_id" = "u"."uid"
LIMIT 1),
(SELECT "i"."filename" || '.' || "i"."extension"
FROM "panels" "p"
JOIN "images" "i" ON ("p"."image_id" = "i"."id")
WHERE "p"."story_id" = "s"."id"
LIMIT 1) AS "file"
FROM
"stories" "s"
WHERE
"s"."complete" = false AND
"s"."created_by" = 205700489
ORDER BY
"s"."created_at" DESC
It will get only 1 record from "users" and "panels" JOIN "images" per record in "stories" .
Add ORDER BY, extra WHERE or some aggregates to get what you need from "users" and "panels" JOIN "images"
UPD Also, you can use something like this:
SELECT *
FROM (
SELECT DISTINCT ON ("s"."id")
"s".*,
"u"."first_name",
"u"."last_name",
("i"."filename" || '.' || "i"."extension") AS "file"
FROM
"stories" "s"
LEFT JOIN "panels" "p" ON("p"."story_id" = "s"."id")
LEFT JOIN "users" "u" ON("s"."user_id" = "u"."uid")
LEFT JOIN "images" "i" ON ("p"."image_id" = "i"."id")
WHERE
"s"."complete" = false AND
"s"."created_by" = 205700489
ORDER BY
"s"."id"
) t ORDER BY "t"."created_at" DESC
It will leave only one row for every distinct "s"."id"