Analytics function is not allowed in Group By - sql

I have a following query:
SELECT t.date,
t.transactionId,
t.channelGrouping,
tp.itemRevenue,
t.transactionItemQuantity,
t.company,
t.campaign,
ifnull(replace((regexp_extract(tp.productCategory, r"(^[a-zA-Z0-9_.&;+ -]+)")),"&", "&"), "N/A") as productCategory,
ifnull(sum(a.adCost),0) as adCost
FROM `kpi.TransactionsByChannel` t left join kpi.TransactionProducts tp
on tp.transactionId = t.transactionId
left join kpi.Ads a
on a.company = t.company and a.date = t.date and a.campaign = t.campaign group by 1,2,3,4,5,6,7,8
which returns me result:
However, as you can see, adCost is not correct and duplicated. I need to get ratio of it based on itemRevenue or just write first row and other rows should be 0.
I tried following query:
SELECT t.date,
t.transactionId,
t.channelGrouping,
tp.itemRevenue,
t.transactionItemQuantity,
t.company,
t.campaign,
ifnull(replace((regexp_extract(tp.productCategory, r"(^[a-zA-Z0-9_.&;+ -]+)")),"&", "&"), "N/A") as productCategory,
round(adCost*(itemRevenue/SUM(itemRevenue) over (partition by tp.company, tp.productCategory, tp.date)),2) as adCost_adjusted,
FROM kpi.TransactionsByChannel t left join kpi.TransactionProducts tp
on tp.transactionId = t.transactionId
left join kpi.Ads a
on a.company = t.company and a.date = t.date and a.campaign = t.campaign group by 1,2,3,4,5,6,7,8,9
but I am getting an error of:
Column 9 contains an analytic function, which is not allowed in GROUP BY at [13:99]
Any helps would be really appreciated!

Try something like this approach:
with ttr as
(
select
t.transactionId,
sum(tp.itemRevenue) as TotalItemRevenue
from
`kpi.TransactionsByChannel` t
inner join
kpi.TransactionProducts tp
on
tp.transactionId = t.transactionId
group by
t.transactionId
)
SELECT
t.date,
t.transactionId,
t.channelGrouping,
tp.itemRevenue,
t.transactionItemQuantity,
t.company,
t.campaign,
ifnull(replace((regexp_extract(tp.productCategory, r"(^[a-zA-Z0-9_.&;+ -]+)")),"&", "&"), "N/A") as productCategory,
ifnull(a.adCost,0) * tp.itemRevenue / ttr.TotalItemRevenue as adCost_adjusted
FROM
`kpi.TransactionsByChannel` t
inner join
ttr
on
t.transactionId = ttr.transactionId
inner join
kpi.TransactionProducts tp
on
tp.transactionId = t.transactionId
left join
kpi.Ads a
on
a.company = t.company
and a.date = t.date
and a.campaign = t.campaign

Related

Pull a separate column that matches the (min) of an aggregate function

It works well so far but I am stumped from here as I am brand new to this. This query finds the closest distance match, pairing up every item in the "FAILED" folder against everything that isn't in the "FAILED" folder.
There is a column "RouteID" in the "table p" that I want to match up with the min() aggregate.
I cannot process how to make the SELECT query simply show the associated "RouteID" column from tbl p but ultimately, I want to turn this into an update query that will SET a.Route = p.Route that is associated with the min()
Any help would be appreciated.
SELECT a.name, a.Reference1,
MIN(round(ACOS(COS(RADIANS(90-a.lat))
*COS(RADIANS(90-p.latpoint))
+SIN(RADIANS(90-a.lat))
*SIN(RADIANS(90-p.latpoint))
*COS(RADIANS(a.lon-p.longpoint)))
*3958.756,2)) AS 'DISTANCE'
FROM tblOrder AS a WITH (NOLOCK)
LEFT JOIN
(
SELECT b.lat AS latpoint, b.lon AS longpoint,
b.Sequence, b.routeid
from tblOrder b WITH (NOLOCK)
WHERE b.CUSTID = 180016
AND b.routeID <> 'FAILED'
AND b.StopType = 1
) AS p ON 1=1
WHERE a.CustID = 180016
AND a.RouteID = 'FAILED'
AND a.StopType = 1
AND P.RouteID <> 'FAILED'
GROUP BY
a.name, a.Reference1
You can select them separately and then join them
SELECT c.name, c.Reference1, q.RouteID
FROM
(
SELECT a.name, a.Reference1,
MIN(round(ACOS(COS(RADIANS(90-a.lat))
*COS(RADIANS(90-p.latpoint))
+SIN(RADIANS(90-a.lat))
*SIN(RADIANS(90-p.latpoint))
*COS(RADIANS(a.lon-p.longpoint)))
*3958.756,2)) AS 'DISTANCE'
FROM tblOrder AS a WITH (NOLOCK)
LEFT JOIN
(
SELECT b.lat AS latpoint, b.lon AS longpoint,
b.Sequence, b.routeid
from tblOrder b WITH (NOLOCK)
WHERE b.CUSTID = 180016
AND b.routeID <> 'FAILED'
AND b.StopType = 1
) AS p ON 1=1
WHERE a.CustID = 180016
AND a.RouteID = 'FAILED'
AND a.StopType = 1
AND P.RouteID <> 'FAILED'
GROUP BY
a.name, a.Reference1
) c
LEFT JOIN
(
SELECT b.lat AS latpoint, b.lon AS longpoint,
b.Sequence, b.routeid
from tblOrderRouteStops b WITH (NOLOCK)
WHERE b.CUSTID = 180016
AND b.routeID <> 'FAILED'
AND b.StopType = 1
) AS q
ON q.routeID = c.DISTANCE

Probably subquerys and not joins

This big query below returns me 2860 records and its correct number. I'm getting. The thing is that I need to add to this query invoice lines and make the same thing as I did with sale_order_lines "sum(l.price_subtotal / COALESCE(cr.rate, 1.0)) AS price_subtotal". I need to get the sum of price_subtotal of invoice lines.
so my first thought was to join tables like this.
JOIN sale_order_invoice_rel so_inv_rel on (so_inv_rel.order_id = s.id )
JOIN account_invoice inv on (inv.id = so_inv_rel.invoice_id and inv.state in ('open','paid'))
JOIN account_invoice_line ail on (inv.id = ail.invoice_id)
and then
sum(ail.price_subtotal / COALESCE(cr.rate, 1.0)) as price_subtotal
but after the first JOIN number of lines, I'm selecting is changing and even if I joins are done the numbers are way off basically I get 5x2860. So probably I need to make some subquery but at this point, I don't know how and asking for help.
WITH currency_rate AS (
SELECT r.currency_id,
COALESCE(r.company_id, c.id) AS company_id,
r.rate,
r.name AS date_start,
( SELECT r2.name
FROM res_currency_rate r2
WHERE r2.name > r.name AND r2.currency_id = r.currency_id AND (r2.company_id IS NULL OR r2.company_id = c.id)
ORDER BY r2.name
LIMIT 1) AS date_end
FROM res_currency_rate r
JOIN res_company c ON r.company_id IS NULL OR r.company_id = c.id
)
SELECT min(l.id) AS id,
l.product_id,
l.color_id,
l.product_size_id,
t.uom_id AS product_uom,
sum(l.product_uom_qty / u.factor * u2.factor) AS product_uom_qty,
sum(l.qty_delivered / u.factor * u2.factor) AS qty_delivered,
sum(l.qty_invoiced / u.factor * u2.factor) AS qty_invoiced,
sum(l.qty_to_invoice / u.factor * u2.factor) AS qty_to_invoice,
sum(l.price_total / COALESCE(cr.rate, 1.0)) AS price_total,
l.price_unit / COALESCE(cr3.rate, 1.0) AS price_total_by_cmp_curr,
sum(l.price_subtotal / COALESCE(cr.rate, 1.0)) AS price_subtotal,
count(*) AS nbr,
s.date_order AS date,
s.state,
s.partner_id,
s.user_id,
s.company_id,
date_part('epoch'::text, avg(date_trunc('day'::text, s.date_order) - date_trunc('day'::text, s.create_date))) / (24 * 60 * 60)::numeric(16,2)::double precision AS delay,
t.categ_id,
s.pricelist_id,
s.project_id AS analytic_account_id,
s.team_id,
p.product_tmpl_id,
partner.country_id,
partner.commercial_partner_id
FROM sale_order_line l
JOIN sale_order s ON l.order_id = s.id
JOIN res_partner partner ON s.partner_id = partner.id
LEFT JOIN product_product p ON l.product_id = p.id
LEFT JOIN product_template t ON p.product_tmpl_id = t.id
LEFT JOIN product_uom u ON u.id = l.product_uom
LEFT JOIN product_uom u2 ON u2.id = t.uom_id
LEFT JOIN res_company rc ON rc.id = s.company_id
LEFT JOIN product_pricelist pp ON s.pricelist_id = pp.id
LEFT JOIN currency_rate cr ON cr.currency_id = pp.currency_id AND cr.company_id = s.company_id AND cr.date_start <= COALESCE(s.date_order::timestamp with time zone, now()) AND (cr.date_end IS NULL OR cr.date_end > COALESCE(s.date_order::timestamp with time zone, now()))
LEFT JOIN currency_rate cr3 ON cr.currency_id = rc.currency_id AND cr.company_id = s.company_id AND cr.date_start <= COALESCE(s.date_order::timestamp with time zone, now()) AND (cr.date_end IS NULL OR cr.date_end > COALESCE(s.date_order::timestamp with time zone, now()))
GROUP BY l.product_id, t.uom_id, t.categ_id, s.date_order, s.partner_id, s.user_id, s.state, s.company_id, s.pricelist_id, s.project_id, s.team_id, l.color_id, cr3.rate, l.price_unit, l.product_size_id, p.product_tmpl_id, partner.country_id, partner.commercial_partner_id;
You can add the part that could be in a subquery to the with statement you already have to avoid the increase in number of lines, like so:
WITH currency_rate AS (
SELECT r.currency_id,
COALESCE(r.company_id, c.id) AS company_id,
r.rate,
r.name AS date_start,
( SELECT r2.name
FROM res_currency_rate r2
WHERE r2.name > r.name AND r2.currency_id = r.currency_id AND (r2.company_id IS NULL OR r2.company_id = c.id)
ORDER BY r2.name
LIMIT 1) AS date_end
FROM res_currency_rate r
JOIN res_company c ON r.company_id IS NULL OR r.company_id = c.id
)
, order_line_subtotal AS(
SELECT so_inv_rel.order_id, sum(ail.price_subtotal) as price_subtotal
FROM sale_order_invoice_rel so_inv_rel
JOIN account_invoice inv on (inv.id = so_inv_rel.invoice_id and inv.state in ('open','paid'))
JOIN account_invoice_line ail on (inv.id = ail.invoice_id)
GROUP BY so_inv_rel.order_id )
SELECT min(l.id) AS id,
....
From there it should be straightforward to add to the query without increasing the number of rows (since from the joins you already have a row for each order before the aggregation / group by.

Query that will count text values and return the row with the lowest count value (min)?

Wondering if someone could assist with this query, I need to write a query that will count stores, based on that the state location, then display the state location with the lowest count for the country. Each country code must only be represented once.
this is what I have so far :-
select
CountryRegionCode]
, [StateProvinceCode]
, COUNT(PSP.StateProvinceID) as [No. of Stores]
from [Sales].[Store] as SS
inner join [Person].[BusinessEntityAddress] as PBEA on SS.BusinessEntityID = PBEA.BusinessEntityID
inner join [Person].[Address] as PA on PBEA.AddressID = PA.AddressID
inner join [Person].[StateProvince] as PSP on PA.StateProvinceID = PSP.StateProvinceID
group by
CountryRegionCode
,StateProvinceCode
having
count(PSP.StateProvinceID) =
(select min(a.cnt)
from (select count(PSP.StateProvinceID) as cnt from [Sales].[Store] as SS2
inner join [Person].[BusinessEntityAddress] as PBEA2 on SS2.BusinessEntityID = PBEA2.BusinessEntityID
inner join [Person].[Address] as PA2 on PBEA2.AddressID = PA2.AddressID
inner join [Person].[StateProvince] as PSP2 on pa2.StateProvinceID = PSP2.StateProvinceID
group by
CountryRegionCode) as a)
order by CountryRegionCode
I've attached the current output, how do I restrict the output to only show the lines highlighted in yellow? Eg the state with the lowest store count for each country.
(I'm not allowed to use the top clause)
thanks in advance
you can use min() function
with cte as
(
select
CountryRegionCode]
, [StateProvinceCode]
, COUNT(PSP.StateProvinceID) as no_of_store
from [Sales].[Store] as SS
inner join [Person].[BusinessEntityAddress] as PBEA on SS.BusinessEntityID = PBEA.BusinessEntityID
inner join [Person].[Address] as PA on PBEA.AddressID = PA.AddressID
inner join [Person].[StateProvince] as PSP on PA.StateProvinceID = PSP.StateProvinceID
group by
CountryRegionCode
,StateProvinceCode
having
count(PSP.StateProvinceID) =
(select min(a.cnt)
from (select count(PSP.StateProvinceID) as cnt from [Sales].[Store] as SS2
inner join [Person].[BusinessEntityAddress] as PBEA2 on SS2.BusinessEntityID = PBEA2.BusinessEntityID
inner join [Person].[Address] as PA2 on PBEA2.AddressID = PA2.AddressID
inner join [Person].[StateProvince] as PSP2 on pa2.StateProvinceID = PSP2.StateProvinceID
group by
CountryRegionCode) as a)
), cte2 as
(
select *,
min(no_of_store) over(partition by CountryRegionCode order by
CountryRegionCode ) as rn from cte
) select * from cte2 where rn=1
Use window functions:
select cs.*
from (select CountryRegionCode, StateProvinceCode,
count(*) as num_stores,
rank() over (partition by CountryRegionCode order by count(*) desc) as seqnum
from [Sales].[Store] ss join
[Person].[BusinessEntityAddress] pbea
pbeaon ss.BusinessEntityID = pbea.BusinessEntityID inner join
[Person].[Address] pa
on pbea.AddressID = pa.AddressID inner join
[Person].[StateProvince] psp
on pa.StateProvinceID = psp.StateProvinceID
group by CountryRegionCode, StateProvinceCode
) cs
where seqnum = 1;
If there are ties, this returns all the states with the largest values within a country. If you want only one, then use row_number() instead of rank().

Using a group by to group a select statement

Using a group by to group a select stament
SELECT
k.Ivalue, k.JOBDESCRIPTION ,
count( k.Ivalue) as TOTAL
FROM
(SELECT
a."ID" as Ivalue, b."JOBDESCRIPTION", rq."CURRENTSTATUS"
FROM
tblG2o_Requests a
INNER JOIN
tblG2o_JOBS b ON a."JOBPOSTID" = b."ID"
INNER JOIN
(SELECT
r.REQUESTID, ir."CURRENTSTATUS"
FROM
TBLG2O_RESULTSPOOL r
INNER JOIN
tblG2o_Requests ir ON r.RequestID = ir."ID"
WHERE
r.ShortListed = '1') rq ON rq.REQUESTID = a."ID"
WHERE
"ACTIVE" = '1'
AND "DATECOMPLETED" IS NULL
ORDER BY
"REQUESTDATE" DESC) k
GROUP BY
k.JOBDESCRIPTION
What is the question? You seem to be missing the group by clause, and you do not need double quotes around field names unless you have spaces in them, and even then, if TSQL for example, you would use [] in preference.
I had to remove an ORDER BY in the subquery, that isn't allowed unless other conditions demand it (like TOP n in TSQL)
SELECT
k.Ivalue
, k.JOBDESCRIPTION
, COUNT(k.Ivalue) AS TOTAL
FROM (
SELECT
a.ID AS Ivalue
, b.JOBDESCRIPTION
, rq.CURRENTSTATUS
FROM tblG2o_Requests a
INNER JOIN tblG2o_JOBS b
ON a.JOBPOSTID = b.ID
INNER JOIN (
SELECT
r.REQUESTID
, ir.CURRENTSTATUS
FROM TBLG2O_RESULTSPOOL r
INNER JOIN tblG2o_Requests ir
ON r.RequestID = ir.ID
WHERE r.ShortListed = '1'
) rqenter
ON rq.REQUESTID = a.ID
WHERE ACTIVE = '1'
AND DATECOMPLETED IS NULL
) k
GROUP BY
k.Ivalue
, k.JOBDESCRIPTION
Finally worked
SELECT
k.Ivalue
, l.JOBDESCRIPTION
, k.TOTAL,
k.CURRENTSTATUS
FROM (
SELECT
a.ID AS Ivalue
,b.ID as JobPostID
, rq."CURRENTSTATUS"
,COUNT(a.ID) AS TOTAL
FROM tblG2o_Requests a
INNER JOIN tblG2o_JOBS b
ON a."JOBPOSTID" = b.ID
INNER JOIN (
SELECT
r."REQUESTID"
, ir."CURRENTSTATUS"
FROM TBLG2O_RESULTSPOOL r
INNER JOIN tblG2o_Requests ir
ON r."REQUESTID" = ir.ID
WHERE r."SHORTLISTED" = 1
) rq
ON rq."REQUESTID" = a.ID
WHERE ACTIVE = '1'
AND DATECOMPLETED IS NULL
GROUP BY
a.ID ,b.ID
, rq."CURRENTSTATUS" ) k
inner join tblG2o_JOBS l on k.JobPostID =l.ID
enter code here

How to have distinct data in SQL using MAX on inner joined tables

I used this code to get distinct columns according to the max update_date. But still I get about 4 or 5 status_ids for the same tel_number. I want the max update date to take only the last date...which is not currently done by my code. Can someone please help me
SELECT DISTINCT t.Tel_Number,
t.Entity_ID,
t.Datasource,
t.Datasource_Number,
t.UpdateDate,
t.DataDate,
t.Telephone_ID,
t.Status_Id,
t.DateInserted,
t.ProcessName,
c.Status_Id AS CurrentCe_Status_ID,
s.StatusType AS CurrentCe_StatusType,
s.Description AS CurrentCe_Status_Description,
MAX(c.Update_Date) AS CurrentCe_Status_Date
FROM
Wrk.dbo.tel_trsn t WITH (NOLOCK) INNER JOIN CrWec.dbo.teldet d WITH (NOLOCK)
ON d.Tel_Number = t.Tel_Number
AND d.Entity_Id = t.Entity_ID
INNER JOIN CrWec.dbo.status c WITH (NOLOCK)
ON c.Entity_Id = t.Entity_ID
INNER JOIN CrWec.dbo.statusType s WITH (NOLOCK)
ON s.Status_Id = c.Status_Id
GROUP BY t.Tel_Number,
t.Entity_ID,
t.Datasource,
t.Datasource_Number,
t.UpdateDate,
t.DataDate,
t.Telephone_ID,
t.Status_Id,
t.DateInserted,
t.ProcessName,
c.Status_Id,
s.StatusType,
s.Description
Since you didn't specify what any of the keys were, I did the best I could with the query. In reality, if your key values are just Tel_Number, Entity_ID, Datasource, then you'd only need to partition on those 3 columns in the ROW_NUMBER function (or however many is necessary).
;with MaxUpdateDate as (
SELECT t.Tel_Number,
t.Entity_ID,
t.Datasource,
t.Datasource_Number,
t.UpdateDate,
t.DataDate,
t.Telephone_ID,
t.Status_Id,
t.DateInserted,
t.ProcessName,
c.Status_Id AS CurrentCe_Status_ID,
s.StatusType AS CurrentCe_StatusType,
s.Description AS CurrentCe_Status_Description,
c.Update_Date AS CurrentCe_Status_Date,
ROW_NUMBER() OVER (
PARTITION BY
t.Tel_Number,
t.Entity_ID,
t.Datasource,
t.Datasource_Number,
t.UpdateDate,
t.DataDate,
t.Telephone_ID,
t.Status_Id,
t.DateInserted,
t.ProcessName,
c.Status_Id,
s.StatusType,
s.Description
ORDER BY
c.Update_Date DESC) as 'RowNum'
FROM
Wrk.dbo.tel_trsn t WITH (NOLOCK)
INNER JOIN CrWec.dbo.teldet d WITH (NOLOCK)
ON d.Tel_Number = t.Tel_Number
AND d.Entity_Id = t.Entity_ID
INNER JOIN CrWec.dbo.status c WITH (NOLOCK)
ON c.Entity_Id = t.Entity_ID
INNER JOIN CrWec.dbo.statusType s WITH (NOLOCK)
ON s.Status_Id = c.Status_Id
)
SELECT
*
FROM
MaxUpdateDate
WHERE
RowNum = 1