Django: How do I explicitly make a query with a HAVING clause? - sql

I need to execute some SQL that looks like this:
select approve_firm_id,approve_dt,approve_result
from main_approve
group by approve_firm_id
having MAX(approve_dt) and approve_result=0;
it runs (mysql-5.1),
but if I try in the Django model like this:
Approve.objects.annotate(max_dt=Max('approve_dt')).
filter(max_dt__gt=0).filter(approve_result=0).query
The query generated is this:
SELECT `main_approve`.`id`, `main_approve`.`approve_result`,
`main_approve`.`approve_dt`, `main_approve`.`approve_user_id`,
`main_approve`.`approve_firm_id`, `main_approve`.`exported_at`,
MAX(`main_approve`.`approve_dt`) AS `max_dt` FROM `main_approve`
WHERE (`main_approve`.`approve_result` = 0 )
GROUP BY `main_approve`.`id`
HAVING MAX(`main_approve`.`approve_dt`) > 0
ORDER BY NULL
I need the WHERE clause to be AFTER the GROUP BY clause.

Does the SQL even work? The having MAX(approve_dt) part certainly looks suspicious. Perhaps you mean this:
SELECT DISTINCT
main_approve.approve_firm_id,
main_approve.approve_dt,
main_approve.approve_result
FROM
main_approve
JOIN (
SELECT
approve_firm_id,
MAX(approve_dt) AS max_dt
FROM
main_approve
GROUP BY
approve_firm_id
) AS t
ON
main_approve.approve_firm_id = t.approve_firm_id
AND main_approve.approve_dt = t.max_dt
WHERE
main_approve.approve_result = 0;
It will be easier to construct the ORM expression after you know what exactly is the SQL going to be.

Related

Postgresql: Update column from select and add condition when multiple rows returned

Basically, I need to update a column using a SELECT, which can return more than one value. If that happens, I'd like to apply a second condition to determine which of those values is to be chosen:
UPDATE train
SET var1 = (
CASE
WHEN (SELECT COUNT(*)
FROM cars
WHERE (train.var2 LIKE cars.var2))
> 1)
THEN (
SELECT var1
FROM cars
WHERE (train.var2 LIKE cars.var2)
AND cars.var2 in (
SELECT var2
FROM cars
WHERE train.user_id = cars.user_id)
)
ELSE (
SELECT var1
FROM cars
WHERE (train.var2 LIKE cars.var2))
)
END
);
I think the above works, but I repeat 3 times the same SELECT. Do you have a nice way to avoid that? Maybe there is a simple way to catch when the select returns more than one value and do something about it?
Thank you
update train set
var1 = (
select cars.var1
from cars
where train.var2 like cars.var2
order by train.user_id = cars.user_id desc
limit 1);
The above answer is good and works out of the box. If you do a lot of these, take a look at: https://wiki.postgresql.org/wiki/First/last_(aggregate)
Then you can do this:
update train set
var1 = (
select first(cars.var1 order by train.user_id = cars.user_id desc)
from cars
where train.var2 like cars.var2
);
Depending on your exact use-case this may be neater, easier to read, easier to reason about (order by in subselect is full of nasty edge-cases) or just more faff than it's worth.

table alias not work in subquery in oracle

I am generating records with sum aggregate function and subquery, but the alias is not work there in inner query.
my query is
select UPP.item_total,
(select sum(INN.item_value_afs) total_item_value_afs from
(select distinct INN.reg_no,INN.tpt_cuo_nam,INN.item_total,INN.item_value_afs
from sigtasad.customs_import_data INN where INN.reg_no=UPP.reg_no and INN.tpt_cuo_nam=UPP.tpt_cuo_nam)) total_item_value,
sum(UPP.code_tax_amount), UPP.cmp_nam from SIGTASAD.CUSTOMS_IMPORT_DATA UPP where
UPP.reg_no='38699' and UPP.company_tin='9003247336' group by
UPP.reg_no,UPP.tpt_cuo_nam,UPP.cmp_nam,UPP.item_total ;
this query generate this error :
ORA-00904: "UPP"."TPT_CUO_NAM": invalid identifier
I want like this result!!!
Your query has numerous errors and bad habits. For instance:
You qualify a column name with an undefined table alias.
You are aggregating by columns not in the select.
You are using sum() on a subquery that has sum().
Based on the picture that you show, you probably want something like this:
select upp.item_total,
sum(item_value_afs) as total_item_value,
sum(upp.code_tax_amount),
upp.cmp_nam
from SIGTASAD.CUSTOMS_IMPORT_DATA upp
where upp.reg_no = '38699' and upp.company_tin = '9003247336'
group by upp.cmp_nam, upp.item_total ;
Or perhaps:
select upp.item_total,
sum(sum(item_value_afs)) over (partition by upp.cmp_nam, upp.item_total) as total_item_value,
sum(upp.code_tax_amount),
upp.cmp_nam
from SIGTASAD.CUSTOMS_IMPORT_DATA upp
where upp.reg_no = '38699' and upp.company_tin = '9003247336'
group by upp.cmp_nam, upp.item_total ;
Your innermost subquery
(select distinct nn.reg_no,inn.tpt_cuo_nam, inn.item_total, inn.item_value_afs
from sigtasad.customs_import_data inn
where inn.reg_no = upp.reg_no and inn.tpt_cuo_nam = upp.tpt_cuo_nam
)
references a table that is not joined (upp). It also does not have an alias but that problem would come later. Please note, that there also seems to be a type nn.reg_no instead of inn.reg_no
The structure of the tables is not displayed here but fixing the problem would mean something along the lines of:
(select distinct inn.reg_no,inn.tpt_cuo_nam, inn.item_total, inn.item_value_afs
from sigtasad.customs_import_data inn, SIGTASAD.CUSTOMS_IMPORT_DATA upp
where inn.reg_no = upp.reg_no and inn.tpt_cuo_nam = upp.tpt_cuo_nam
)

Case Statement as vals Outer Apply

I am trying to add a computed column to a table. Currently I have the following CASE statement building a calculated column in a SELECT statement,
--but I want to use this column to determine subsequent columns (sooo.. adding a computed column is somewhat a workaround to avoid complex CTE that i'm not sure i can figure out - let me know if CTE or functions is a better way to go)
--- I want the CASE statement to be a computed column in the table itself
SELECT top 1000
L.[MsgDate]
,C.[AlarmType]
,L.[Type]
,L.[UsrStr1]
,L.[UsrStr4]
,L.[UsrStr5]
,L.[UsrStr12]
,L.[UsrStr15]
,CASE
WHEN EXISTS
(
SELECT *
FROM Breinigsville.dbo.SCADA_SR S
WHERE S.SCADA_SR_TAG = L.UsrStr15 and
((L.[UsrStr4]='analog' and C.[AlarmType] like '%HH%') or (L.[UsrStr4]='status'))
)
THEN 1
ELSE 0
END AS [Safety]
FROM [Breinigsville].[dbo].[LMFW] L
full outer join [Breinigsville].[dbo].[_AcknowledgedAlarms] C
on
L.SeqNo=C.SeqNo2
WHERE (
L.[Type]='Ack' AND
L.UsrStr12 LIKE '%CCXOS%' AND
L.UsrStr12 NOT LIKE '%CCXOS5' AND
L.UsrStr12 NOT LIKE '%CCXOS6' AND
L.UsrStr12 NOT LIKE '%CCXOS9' AND
L.UsrStr12 NOT LIKE '%CCXOS12' AND
L.MsgDate>getdate()-1
)
order by L.SeqNo desc
Use outer apply:
FROM [Breinigsville].[dbo].[LMFW] L full outer join
[Breinigsville].[dbo].[_AcknowledgedAlarms] C
on L.SeqNo = C.SeqNo2 OUTER APPLY
(SELECT (CASE WHEN COUNT(*) > 0 THEN 1 ELSE 0 END) as Safety
FROM Breinigsville.dbo.SCADA_SR S
WHERE S.SCADA_SR_TAG = L.UsrStr15 and
((L.[UsrStr4] = 'analog' and C.[AlarmType] like '%HH%') or
(L.[UsrStr4] = 'status')
)
) vals
Then you can use vals.Safety anywhere in the query.
Note: this version uses count(*). If performance is at all a concern, then you can get what you want using an additional subquery. I answered this way, because the structure of the query seems easier to follow.
Incidentally, you cannot easily put a subquery in as a computed column. You can do it, but it requires creating a user-defined function, and using that for the computed column. OUTER APPLY is definitely simpler, and you can even put this logic into a table-valued function or view.

Adding to complex DQL Query

Hello I have this query witch works fine
SELECT y,
CASE WHEN (v IS NULL) THEN 0 ELSE SUM(v.viewCount) END AS HIDDEN sumviewcount
FROM YTScraperBundle:YouTubeVideo y
LEFT JOIN y.allViews WITH v.firstFetch BETWEEN :fromDate AND :toDate
GROUP BY y ORDER BY sumviewcount DESC
This is the SQL that is generated by the DQL
SELECT y0_.id AS id0, y0_.video_id AS video_id1, y0_.name AS name2, y0_.type AS type3, y0_.link AS
link4, y0_.first_fetch AS first_fetch5, y0_.last_fetch AS last_fetch6, CASE WHEN (v1_.id IS NULL)
THEN 0 ELSE SUM(v1_.viewCount) END AS sclr7 FROM youtubevideo y0_ LEFT JOIN views v1_ ON y0_.id
v1_.youtubevideo_id AND (v1_.first_fetch BETWEEN ? AND ?) GROUP BY y0_.id, y0_.video_id, y0_.name,
y0_.type, y0_.link, y0_.first_fetch, y0_.last_fetch ORDER BY sclr7 DESC LIMIT 30 OFFSET 0
I need to add a upper LIKE clause, that sorts this first. The above query works fine, but it runs on all YouTubeVideo's y, if I want it to run on just some of the videos where name has a specifik LIKE value, I would add an AND clause after the LEFT JOIN.
But I dont know where to add it, if I add it after the:
BETWEEN :fromDate AND :toDate
Like this:
BETWEEN :fromDate AND :toDate AND y.name LIKE '%somevalue%'
The SQL renders like this:
LEFT JOIN views v1_ ON y0_.id
v1_.youtubevideo_id AND (v1_.first_fetch BETWEEN ? AND ? AND y.name LIKE '%somevalue%')
The clause I am adding should not be inside the between paranthesis. It shouldn't it be outside?
Anyway how can I get around the BETWEEN statment? can I make it a:
...MyComparison AND BETWEEN...
?
try below where clause
Where '2014-10-01'< Date_Column and '2014-11-01'>Date_column and name like '%abc%'

optimising aggregates with and without a condition in ms sql

I would like to get a sum from a column, with and without a condition. The code I have now is
SELECT regular.id, regular.sum as regularsum, special.sum as specialsum FROM
(SELECT Sum(stuff) as sum, id FROM table
WHERE commonCondition = true
GROUP BY id) as regular
INNER JOIN
(SELECT Sum(stuff) as sum, id FROM table
Where commonCondition = true AND specialCondition = true
GROUP BY id) as special
ON regular.id = special.id
Which works, but seems very inefficient, not to mention ugly. the table is fairly large, so some optimisation would be welcome.
How can I write this in a better, more efficent way?
I think you could do something like this:
SELECT
Sum(stuff) as regularsum,
sum(case when specialcondition=true then stuff else 0 end) as specialsum,
id FROM table
WHERE commonCondition = true
GROUP BY id
However, you'd want to test to see if it was any faster.