We have a postgresql database that I am using OPENQUERY to query from a SQL Server 2008 R2 install. From an optimization standpoint is it quicker to
OPTION 1:
1) Create a view of the postgresql data on SQL Server
2) Write a CTE to aggregate the data from the view & query
OPTION 2:
1) Write a CTE using OPENQUERY and aggregate the data in one query
I have run tests using both methods and in the instances I ran there is roughly a 2 – 3 second difference with either way. In the guru's opinions what is the quickest/less resource intensive way to run a query of this nature?
****And let me add that since the views are created from using OPENQUERY there is no way to index the views.
SELECT *
FROM OPENQUERY(555.187.331.191,'WITH CTE_Employee AS
(
select
cm.empID As employeeID
,RTRIM(LTRIM(cm.employeeName)) As employeeName
,cm.employeeStatus As employeeStatus
FROM employee cm
WHERE employeeStatus IN (''I'', ''A'', ''R'', ''F'')
ORDER BY employeeName ASC
)
, CTE_TSA AS
(
Select
cm.employeeName employee
,SUM(CAST(COALESCE(rl.salesamount,0) AS DOUBLE PRECISION)) As "TotalSaleAmount"
,rc.saleDate TransDate
FROM saleLog rc
JOIN saleLogLine rl
ON rc.saleID = rl.saleLog
JOIN saleType sc
ON rl.saleType = sc.saleTypeID
JOIN employee cm
ON rc.empID = cm.empID
WHERE rl.saleLog = rc.saleID
AND sc.saleVerified = ''True''
AND rc.saleDate BETWEEN ''2017-01-01'' AND ''2017-01-31''
GROUP BY cm.employeeName,rc.saleDate
)
, CTE_TB AS
(
Select
cm.employeeName employee
,SUM(CAST(COALESCE(jc.bonusAmt,0) AS DOUBLE PRECISION)) As totalBonus
,jc.saleID saleID
,iv.saleDate saleDate
FROM saleBonus jc
INNER JOIN saleBonusStructure ac
ON jc.saleID = ac.saleID
LEFT OUTER JOIN saleCategory wp
ON wp.saleCatID = ac.saleCatID
Left join sales iv
ON jc.saleID = iv.saleID
LEFT JOIN hoursWorkred jb
ON jc.saleID = jb.saleID
INNER JOIN employee cm
ON cm.empID = jb.empID
WHERE (ac.ice)
AND jc.saleID IN (Select saleID from sales)
AND iv.saleDate BETWEEN ''2017-01-01'' AND ''2017-01-31''
GROUP BY cm.employeeName,jc.saleID,iv.saleDate
)
, CTE_Tres AS
(
SELECT hoursWorkredarchive.saleID,
CAST(SUM(hoursWorkredarchive.actualSaleAmt) AS DOUBLE PRECISION) "ActualAmt"
,employee.employeeName As "employee"
,hoursWorkredarchive.saleDate
,hoursWorkredarchive.saleDescription
FROM ((hoursWorkredarchive hoursWorkredarchive
LEFT OUTER JOIN employee employee
ON hoursWorkredarchive.empID=employee.empID )
LEFT OUTER JOIN salesperson salesperson
ON hoursWorkredarchive.salesid=salesperson.salesid )
LEFT OUTER JOIN hoursWorkred hoursWorkred
ON hoursWorkredarchive.saleID=hoursWorkred.saleID
WHERE ((hoursWorkredarchive.saleDate BETWEEN ''2017-01-01'' AND ''2017-01-31''))
GROUP BY hoursWorkredarchive.saleID,employee.employeeName,hoursWorkredarchive.saleDate, hoursWorkredarchive.empID, hoursWorkred.hoursWorkredtype,hoursWorkredarchive.saleDescription
ORDER BY hoursWorkredarchive.empID, hoursWorkred.hoursWorkredtype, employee.employeeName
)
, CTE_TotalSales AS
(
with MaxVerNumber as
(
select max(saleVersionNumber) as "MaxSaleNumber"
,saleNumber
from sales
group by saleNumber
)
select
es.saleNumber As "saless"
,cm.employeeName employeeName
,es.saleDate saleDate
FROM MaxVerNumber vn
INNER JOIN sales es
ON vn.saleNumber=es.saleNumber
AND COALESCE(vn."MaxSaleNumber",0)=COALESCE(es.saleVersionNumber,0)
INNER JOIN employee cm
ON es.employeeID = cm.empID
WHERE es.saleDate BETWEEN ''2017-01-01'' AND ''2017-01-31''
)
, CTE_hoursWorked As
(
Select
iv.saleID As hoursWorked
,cm.empID employeeID
,cm.employeeName employeeName
,iv.saleDate saleDate
from sales iv
INNER JOIN hoursWorkred jb
ON jb.saleID = iv.saleID
INNER JOIN employee cm
ON cm.empID = jb.empID
WHERE iv.saleDate BETWEEN ''2017-01-01'' AND ''2017-01-31''
GROUP BY cm.empID, cm.employeeName, hoursWorked, iv.saleDate
)
, CTE_salessUnder500 As
(
WITH salesAmt AS
(Select
cm.employeeName As "employee"
,jb.saleID saleID
,CAST(COALESCE(jp.saleAmt,0) As DOUBLE PRECISION) salesAmt
,CAST(jp.ccsaleDate As Date) saleDate
FROM hoursWorkredpart jp
Inner Join hoursWorkred jb
on jp.saleID = jb.saleID
Inner join employee cm
on cm.empID = jb.empID
WHERE jp.ccsaleDate BETWEEN ''2017-01-01'' AND ''2017-01-31''
AND cm.employeeStatus IN (''I'', ''A'', ''R'', ''F''),
employees As
(select
cm.employeeName As employee
,cm.employeeStatus As employeeStatus
FROM employee cm
ORDER BY employee ASC)
Select
DISTINCT(RTRIM(LTRIM(employee))) As "employeeName"
,COUNT(1) AS "Count"
FROM employees cm
JOIN (
Select
RTRIM(LTRIM("employee")) As employee
,SUM(salesAmt) As "TotalsalesAmt"
,saleDate
,saleID
FROM salesAmt
GROUP BY RTRIM(LTRIM("employee")),saleDate,saleID
) As ia
ON RTRIM(LTRIM(ia.employee)) = RTRIM(LTRIM(cm.employee))
WHERE CAST(saleDate As Date) BETWEEN ''2017-01-01'' AND ''2017-01-31''
AND "TotalsalesAmt" <= ''500.00''
GROUP BY "employeeName"
)
, CTE_saless As
(
WITH salesAmt AS
(Select
cm.employeeName employee
,jb.saleID saleID
,CAST(COALESCE(jp.saleAmt,0) As DOUBLE PRECISION) salesAmt
,CAST(jp.ccsaleDate As Date) saleDate
FROM hoursWorkredpart jp
Inner Join hoursWorkred jb
on jp.saleID = jb.saleID
Inner join employee cm
on cm.empID = jb.empID
WHERE jp.ccsaleDate BETWEEN ''2017-01-01'' AND ''2017-01-31''
AND cm.employeeStatus IN (''I'', ''A'', ''R'', ''F''),,
employees As
(select
cm.employeeName As employee
,cm.employeeStatus As employeeStatus
FROM employee cm
ORDER BY employee ASC)
Select
DISTINCT(RTRIM(LTRIM(employee))) As "employeeName"
,COUNT(1) AS "Count"
FROM employees cm
JOIN (
Select
RTRIM(LTRIM(employee)) As employee
,SUM(salesAmt) As "TotalsalesAmt"
,saleDate
,saleID
FROM salesAmt
GROUP BY RTRIM(LTRIM(employee)),saleDate,saleID
) As ia
ON RTRIM(LTRIM(ia.employee)) = RTRIM(LTRIM(cm.employee))
WHERE CAST(saleDate As Date) BETWEEN ''2017-01-01'' AND ''2017-01-31''
GROUP BY "employeeName"
)
SELECT S.employeeName
, COUNT(JB.hoursWorked) As "NumberOfhoursWorkreds"
, COUNT(ES."saless") As "NumberOfsaless"
, COALESCE(IU500."Count",0) As "salessUnder500"
, COALESCE(IV."Count",0) As "saless"
, CAST(COALESCE(TR."TotalSaleAmount",0) AS INT) As "TotalSales"
, CAST(COALESCE(TR."TotalSaleAmount",0) AS INT)-COALESCE(TF.totalBonus,0) As "EmpBenefit"
, COALESCE(TF.totalBonus,0) As "FreightRev"
, COALESCE(TF.totalBonus,0)*.75 As "FreightCost"
, (COALESCE(CAST(TR."TotalSaleAmount" AS DOUBLE PRECISION),0)-COALESCE(CAST(TF.totalBonus AS DOUBLE PRECISION),0))+(COALESCE(CAST(TF.totalBonus AS DOUBLE PRECISION),0)*.75) As "Log1"
, COALESCE(CAST(DAC."ActualAmt" AS DOUBLE PRECISION),0) As "Log2"
, COALESCE(CAST(TR."TotalSaleAmount" AS DOUBLE PRECISION),0)-COALESCE(CAST(DAC."ActualAmt" AS DOUBLE PRECISION),0) As "Log3"
, case
when CAST((COALESCE(CAST(TR."TotalSaleAmount" AS DOUBLE PRECISION) ,0)-COALESCE(CAST(DAC."ActualAmt" AS DOUBLE PRECISION),0))/NULLIF(COALESCE(CAST(TR."TotalSaleAmount" AS DOUBLE PRECISION),0),0) AS DECIMAL(10,2)) IS NULL THEN ''0.00''
else CAST((COALESCE(CAST(TR."TotalSaleAmount" AS DOUBLE PRECISION) ,0)-COALESCE(CAST(DAC."ActualAmt" AS DOUBLE PRECISION),0))/NULLIF(COALESCE(CAST(TR."TotalSaleAmount" AS DOUBLE PRECISION),0),0) AS DECIMAL(10,2))
end As "Log4"
FROM CTE_Employee AS S
LEFT OUTER JOIN CTE_TSA AS TR ON TR.employee = S.employeeName
LEFT OUTER JOIN CTE_TB AS TF ON TF.employee = S.employeeName
LEFT OUTER JOIN CTE_Tres AS DAC ON DAC."employee" = S.employeeName
LEFT OUTER JOIN CTE_TotalSales AS ES ON ES.employeeName = S.employeeName
LEFT OUTER JOIN CTE_hoursWorked AS JB ON JB.employeeName = S.employeeName
LEFT OUTER JOIN CTE_salessUnder500 AS IU500 ON IU500."employeeName" = S.employeeName
LEFT OUTER JOIN CTE_saless AS IV ON IV."employeeName" = S.employeeName
GROUP BY S.employeeName, IU500."Count",IV."Count",TR."TotalSaleAmount",TF."totalBonus",dac."ActualAmt"
ORDER BY S.employeeName ASC')
Best Answer
Doing aggregations of remote data in a SQL Server view is going to be expensive, because SQL Server will have to first copy the un-aggregated data to the SQL Server (in memory/tempdb) before it can do the aggregation you want.
If at all possible, do the aggregation on the PostgreSQL side. Create a view in the Postgres DB that does any GROUP BYs you need, and then select from that view on the SQL Server side.