Postgres json_agg order by - postgresql

I have multiple tables in a json_agg for psql:
SELECT json_agg(t) FROM (SELECT *,
( SELECT row_to_json(b) FROM ( SELECT * from (SELECT *, ( (3959 * acos( cos( radians(6.414478) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(12.466646) ) + sin( radians(6.414478) ) * sin( radians( lat ) ) ) ) * 1.609344 ) AS distance
from farm_location ) al WHERE farm_location_id=supply_forecast.farm_location_id and distance < 100 ) b) as farm_location,
( SELECT json_agg(c) FROM ( SELECT *
FROM supply_forecast_status_history WHERE supply_forecast_id=supply_forecast.supply_forecast_id) c) as supply_forecast_status
FROM supply_forecast WHERE delete = B'0' ORDER BY farm_location.distance desc) t;
I'm trying to calculate the distance from a farm location. The issue is I'm trying to sort the data by "distance" but it's throwing an error:
ERROR: missing FROM-clause entry for table "farm_location"
LINE 6: ...FROM supply_forecast WHERE delete = B'0' ORDER BY farm_locat...
^
********** Error **********
ERROR: missing FROM-clause entry for table "farm_location"
SQL state: 42P01
Character: 642
If I remove the ORDER BY farm_location.distance desc the query works but the data is not sorted by distance. Any idea on how to go about this?

json_agg function can support order by parameter.
SELECT json_agg(t.* ORDER BY distance desc) FROM
(SELECT *,
(SELECT row_to_json(b) FROM
(SELECT * from
(SELECT *
, ((3959 * acos( cos( radians(6.414478)) * cos( radians( lat )) * cos(radians( lng )
- radians(12.466646) ) + sin( radians(6.414478) ) * sin( radians( lat ) ) ) ) * 1.609344 ) as distance
from farm_location) al
where farm_location_id=supply_forecast.farm_location_id and distance < 100
) b
) as farm_location
, (select json_agg(c)
from (SELECT * FROM supply_forecast_status_history
WHERE supply_forecast_id=supply_forecast.supply_forecast_id) c
)as supply_forecast_status
FROM supply_forecast WHERE delete = B'0'
) t;

I apply ORDER BY inside function json_agg() and itÅ› work OK
select
jsonb_agg(jsonb_build_object('toma_parada', app51.app51_tomas__toma_parada)|| jsonb_build_object('lado', app51.app51_tomas__lado)|| jsonb_build_object('existen_tomas_paradas', app51.app51_tomas__existen_tomas_paradas)|| jsonb_build_object('b', app51.app51_tomas__b)|| jsonb_build_object('a', app51.app51_tomas__a)|| jsonb_build_object('m', app51.app51_tomas__m)|| jsonb_build_object('nombre', app51.app51_tomas__nombre, 'id_publico', app51.app51_tomas__id_publico , 'id', app51.app51_tomas__id )
order by app51.app51_tomas__id desc) as app51_tomas,
app51.fecha_modificacion,
app51.fecha_creacion,
app51.id_cliente,
app51.id_rumas,
app51.id_usuario,
app51.id,
app51.id_publico,
app51.habilitado,
app51.borrado,
app51.produccion_fin,
app51.produccion_inicio
from
(
select
distinct app51.id_cliente,
app51.id_rumas,
app51.id_usuario,
app51.id,
app51.id_publico,
app51.habilitado,
app51.borrado,
app51.atributos ->> 'produccion_fin' as produccion_fin,
app51.atributos ->> 'produccion_inicio' as produccion_inicio,
app51_tomas.atributos ->> 'toma_parada' as app51_tomas__toma_parada,
app51_tomas.atributos ->> 'lado' as app51_tomas__lado,
app51_tomas.atributos ->> 'existen_tomas_paradas' as app51_tomas__existen_tomas_paradas,
app51_tomas.atributos ->> 'b' as app51_tomas__b,
app51_tomas.atributos ->> 'a' as app51_tomas__a,
app51_tomas.atributos ->> 'm' as app51_tomas__m,
app51_tomas.atributos ->> 'nombre' as app51_tomas__nombre,
app51_tomas.id as app51_tomas__id ,
app51_tomas.id_publico as app51_tomas__id_publico ,
jsonb_build_object() as id_usuario_creacion,
jsonb_build_object() as id_usuario_modificacion,
(
select
TO_CHAR(app51.fecha_creacion::timestamptz at time zone 'GMT' , 'YYYY-MM-DD"T"HH24:MI:SS.ss0"Z"')) as fecha_creacion ,
(
select
TO_CHAR(app51.fecha_modificacion::timestamptz at time zone 'GMT' , 'YYYY-MM-DD"T"HH24:MI:SS.ss0"Z"')) as fecha_modificacion
from
app51
left join app51_app51_tomas on
app51_app51_tomas.id_app51 = app51.id
left join app51_tomas on
app51_app51_tomas.id_app51_tomas = app51_tomas.id
and ((app51_tomas.habilitado = true
and app51_tomas.borrado = false)
or app51_tomas is null )
where
(app51.habilitado = true
and app51.borrado = false)
order by
app51_tomas.id desc) as app51
group by
app51.fecha_modificacion,
app51.fecha_creacion,
app51.id_cliente,
app51.id_rumas,
app51.id_usuario,
app51.id,
app51.id_publico,
app51.habilitado,
app51.borrado,
app51.produccion_fin,
app51.produccion_inicio

Related

Why using same field when filtering cause different execution time? (different index usage)

When I run query and filter by agreement_id it is slow,
but when I filter by an alias id it is fast. (Look at the end of the query)
Why using same field when filtering cause different execution time?
Links to explain analyze:
slow1, slow2
fast1, fast2
Difference start at #20: Where different indexes are used:
Index Cond: (o.sys_period #> sys_time()) VS Index Cond: (o.agreement_id = 38)
PS. It would be nice if I can contact to developer of this feature (I have one more similar problem)
UPD I did some experiments. when I remove window functions from my query it works fast in any case. So why window function stop index usage in some cases? How to escape/workaround that?
dbfiddle with minimal test case
Server version is v13.1
Full query:
WITH gconf AS
-- https://www.postgresql.org/docs/current/queries-with.html#QUERIES-WITH-SELECT
NOT MATERIALIZED -- force it to be merged into the parent query
-- it gives a net savings because each usage of the WITH query needs only a small part of the WITH query's full output.
( SELECT
ocd.*,
tstzrange( '2021-05-01', '2021-05-01', '[]') AS acc_period,
(o).agreement_id AS id, -- Required to passthrough WINDOW FUNCTION
(o).id AS order_id,
(ic).consumed_period AS consumed_period,
dense_rank() OVER ( PARTITION BY (o).agreement_id, (o).id ORDER BY (ic).consumed_period ) AS nconf,
row_number() OVER ( wconf ORDER BY (c).sort_order NULLS LAST ) AS nitem,
(sum( ocd.item_cost ) OVER wconf)::numeric( 10, 2) AS conf_cost,
max((ocd.ic).consumed) OVER wconf AS consumed,
CASE WHEN true
THEN (sum( ocd.item_suma ) OVER wconf)::numeric( 10, 2 )
ELSE (sum( ocd.item_cost ) OVER wconf)::numeric( 10, 2 )
END AS conf_suma
FROM order_cost_details( tstzrange( '2021-05-01', '2021-05-01', '[]') ) ocd
WHERE true OR (ocd.ic).consumed_period #> lower( tstzrange( '2021-05-01', '2021-05-01', '[]') )
WINDOW wconf AS ( PARTITION BY (o).agreement_id, (o).id, (ic).consumed_period )
),
gorder AS (
SELECT *,
(conf_suma/6)::numeric( 10, 2 ) as conf_nds,
sum( conf_suma ) FILTER (WHERE nitem = 1) OVER worder AS order_suma
FROM gconf
WINDOW worder AS ( PARTITION BY gconf.id, (o).id )
-- TODO: Ask PG developers: Why changing to (o).agreement_id slows down query?
-- WINDOW worder AS ( PARTITION BY (o).agreement_id, (o).id )
)
SELECT
u.id, consumed_period, nconf, nitem,
(c).id as item_id,
COALESCE( (c).sort_order, pd.sort_order ) as item_order,
COALESCE( st.display, st.name, rt.display, rt.name ) as item_name,
COALESCE( item_qty, (c).amount/rt.unit ) as item_qty,
COALESCE( (p).label, rt.label ) as measure,
item_price, item_cost, item_suma,
conf_cost, consumed, conf_suma, conf_nds, order_suma,
(order_suma/6)::numeric( 10, 2 ) as order_nds,
sum( conf_suma ) FILTER (WHERE nitem = 1 ) OVER wagreement AS total_suma,
sum( (order_suma/6)::numeric( 10, 2 ) ) FILTER (WHERE nitem = 1 AND nconf = 1) OVER wagreement AS total_nds,
pkg.id as package_id,
pkg.link_1c_id as package_1c_id,
COALESCE( pkg.display, pkg.name ) as package,
acc_period
FROM gorder u
LEFT JOIN resource_type rt ON rt.id = (c).resource_type_id
LEFT JOIN service_type st ON st.id = (c).service_type_id
LEFT JOIN package pkg ON pkg.id = (o).package_id
LEFT JOIN package_detail pd ON pd.package_id = (o).package_id
AND pd.resource_type_id IS NOT DISTINCT FROM (c).resource_type_id
AND pd.service_type_id IS NOT DISTINCT FROM (c).service_type_id
-- WHERE (o).agreement_id = 38 -- slow
WHERE u.id = 38 -- fast
WINDOW wagreement AS ( PARTITION BY (o).agreement_id )
As problem workaround we can additionally SELECT an alias for column used at PARTITION BY expression. Then PG apply optimization and use index.
The answer to the question could be: PG does not apply optimization if composite type is used. Notice as it works:
PARTITION | FILTER | IS USED?
------------------------------
ALIAS | ORIG | NO
ALIAS | ALIAS | YES
ORIG | ALIAS | NO
ORIG | ORIG | NO
See this dbfiddle
create table agreement ( ag_id int, name text, cost numeric(10,2) );
create index ag_idx on agreement (ag_id);
insert into agreement (ag_id, name, cost) values ( 1, '333', 22 ),
(1,'333', 33), (1, '333', 7), (2, '555', 18 ), (2, '555', 2), (3, '777', 4);
select * from agreement;
create function initial ()
returns table( agreement_id int, ag agreement ) language sql stable AS $$
select ag_id, t from agreement t;
$$;
select * from initial() t;
explain( analyze, costs, buffers, verbose ) with totals_by_ag as (
select
*,
sum( (t.ag).cost ) over ( partition by agreement_id ) as total
from initial() t
)
select * from totals_by_ag t
where (t.ag).ag_id = 1; -- index is NOT USED
explain( analyze, costs, buffers, verbose ) with totals_by_ag as (
select
*,
sum( (t.ag).cost ) over ( partition by agreement_id ) as total
from initial() t
)
select * from totals_by_ag t
where agreement_id = 1; -- index is used when alias for column is used
explain( analyze, costs, buffers, verbose ) with totals_by_ag as (
select
*,
sum( (t.ag).cost ) over ( partition by (t.ag).ag_id ) as total --renamed
from initial() t
)
select * from totals_by_ag t
where agreement_id = 1; -- index is NOT USED because grouping by original column
explain( analyze, costs, buffers, verbose ) with totals_by_ag as (
select
*,
sum( (t.ag).cost ) over ( partition by (t.ag).ag_id ) as total --renamed
from initial() t
)
select * from totals_by_ag t
where (t.ag).ag_id = 1; -- index is NOT USED even if at both cases original column

PostgreSQL column does not exist when sub select

I have a table called user, and I need to get another users in an expecific distance range and the distance between them. My query is this:
select ( 6371 * acos(
cos( radians(users.latitude) )
* cos( radians(-22.9035) )
* cos( radians(-43.2096) - radians(users.longitude) )
+ sin( radians(users.latitude) )
* sin( radians(-22.9035) )
)
) as distance, users from users where users.id != 41 and distance > 50
I need to recover the user list and the distance, but the "as distance" is not working:
ERROR: column "distance" does not exist.
I tried with:
with distance as (
select ( 6371 * acos(
cos( radians(users.latitude) )
* cos( radians(-22.9035) )
* cos( radians(-43.2096) - radians(users.longitude) )
+ sin( radians(users.latitude) )
* sin( radians(-22.9035) )
)
) from users
)
select * from users where users.id != 41 and distance > 50
However, the error remains the same.
In Postgres, you cannot use column aliases in a where clause. That's why your first query does not work.
Your second query using a CTE can be made to work, but distance is a virtual table, not a column. Join with it.
with users_distance as (
select
id,
( 6371 * acos(
cos( radians(users.latitude) )
* cos( radians(-22.9035) )
* cos( radians(-43.2096) - radians(users.longitude) )
+ sin( radians(users.latitude) )
* sin( radians(-22.9035) )
)
) as distance
from users
)
select *
from users
join users_distance ud on users.id = ud.id
where users.id != 41 and ud.distance > 50
If you do this a lot, consider adding a generated column to the table so you don't have to recalculated it all the time.
alter table users add column distance numeric
generated always as (
6371 * acos(
cos( radians(users.latitude) )
* cos( radians(-22.9035) )
* cos( radians(-43.2096) - radians(users.longitude) )
+ sin( radians(users.latitude) )
* sin( radians(-22.9035) )
)
) stored
Try it
Finally, rather than doing these calculations yourself, consider using the very powerful PostGIS extension.

How to get distance for the nearest city from Two different table and combining it into 1 column in PostgreSQL

i was stuck for days in this schema now.
I am trying to populate distance column in a different table from other 2 tables. Inside the table there are lat, long, city id, distance, and location id.
This is the current table that i wanted to populate
This is the two tables that i can get to calculate the distance from
LocationID are the same as ID in the first table
To calculate the distance to the nearest city i calculate it using lat long, this is what my code look like for the nearest distance
select location_id, distance
from (SELECT t.table1.location_id as location_id,
( 6371 * acos( cos( radians(6.414478) ) *
cos( radians(t.table1.latitude::float8) ) *
cos( radians(t.table1.longitude::float8) - radians(12.466646) )
+ sin( radians(6.414478) ) * sin( radians(t.table1.latitude::float8) ) ) ) AS distance
FROM t.table1
INNER JOIN t.table2
on t.table1.location_id = t.table2.id
) km
group by location_id, distance
Having distance < 2000
order by distance limit 20;
but the table only returns null value
I'm using PostgreSQL for this code and the application used for visualising is metabase.
I would recommend you to use ST_Distance function from PostGIS extension for distance calculation instead of doing it yourself. It will be easier and definitely much faster.
Edited: Probably misunderstood the initial intentions.
This should work:
select d.city_id, d.distance, latitude,location_id, longitude
from t.table1
left join lateral (
select city_id, distance from (
select location_id city_id, ( 6371 * acos( cos( radians(table3.latitude) ) *
cos( radians(t.table1.latitude::float8) ) *
cos( radians(t.table1.longitude::float8) - radians(table3.longitude) )
+ sin( radians(table3.latitude) ) * sin( radians(t.table1.latitude::float8) ) ) ) AS distance
from t.table3
) d
where distance < 2000
order by distance
limit 1
) cities on true
Try it out.
Best regards,
Bjarni

How to PIVOT this query and display only TOP 10 records filtered by SUM(NetWrittenPremium) DESC

In this query I cant understand what would be the proper syntax to PIVOT it by month and also display just top 10 records based on SUM(NetWrittenPremium).
;with cte_TopClasses
AS (
select
b.YearNum,
b.MonthNum,
REPLACE(ClassCode,'+','') + ' - '+ QLL.Description as Description,
SUM( Premium) as NetWrittenPremium
FROM tblCalendar b
LEFT JOIN ProductionReportMetrics prm ON b.MonthNum=Month(prm.EffectiveDate) AND b.YearNum = YEAR(EffectiveDate)
AND prm.EffectiveDate >=DateAdd(yy, -1, DATEADD(d, 1, EOMONTH(GETDATE()))) AND prm.EffectiveDate <= EOMONTH(GETDATE()) AND CompanyLine = 'Ironshore Insurance Company'
LEFT JOIN NetRate_Quote_Insur_Quote Q ON prm.NetRate_QuoteID = Q.QuoteID
LEFT JOIN NetRate_Quote_Insur_Quote_Locat QL ON Q.QuoteID = QL.QuoteID
LEFT JOIN (SELECT * FROM NetRate_Quote_Insur_Quote_Locat_Liabi nqI
JOIN ( SELECT LocationID as LocID, MAX(ClassCode) as ClCode
FROM NetRate_Quote_Insur_Quote_Locat_Liabi GROUP BY LocationID ) nqA
ON nqA.LocID = nqI.LocationID AND nqA.ClCode = nqI.ClassCode ) QLL
ON QLL.LocationID = QL.LocationID
WHERE ( b.YearNum = YEAR(GETDATE())-1 and b.MonthNum >= MONTH(GETDATE())+1 ) OR
( b.YearNum = YEAR(GETDATE()) and b.MonthNum <= MONTH(GETDATE()) )
GROUP BY b.YearNum,b.MonthNum,ClassCode, QLL.Description
)
SELECT
--TOP 10
RANK() OVER (ORDER BY NetWrittenPremium DESC) AS Rank, *
FROM cte_TopClasses
WHERE Description IS NOT NULL
ORDER BY NetWrittenPremium DESC,YearNum,MonthNum
The result should look something like that:
If I use the query below and then using matrics in SSRS to PIVOT it - then after grouping by Description it only displays me 2 Description.
;with cte_TopClasses
AS (
select
b.YearNum,
b.MonthNum,
REPLACE(ClassCode,'+','') + ' - '+ QLL.Description as Description,
SUM( Premium) as NetWrittenPremium
FROM tblCalendar b
LEFT JOIN ProductionReportMetrics prm ON b.MonthNum=Month(prm.EffectiveDate) AND b.YearNum = YEAR(EffectiveDate)
AND prm.EffectiveDate >=DateAdd(yy, -1, DATEADD(d, 1, EOMONTH(GETDATE()))) AND prm.EffectiveDate <= EOMONTH(GETDATE()) AND CompanyLine = 'Ironshore Insurance Company'
LEFT JOIN NetRate_Quote_Insur_Quote Q ON prm.NetRate_QuoteID = Q.QuoteID
LEFT JOIN NetRate_Quote_Insur_Quote_Locat QL ON Q.QuoteID = QL.QuoteID
LEFT JOIN (SELECT * FROM NetRate_Quote_Insur_Quote_Locat_Liabi nqI
JOIN ( SELECT LocationID as LocID, MAX(ClassCode) as ClCode
FROM NetRate_Quote_Insur_Quote_Locat_Liabi GROUP BY LocationID ) nqA
ON nqA.LocID = nqI.LocationID AND nqA.ClCode = nqI.ClassCode ) QLL
ON QLL.LocationID = QL.LocationID
WHERE ( b.YearNum = YEAR(GETDATE())-1 and b.MonthNum >= MONTH(GETDATE())+1 ) OR
( b.YearNum = YEAR(GETDATE()) and b.MonthNum <= MONTH(GETDATE()) )
GROUP BY b.YearNum,b.MonthNum,ClassCode, QLL.Description
)
SELECT *
FROM (SELECT RANK() OVER (ORDER BY NetWrittenPremium DESC) AS Rank, *
FROM cte_TopClasses
WHERE Description IS NOT NULL) AA
WHERE AA.Rank <= 10
ORDER BY AA.NetWrittenPremium DESC, AA.YearNum, AA.MonthNum
And the result of it in SSRS matrics :
You could try something like this at the end of the query, rather than what is there now:
SELECT *
FROM (SELECT RANK() OVER (ORDER BY [Description] DESC) AS Rank, *
FROM cte_TopClasses
WHERE Description IN (SELECT [Description]
FROM (SELECT RANK() OVER (ORDER BY SUM(NetWrittenPremium) DESC) AS [Rank], [Description], SUM(NetWrittenPremium) AS total
FROM cte_TopClasses
WHERE [Description] IS NOT NULL
GROUP BY [Description]) BB
WHERE [Rank] <= 10)) AA
ORDER BY YearNum, MonthNum
This wraps the query in a SELECT, and filters the ranked results to the 10 you want.
Then use a matrix in the report to pivot the results.

Pass multiple postgres SQL statements in a single PGexec call

In t-sql, it's possible to run multiple select statements without a ;. Example:
select 1 select 2 is valid, and returns two datasets of 1 and 2 respectively.
In postgres, it is not possible to run multiple select statements... you need a ; delimiter otherwise you get a syntax error.
Referencing the docs: http://www.postgresql.org/docs/current/interactive/libpq-exec.html
Multiple queries sent in a single PQexec call are processed in a single transaction, unless there are explicit BEGIN/COMMIT commands included in the query string to divide it into multiple transactions.
How can I do this?
Let's say I want to run these two queries on the server: select 1 select 2: should it look like this:
begin
select 1
commit;
begin
select 2
commit
I'm ok with it only returning the last query as the result set, but I need to know that the first query was executed on the server, even if it's not returning with that result set.
Why I want to do this: I have a complex sql script that has ~6 temp tables to build that the main query will use. By delimiting the temp tables with the ; syntax, I can't schedule this script in cron to run on a schedule. If I can get the temp tables to run and the main query to access them in the same PGexec call, I'd be very very happy.
You don't need libpq directly, you can just use he psql front end (in cron, you might need to specify the absolute pathname for the binary)
#!/bin/sh
psql -U my_user mydb <<OMG
begin;
select tralal 1;
commit;
begin;
select domtidom 2;
commit;
OMG
I was able to accomplish what I was looking for with CTEs rather than temp tables... one long chain of CTEs (acting as temp tables) waterfalling into the main query.
A simple example:
with first as (
select 1 as col
),
second as (
select 2 as col
)
select * from first union all select * from second
A more complex example:
with COGS as (
select 'Product1' Vertical, 3.0 Credit, 1.00 Debit, 2.75 Blend, 4.30 Amex, 0.25 ACH union
select 'Product2', 3.1, 2.2, 2.8, 4.5, 0.25 union
),
Allocable_Card_Volume as (
select MPR.Date, sum(MPR.Card_Volume_Net_USD) Allocable_Card_Volume
from mpr_base MPR
where MPR.Gateway in ('YapProcessing') and MPR.Vertical not in ('HA-Intl','HA')
group by MPR.Date
),
COGS_Financials_Base as (
select '2013-01-31'::DATE Date , 1000 Total_COGS , 200 Homeaway , (select Allocable_Card_Volume from Allocable_Card_Volume where Date in ('2013-01-31') ) Allocable_Card_Volume union
),
Initial_COGS as (
select
MPR.Date,
sum(
case when MPR.PaymentTypeGroup in ('ACH_Scan','AmEx') then (Txn_Count * COGS.ACH) else 0 end +
case when MPR.Vertical not in ('HA') and MPR.PaymentTypeGroup in ('Card','AmEx-Processing') then
coalesce( ((Credit_Card_Net_USD - Amex_Processing_Net_USD) * COGS.Credit * 0.01),0) + coalesce((Debit_Card_Net_USD * COGS.Debit * 0.01),0) + coalesce((Amex_Processing_Net_USD * COGS.Amex * 0.01),0) + coalesce((case when TPV is null and PaymentTypeGroup in ('Card') then TPV_Billing else 0 end * COGS.Blend * 0.01),0)
when MPR.Vertical in ('HA') and MPR.PaymentTypeGroup in ('Card','AmEx-Processing') and FeePaymentType in ('PropertyPaid') then
coalesce(COGS_Financials.Homeaway,0)
else 0 end
) Initial_COGS
from
mpr_base MPR
left join COGS on COGS.Vertical = MPR.Vertical and MPR.Gateway in ('YapProcessing') and MPR.PaymentTypeGroup not in ('Cash')
left join COGS_Financials_Base COGS_Financials on MPR.Date = COGS_Financials.Date and MPR.Gateway in ('YapProcessing') and MPR.PaymentTypeGroup in ('Card')
where MPR.Gateway in ('YapProcessing') and MPR.Vertical not in ('HA-Intl') and MPR.PaymentTypeGroup not in ('Cash')
group by
MPR.Date
),
COGS_Financials as (
select
COGS_Financials_Base.*, (COGS_Financials_Base.Total_COGS - Initial_COGS.Initial_COGS) Allocation
from
COGS_Financials_Base
join Initial_COGS on COGS_Financials_Base.Date = Initial_COGS.Date
),
MPR as (
select
MPR.Date,MPR.Gateway,MPR.Vertical, MPR.ParentAccountId, MPR.ParentName ,
MPR.PaymentTypeGroup ,
sum(TPV_USD) TPV_USD,
sum(TPV_Net_USD) TPV_Net_USD,
sum(Revenue_Net_USD) Revenue_Net_USD ,
sum(coalesce(
case when MPR.PaymentTypeGroup in ('ACH_Scan','AmEx') then (Txn_Count * COGS.ACH) else 0 end +
case when MPR.Vertical not in ('HA') and MPR.PaymentTypeGroup in ('Card','AmEx-Processing') then
coalesce( ((Credit_Card_Net_USD - Amex_Processing_Net_USD) * COGS.Credit * 0.01),0) + coalesce((Debit_Card_Net_USD * COGS.Debit * 0.01),0) + coalesce((Amex_Processing_Net_USD * COGS.Amex * 0.01),0) + coalesce((case when TPV is null and PaymentTypeGroup in ('Card') then TPV_Billing else 0 end * COGS.Blend * 0.01),0)
+(coalesce( ( ( cast(Card_Volume_Net_USD as decimal(18,2) ) / cast(COGS_Financials.Allocable_Card_Volume as decimal(18,2)) ) * COGS_Financials.Allocation ), 0) ) -- Excess
when MPR.Vertical in ('HA') and MPR.PaymentTypeGroup in ('Card','AmEx-Processing') and MPR.FeePaymentType in ('PropertyPaid') then coalesce(COGS_Financials.Homeaway,0)
else 0
end,0)
) COGS_USD,
sum(Txn_Count) Txn_Count
from
mpr_Base MPR
left join COGS on COGS.Vertical = MPR.Vertical and MPR.Gateway in ('YapProcessing') and MPR.PaymentTypeGroup not in ('Cash')
left join COGS_Financials on MPR.Date = COGS_Financials.Date and MPR.Gateway in ('YapProcessing') and MPR.PaymentTypeGroup in ('Card','AmEx-Processing')
where
MPR.Date in ('2016-02-29')
group by
MPR.Date,MPR.Gateway,MPR.Vertical , MPR.ParentAccountId ,MPR.ParentName,
MPR.PaymentTypeGroup
)
select
Vertical,
sum(TPV_USD)::money as TPV_USD,
sum(Revenue_Net_USD)::money as Revenue_Net_USD,
sum(COGS_USD)::money COGS_USD,
round((sum(Revenue_Net_USD)-sum(COGS_USD))/sum(Revenue_Net_USD)*100,2) Accounting_Margin
from
MPR
where Date in ('2016-02-29')
group by
Vertical
union all
select
'Total' ,
sum(TPV_USD)::money as TPV_USD,
sum(Revenue_Net_USD)::money as Revenue_Net_USD,
sum(COGS_USD)::money COGS_USD,
round((sum(Revenue_Net_USD)-sum(COGS_USD))/sum(Revenue_Net_USD)*100,2) Accounting_Margin
from
MPR
where Date in ('2016-02-29')
I said it would be complex :-)
From your answer, you could also do this
SELECT * FROM a
UNION ALL
SELECT * FROM b
UNION ALL
SELECT * FROM c
...