SUM() OVER PARTITION BY - Invalid use of Aggregate Function - sql

I have a query which in the following example works fine
Select t2.leadno
, t1.quoteno
, t1.cn_ref
, sum(t1.qty/100)
, ROW_NUMBER() Over (Partition By t2.leadno order by sum(qty/100) desc) as RN
From dba.quotelne as t1
LEFT JOIN dba.quotehdr as t2 ON t1.quoteno = t2.quoteno
Where leadno = 31665
and t1.statusflag = 'A'
and t2.statusflag = 'A'
Group By t2.leadno
, t1.quoteno
, t1.cn_ref
As soon as I tell try to filter this to only show RN = 1 as shown below its give me an error of
"Invalid use of aggregate function"
Select t2.leadno
, t1.quoteno
, t1.cn_ref
, sum(t1.qty/100)
, ROW_NUMBER() Over (Partition By t2.leadno order by sum(qty/100) desc) as RN
From dba.quotelne as t1
LEFT JOIN dba.quotehdr as t2 ON t1.quoteno = t2.quoteno
Where leadno = 31665
and t1.statusflag = 'A'
and t2.statusflag = 'A'
and RN = 1
Group By t2.leadno
, t1.quoteno
, t1.cn_ref
All I have done is added is RN = 1 to the where statement, What am I missing?
I am using Adaptive Server Anywhere 9.0

I think you want:
Select Top 1 t2.leadno
, t1.quoteno
, t1.cn_ref
, sum(t1.qty/100)
, ROW_NUMBER() Over (Partition By t2.leadno order by sum(qty/100) desc) as RN
From dba.quotelne as t1
LEFT JOIN dba.quotehdr as t2 ON t1.quoteno = t2.quoteno
Where leadno = 31665
and t1.statusflag = 'A'
and t2.statusflag = 'A'
Group By t2.leadno
, t1.quoteno
, t1.cn_ref
Order By RN

You cannot use a column alias defined in a SELECT in a WHERE at the same level. This has nothing to do with window functions. It is a rule for all columns. So, use a subquery:
select t.*
from (Select t2.leadno, t1.quoteno, t1.cn_ref, sum(t1.qty/100),
ROW_NUMBER() Over (Partition By t2.leadno order by sum(qty/100) desc) as RN
From dba.quotelne t1 INNER JOIN
dba.quotehdr t2
ON t1.quoteno = t2.quoteno
Where leadno = 31665 and t1.statusflag = 'A' and t2.statusflag = 'A'
Group By t2.leadno, t1.quoteno, t1.cn_ref
) t
where rn = 1;
Note: Your LEFT JOIN is unnecessary, because the WHERE clause turns it into an INNER JOIN. So, I changed it to the INNER JOIN.

Related

Use CTE in SQL to flag DUPLICATES and reference in sub-query

So I have the following CTE:
with dupeinv AS (
select * from (
select
tci.t_idoc,
tci.t_idat,
ROW_NUMBER() OVER (partition by tci.t_idoc ORDER BY tci.t_idoc, tci.t_idat DESC) as rn
from [ln106].[dbo].tcisli305100 tci
) as t
where t.rn = 1
)
There are duplicates in the above table ([ln106].[dbo].tcisli305100) , hence the CTE to get single values. I want to format just these values in the below query (prefixed with ---)
select 'JCI' as BU,
RTRIM(LTRIM(cl.t_orno)) AS SALES_ORDER_NUMBER
, cl.t_pono AS SALES_ORDER_LINE_NUMBER
, CONCAT(cl.t_shpm, cl.t_pono, cl.t_idoc) AS SHIPPING_RECORD_ID
,CASE WHEN cl.t_dqua = 0 or cl.t_dqua is null THEN cl.t_amti ELSE
cl.t_amti / cl.t_dqua END AS AR_INVOICE_LINE_ITEM_PRICE_LOCAL
, cl.t_line AS AR_INVOICE_LINE_NUMBER
, cl.t_dqua AS AR_INVOICE_LINE_ITEM_QUANTITY
--- , concat(dupeinv.t_idoc,'|',format(dupeinv.t_idat,'MMddyyyy') ---
,ci.t_ccur AS AR_INVOICE_CURRENCY
, ci.t_idat AS AR_INVOICE_DATE
FROM [ln106].[dbo].tcisli310100 cl
LEFT JOIN [ln106].[dbo].tcisli305100 ci ON cl.t_idoc = ci.t_idoc
LEFT JOIN t di on cl.t_doc = di_t_doc
LEFT JOIN (SELECT t_orno,t_pono FROM [ln106].[dbo].ttdsls401100 WHERE t_oltp <> 1 group by t_orno,t_pono) as l --Jed 10162020 Changed the join to prevent duplicate records
ON l.t_orno=cl.t_orno COLLATE SQL_Latin1_General_CP1_CI_AS AND l.t_pono=cl.t_pono
LEFT JOIN dupeinv tci on cl.r_idoc = ci.t_doc
WHERE ci.t_idat > '2017'
Query doesn't like me referencing it in the main query. Can anyone help, or suggest a better idea?
Your final query should look something like this:
WITH dupeinv AS
(SELECT *
FROM
(SELECT tci.t_idoc,
tci.t_idat,
ROW_NUMBER() OVER (PARTITION BY tci.t_idoc
ORDER BY tci.t_idoc,
tci.t_idat DESC) AS rn
FROM [ln106].[dbo].tcisli305100 tci) AS t
WHERE t.rn = 1 )
SELECT 'JCI' AS BU,
RTRIM(LTRIM(cl.t_orno)) AS SALES_ORDER_NUMBER ,
cl.t_pono AS SALES_ORDER_LINE_NUMBER ,
CONCAT(cl.t_shpm, cl.t_pono, cl.t_idoc) AS SHIPPING_RECORD_ID ,
CASE
WHEN cl.t_dqua = 0
OR cl.t_dqua IS NULL THEN cl.t_amti
ELSE cl.t_amti / cl.t_dqua
END AS AR_INVOICE_LINE_ITEM_PRICE_LOCAL ,
cl.t_line AS AR_INVOICE_LINE_NUMBER ,
cl.t_dqua AS AR_INVOICE_LINE_ITEM_QUANTITY ,
concat(dupeinv.t_idoc,
'|',
format(dupeinv.t_idat, 'MMddyyyy')) ,
ci.t_ccur AS AR_INVOICE_CURRENCY ,
ci.t_idat AS AR_INVOICE_DATE
FROM [ln106].[dbo].tcisli310100 cl
LEFT JOIN [ln106].[dbo].tcisli305100 ci ON cl.t_idoc = ci.t_idoc
LEFT JOIN t di ON cl.t_doc = di_t_doc
LEFT JOIN
(SELECT t_orno,
t_pono
FROM [ln106].[dbo].ttdsls401100
WHERE t_oltp <> 1
GROUP BY t_orno,
t_pono) AS l --Jed 10162020 Changed the join to prevent duplicate records
ON l.t_orno=cl.t_orno COLLATE SQL_Latin1_General_CP1_CI_AS
AND l.t_pono=cl.t_pono
LEFT JOIN dupeinv tci ON cl.r_idoc = ci.t_doc
WHERE ci.t_idat > '2017'

What will be the query for this?

JOIN public.match m ON (s.stadium_id = m.stadium_id)
group
AS (
)
SELECT round_number
,stadium_name
,spectators
FROM (
SELECT round_number
,stadium_name
,spectators
,RANK() OVER (
PARTITION BY round_number ORDER BY spectators DESC
) AS rank1
FROM t1
) AS s1
WHERE rank1 = 1
<br>
Any smaller query than this?
I think you can just use window functions:
select ms.*
from (select m.round_number, s.stadium_name, m.no_spectators,
row_number() over (partition by m.round_number order by m.no_spectators desc) as seqnum
from public.stadium s join
public.match m
on s.stadium_id = m.stadium_id
) ms
where seqnum = 1
order by m.round_number;
I don't see why aggregation would be needed for the inner query.
You can use a subquery to get the max first
select m.round_number, s.stadium_name, MaxSpec
from public.stadium s
JOIN public.match m ON (s.stadium_id = m.stadium_id)
JOIN
(
select m.round_number, MAX(m.no_spectators) as MaxSpec
from public.stadium s
JOIN public.match m ON (s.stadium_id = m.stadium_id)
group by m.round_number
)a on m.no_spectators = a.MaxSpec
Just one more way to skin this cat. Throw your MAX(no_spectators) into a WHERE clause.
SELECT
m.round_number,
s.stadium_name,
m.no_spectators
FROM
PUBLIC.stadium s
JOIN
PUBLIC.match m
ON s.stadium_id = m.stadium_id
WHERE
m.no_spectators = (SELECT MAX(no_spectators) FROM PUBLIC.match);
That should do for an intro class.

Use of Analytical Funtions and Keep clause

I have a lenghtly query that can be shorten with the correct functionally (I beleive). Can we use existing functions such as Max Min Keep to make this query more efficient? My entire query is posted below.
For example: Can we remove the CTEs and use analytical functions such as max and min This would also elimate ranks and several joins
SQL:
WITH LAST_VALUE_BEFORE_START_DT AS (
SELECT DISTINCT * FROM(
SELECT
P.CL_ID,
HISTORYID,
H.MENT_DT,
H.ROLE AS MAX_ROLE,
H.PM_ID AS MAX_P_ID,
DENSE_RANK() OVER (PARTITION BY P.CL_ID ORDER BY H.MENT_DT DESC )AS RNK
FROM MANAGER_HISTORY H
INNER JOIN CP CCP ON H.CLIID = CCP.CLIID
INNER JOIN PROGRAM CP ON PROGRAMID = CP.PROGRAMID
WHERE 1=1
AND CP.TYPEID IN (13,200,11001)
AND H.ROLE = 'RED'
AND H.MENT_DT < START_DT
--AND P.CL_ID = 920917
)LAST_VALUE_BEFORE_START_DT_RNK
WHERE 1=1
AND RNK =1
)
,MIN_VALUE_BETWEEN_PROGRAM AS (
SELECT * FROM(
SELECT DISTINCT
P.CL_ID,
HISTORYID,
TRUNC(H.MENT_DT) AS MENT_DT,
H.ROLE AS MIN_ROLE,
H.PM_ID AS MIN_PM_ID,
DENSE_RANK() OVER (PARTITION BY P.CL_ID ORDER BY H.MENT_DT)AS RNK
FROM MANAGER_HISTORY H
INNER JOIN CP CCP ON H.CLIID = CCP.CLIID
INNER JOIN PROGRAM CP ON PROGRAMID = CP.PROGRAMID
WHERE 1=1
AND CP.TYPEID IN (13,200,11001)
AND H.ROLE = 'RED'
AND H.PM_ID IS NOT NULL
AND TRUNC(H.MENT_DT) BETWEEN TRUNC(START_DT) AND NVL(END_DT,SYSDATE)
--AND P.CL_ID = 920917
) MIN_VALUE_BETWEEN_PROGRAM_RNK
WHERE 1=1
AND RNK =1
)
SELECT * FROM (
SELECT
X.*,
DENSE_RANK() OVER (PARTITION BY CL_ID ORDER BY FIRST_ASSGN_DT,MENT_DT ) AS RNK
FROM(
SELECT DISTINCT
C.CL_ID,
P.CL_ID,
CP.PROGRAM,
START_DT,
END_DT,
H.ROLE,
H.MENT_DT,
H.PM_ID,
LVBS.MAX_ROLE,
LVBS.MAX_P_ID,
MVBP.MIN_ROLE,
MVBP.MIN_PM_ID
,CASE
WHEN H.MENT_DT < START_DT AND LVBS.MAX_ROLE = 'RED' AND LVBS.MAX_P_ID IS NOT NULL THEN TRUNC(START_DT)
WHEN H.MENT_DT BETWEEN START_DT AND NVL(END_DT,SYSDATE) AND H.ROLE = 'RED' AND H.PM_ID IS NOT NULL
THEN MVBP.MENT_DT
ELSE NULL --TESTING PURPOSES
END FIRST_ASSGN_DT
FROM MANAGER_HISTORY H
INNER JOIN CP CCP ON H.CLIID = CCP.CLIID
INNER JOIN CLIENT C ON CCP.CLIID = C.CLIID
INNER JOIN PROGRAM CP ON PROGRAMID = CP.PROGRAMID
LEFT JOIN LAST_VALUE_BEFORE_START_DT LVBS ON P.CL_ID = LVBS.CL_ID
LEFT JOIN MIN_VALUE_BETWEEN_PROGRAM MVBP ON P.CL_ID = MVBP.CL_ID
WHERE 1=1
AND CP.TYPEID IN (13,200,11001)
)X)Z
WHERE 1=1
AND Z.RNK = 1

Ways to avoid "This type of correlated subquery pattern is not supported"

This is what I'm trying to do
select something1,something2,account_id,
(select u.organization_id
from public.sfdc_contact sfdcc
join public.users u on u.email=sfdcc.email
where sfdcc.account_id=account_id
group by u.organization_id
order by count(*)
limit 1
)
from something
Redshift prompted me the error because I was trying to ORDER BY count. But I can't have 2 columns in a subquery, any tips?
Not entirely sure if this structure will exactly meet your needs, but using "window functions" such as ROW_NUMBER() OVER() can be used to supply single rows from joined derived tables (subqueries). For example:
SELECT
s.something1
, s.something2
, s.account_id
, d2.organization_id
, d2.cn
FROM something s
LEFT JOIN (
SELECT
organization_id
, account_id
, cn
, ROW_NUMBER() OVER (PARTITION BY organization_id ORDER BY cn) rn
FROM (
SELECT
u.organization_id
, sfdcc.account_id
, COUNT(*) OVER (PARTITION BY u.organization_id, sfdcc.account_id) cn
FROM public.sfdc_contact sfdcc
JOIN public.users u ON u.email = sfdcc.email
) d1
) d2 ON s.account_id = d2.account_id and d2.rn = 1
The use of COUNT() OVER() is probably unnecessary and this may be more pragmatic:
SELECT
s.something1
, s.something2
, s.account_id
, d2.organization_id
, d2.cn
FROM something s
LEFT JOIN (
SELECT
organization_id
, account_id
, cn
, ROW_NUMBER() OVER (PARTITION BY organization_id ORDER BY cn) rn
FROM (
SELECT
u.organization_id
, sfdcc.account_id
, COUNT(*) cn
FROM public.sfdc_contact sfdcc
JOIN public.users u ON u.email = sfdcc.email
GROUP BY
u.organization_id
, sfdcc.account_id
) d1
) d2 ON s.account_id = d2.account_id and d2.rn = 1
Note also that if you wanted the HIGHEST count then change the order used in the row_number to DESCending:
, ROW_NUMBER() OVER (PARTITION BY organization_id ORDER BY cn DESC) rn

joining multiple common table expressions

I have two Query, Query1:
with cte as (
select
dbo.Cable.*,
row_number() over(partition by dbo.Cable.TagNo order by dbo.Cable.CableRevision desc) as rn
from dbo.Cable
where (dbo.Cable.CableRevision = #CoreRevision )
)
select *
from cte
where rn = 1
and also Query2
with cte as (
select
dbo.Cable.TagNo,dbo.Core.*,
row_number() over(partition by dbo.Core.CoreNo order by dbo.Core.CoreRevision desc) as rn
from dbo.Core INNER JOIN
dbo.Cable ON dbo.Cable.Id = dbo.Core.CableId
where (dbo.Core.CoreRevision <= #CoreRevision )
)
select *
from cte
where rn = 1
these two query are related by Query1.TagNo and Query2.TagNo
how can i use join these two querys, is it possible to do that with With Command?
Thank you
Try this query, perhaps this is what you are looking for.
;WITH cte AS
(SELECT dbo.Cable.*,
row_number() over(partition by dbo.Cable.TagNo order by dbo.Cable.CableRevision desc) as rn
FROM dbo.Cable
WHERE dbo.Cable.CableRevision = #CoreRevision
), cte2 AS
(SELECT dbo.Cable.TagNo, dbo.Core.*,
row_number() over(partition by dbo.Core.CoreNo order by dbo.Core.CoreRevision desc) as rn
FROM dbo.Core INNER JOIN dbo.Cable ON dbo.Cable.Id = dbo.Core.CableId
WHERE dbo.Core.CoreRevision <= #CoreRevision
)
SELECT *
FROM cte c FULL JOIN cte2 c2 ON c.TagNo = c2.TagNo
WHERE c.rn = 1 OR c2.rn = 1
with cte as
(
select
dbo.Cable.*,
row_number() over(partition by dbo.Cable.TagNo order by dbo.Cable.CableRevision desc) as rn
from dbo.Cable
where (dbo.Cable.CableRevision = #CoreRevision )
),
cte2 as (
select
dbo.Cable.TagNo,dbo.Core.*,
row_number() over(partition by dbo.Core.CoreNo order by dbo.Core.CoreRevision desc) as rn
from dbo.Core INNER JOIN
dbo.Cable ON dbo.Cable.Id = dbo.Core.CableId
where (dbo.Core.CoreRevision <= #CoreRevision )
)
select *
from cte
join cte2 on cte1.TagNo = cte2.TagNo
where cte.rn = 1 and cte2.rn = 1;
I don't know if the condition cte.rn = 1 and cte2.rn = 1 is what you want. Maybe you just want it on one of the CTEs, maybe on both, maybe you actually want an outer join with cte2.rn = 1 in the join condition...