SQL: SUM on Aggregate columns - sql

I have this query:
SELECT
spend_codes.spend_code,
SUM(orders.shipping_cost + orders.sales_tax + orders.manual_total + orders.fees) as order_total,
SUM(ordered_items.fees + ordered_items.price * ordered_items.quantity) as item_total
FROM spend_codeables
LEFT JOIN spend_codes
ON spend_codeables.spend_code_id = spend_codes.id
LEFT JOIN orders
ON spend_codeables.spend_codeable_id = orders.id AND spend_codeables.spend_codeable_type = 'App\Order'
LEFT JOIN ordered_items
ON spend_codeables.spend_codeable_id = ordered_items.id AND spend_codeables.spend_codeable_type = 'App\OrderedItem'
GROUP BY
spend_codes.spend_code;
Which has this result:
+--------------+---------------+--------------+
| spend_code | order_total | item_total |
|--------------+---------------+--------------|
| 1230131391 | $362.00 | <null> |
| A12345 | <null> | <null> |
| B29393 | <null> | $374.28 |
+--------------+---------------+--------------+
However, I'd like to add order_total and item_total in order to get just total.
So I'd expect this:
+--------------+---------------+
| spend_code | total |
|--------------+---------------+
| 1230131391 | $362.00 |
| A12345 | <null> |
| B29393 | $374.28 |
+--------------+---------------+
Doing this did not work:
SUM(orders.shipping_cost + orders.sales_tax + orders.manual_total + orders.fees) +
SUM(ordered_items.fees + ordered_items.price * ordered_items.quantity) as total
Another monkeywrench is that the type of the numbers is money not integer.
Anyone would be able to help?

If any of the 2 sums returns NULL then the result of the sum of the sums will also be NULL because NULL + anything returns NULL.
Use SUM() only once:
SUM(
orders.shipping_cost +
orders.sales_tax +
orders.manual_total +
orders.fees +
ordered_items.fees +
ordered_items.price * ordered_items.quantity
) as total
If any of the columns involved may also be NULL use COALESCE() like COALESCE(orders.shipping_cost, 0::money)

you do it in next step using sub query and I used coalesce() for avoid null
with cte as ( SELECT
spend_codes.spend_code,
SUM(orders.shipping_cost + orders.sales_tax + orders.manual_total + orders.fees) as order_total,
SUM(ordered_items.fees + ordered_items.price * ordered_items.quantity) as item_total
FROM spend_codeables
LEFT JOIN spend_codes
ON spend_codeables.spend_code_id = spend_codes.id
LEFT JOIN orders
ON spend_codeables.spend_codeable_id = orders.id AND spend_codeables.spend_codeable_type = 'App\Order'
LEFT JOIN ordered_items
ON spend_codeables.spend_codeable_id = ordered_items.id AND spend_codeables.spend_codeable_type = 'App\OrderedItem'
GROUP BY
spend_codes.spend_code
) select spend_code,coalesce(order_total,0)+coalesce(item_total,0) as total from cte

Related

Join with union returns me wrong result

I need to use union all twice. And then to join them with the same table. First union will contain a where statement and the second it will not.
Problem is that when I use the second table my result is changing.
select sum(x.quantity*x.Price)
from CustomerTrans t1
inner join (
select *
from InventoryTrans
union all
select *
from InventoryTransTemp
) x on t1.TrnDocumentID = x.TrnDocumentID
group by t1.TrnDocumentID
Here is the output from this result
First Result
Then I am adding the second union with the where statement inside
select sum(x.quantity*x.Price), sum(x2.quantity*x2.Price)
from CustomerTrans t1
left join (
select *
from InventoryTrans
union all
select *
from InventoryTransTemp
) x on t1.TrnDocumentID = x.TrnDocumentID
left join (
select *
from InventoryTrans
where printed = 2 or InvoicePaymentID = 2
union all
select *
from InventoryTransTemp
where printed = 2 or InvoicePaymentID = 2
) x2 on t1.TrnDocumentID = x2.TrnDocumentID
group by t1.TrnDocumentID
Here is the second result
Second Result
Second result it should be 3.80 and not 7.60
It look like it multiples my price *2 instead *1.
What happens here is that you join rows you don't want to join. Let's say your first subquery returns
+----------+-------+
| quantity | price |
+----------+-------+
| 10 | 100 |
| 10 | 200 |
+----------+-------+
for a particular document ID. And your second subquery returns only
+----------+-------+
| quantity | price |
+----------+-------+
| 10 | 200 |
+----------+-------+
The joined result is:
+------------+---------+-------------+----------+
| x.quantity | x.price | x2.quantity | x2.price |
+------------+---------+-------------+----------+
| 10 | 100 | 10 | 200 |
| 10 | 200 | 10 | 200 |
+------------+---------+-------------+----------+
And the aggregations results thereafter are:
+----------+-----------+
| x_result | x2_result |
+----------+-----------+
| 3000 | 4000 |
+----------+-----------+
instead of
+----------+-----------+
| x_result | x2_result |
+----------+-----------+
| 3000 | 2000 |
+----------+-----------+
Instead of joining single rows, you want to join aggregation results (the totals per document):
select
ct.*,
coalesce(u1.total, 0) as u1_total,
coalesce(u2.total, 0) as u2_total
from customertrans ct
left join
(
select trndocumentid, sum(quantity * price) as total
from
(
select * from inventorytrans
union all
select * from inventorytranstemp
) union1
group by trndocumentid
) u1 on u1.trndocumentid = ct.trndocumentid
left join
(
select trndocumentid, sum(quantity * price) as total
from
(
select * from inventorytrans where printed = 2 or invoicepaymentid = 2
union all
select * from inventorytranstemp where printed = 2 or invoicepaymentid = 2
) union2
group by trndocumentid
) u2 on u2.trndocumentid = ct.trndocumentid
group by ct.trndocumentid
order by ct.trndocumentid;

How to optimize the SQL query with the subquery?

I want to improve the performance of a SQL query. I have the table 'tblEntries' with the column 'sTag':
+----+------+-------+------+---------+
| Id | sTag | sPath | lVer | bActive |
+====+======+=======+======+=========+
| 1 | NULL | t1 | 100 | + |
| 2 | NULL | t2 | 110 | + |
| 3 | x1 | t4 | 110 | + |
| 4 | x1 | t3 | 120 | + |
| 5 | x2 | t7 | 100 | + |
+----+------+-------+------+---------+
A client queries for the path with the specified tag and the query should return a specified entry with the next condition:
If there is an entry with the specified tag it should returns the
entry with the maximum lVer value and bActive should be TRUE.
If
there is no entry with the specified tag it should returns the entry
with the NULL sTag value and with the maximum lVer value and bActive
should be TRUE.
The "tagged" entry has the more priority over "non-tagged" one.
The current SQL query is:
SELECT lVer, sPath
FROM tblEntries
INNER JOIN
(SELECT MAX(lVer) AS V, sTag AS T
FROM tblEntries
WHERE bActive = TRUE
GROUP BY sTag)
ON lVer = V
WHERE T IS NULL OR T = 'user_tag'
ORDER BY T DESC
Then i can select the first entry which satisfies the conditions.
Can i avoid the subquery?
Thanks!
Depending on your data and database, this might have sufficient performance:
select top (1) e.*
from tblEntries e
where e.bActive = TRUE and
(e.t IS NULL OR e.t = 'user_tag')
order by (e.t desc), -- null values last
t.lver desc;
If speed is really an issue and you have an index on (t, active, lver desc), this might be a bit faster:
(select top (1) e.*
from tblEntries e
where e.t = 'user_tag' and e.bActive = TRUE
order by e.lver desc
) union all
(select top (1) e.*
from tblEntries e
where e.t is null and e.bActive = TRUE and
not exists (select 1 from tblEntries e2 where e2.t = 'user_tag' and e2.bActive = TRUE )
order by e.lver desc
);

Dynamic Columns Pivot returning multiple rows - SQL Server 2014

I am trying to write a PIVOT query on dynamic columns. It works but it is returning multiple rows.
My query is
set #query = 'select PanelID, PanelCode, PanelName, EvaluatorID, PersonID, ApplicationID, ' + #cols + ' from (
select
distinct p.PanelID, p.PanelCode, p.PanelName, e.EvaluatorID, e.PersonID, ppl.LastName, pApps.ApplicationID, ev.AnswerNumeric
from
tblpanels p inner join tblEvaluators e on p.PanelID = e.PanelID
inner join tblPeople ppl on ppl.PersonID = e.PersonID
inner join tblPanelApps pApps on pApps.PanelID = p.PanelID
inner join tblEvaluations ev on ev.ApplicationID = pApps.ApplicationID and ev.EvaluatorID = e.EvaluatorID
where
p.PanelID in (1234, 3656)
)tmp
PIVOT
(
max(AnswerNumeric)
For LastName IN (' + #cols + ')
) As P
Order By PanelID, ApplicationID'
execute(#query)
The output is coming as
+=========+===========+===========+=============+==========+===============+======+=======+
| PanelID | PanelCode | PanelName | EvaluatorID | PersonID | ApplicationID | John | Carol |
+=========+===========+===========+=============+==========+===============+======+=======+
| 1234 | 12123412 | Panel A | 3674 | 4834 | 112233 | 6 | NULL |
+---------+-----------+-----------+-------------+----------+---------------+------+-------+
| 1234 | 12123412 | Panel A | 3674 | 4834 | 112233 | NULL | 4 |
+---------+-----------+-----------+-------------+----------+---------------+------+-------+
It should be one row only per ApplicationID. How can I fix this query for below output?
+=========+===========+===========+=============+==========+===============+======+=======+
| PanelID | PanelCode | PanelName | EvaluatorID | PersonID | ApplicationID | John | Carol | Total
+=========+===========+===========+=============+==========+===============+======+=======+
| 1234 | 12123412 | Panel A | 3674 | 4834 | 112233 | 6 | 4 | 10
+---------+-----------+-----------+-------------+----------+---------------+------+-------+
EDIT: I also need a total score at the end for each row.
I fixed it. The evaluatorID and the PersonID were causing the issue. I took them off from the subquery and it is now fixed. I also added the Total column. The updated query is
set #query = 'select PanelID, PanelCode, PanelName, ApplicationID,
' + #cols + ', (ISNULL(' + REPLACE(#cols, ',', ',0.) + ISNULL(') + ',0.)) AS TOTAL from (
select
distinct p.PanelID, p.PanelCode, p.PanelName, pApps.ApplicationID, ppl.LastName, isnull(ev.AnswerNumeric, 0) as AnswerNumeric
from
tblpanels p inner join tblEvaluators e on p.PanelID = e.PanelID
inner join tblPeople ppl on ppl.PersonID = e.PersonID
inner join tblPanelApps pApps on pApps.PanelID = p.PanelID
left join tblEvaluations ev on ev.ApplicationID = pApps.ApplicationID and ev.EvaluatorID = e.EvaluatorID
where
p.PanelID in (1234, 3656)
)tmp
PIVOT
(
max(AnswerNumeric)
For LastName IN (' + #cols + ')
) As P
Order By PanelID, ApplicationID'
--print #query
execute(#query)

Make single record to multiple records in sql server

I have a records look like below
From two rows, I want to split ShiftPattern values and create multiple records and StartWeek will be created sequentially.
Final Query:
Split ShiftPattern Column and Create multiple records
Increase StartWeek like as 20, 21 to rotation.
Output result
This is what you need. Tested in fiddle.
SQLFiddle Demo
select q.locationid,q.employeeid,
case
when (lag(employeeid,1,null) over (partition by employeeid order by weekshiftpatternid)) is null
then startweek
else startweek + 1
end as rotation ,
q.weekshiftpatternid,
q.shiftyear
from
(
select locationid,employeeid, left(d, charindex(',', d + ',')-1) as weekshiftpatternid ,
startweek,shiftyear
from (
select *, substring(shiftpattern, number, 200) as d from MyTable locationid left join
(select distinct number from master.dbo.spt_values where number between 1 and 200) col2
on substring(',' + shiftpattern, number, 1) = ','
) t
) q
Output
+------------+------------+----------+--------------------+-----------+
| locationid | employeeid | rotation | weekshiftpatternid | shiftyear |
+------------+------------+----------+--------------------+-----------+
| 1 | 10000064 | 20 | 1006 | 2016 |
| 1 | 10000064 | 21 | 1008 | 2016 |
| 1 | 10000065 | 20 | 1006 | 2016 |
| 1 | 10000065 | 21 | 1008 | 2016 |
+------------+------------+----------+--------------------+-----------+
Similar:
In my test table my ID is your EmployeeID or however you want to work it.
SELECT
*,
LEFT(shiftBits, CHARINDEX(',', shiftBits + ',')-1) newShiftPattern,
StartWeek+ROW_NUMBER() OVER(PARTITION BY ID ORDER BY shiftBits ) as newStartWeek
FROM
(
SELECT
SUBSTRING(shiftPattern, number, LEN(shiftPattern)) AS shiftBits,
test2.*
FROM
test2,master.dbo.spt_values
WHERE
TYPE='P' AND number<LEN(shiftPattern)
AND SUBSTRING(',' + shiftPattern, number, 1) = ','
) AS x

COUNT() function and SELECT with DISTINCT on multiple columns

Hoping someone can help me figure why I'm getting a syntax error here. I'm counting all rows that have two distinct column values.
select count (*) as 'Distinct' from (
select distinct p.IDD, p.num
from PERF1 p
inner join MAST1 m
on (m.id_table = p.id_table and m.SOURCE_TABLE = p.SOURCE_TABLE)
where m.DATE > '2012-12-31' )
I'm getting an error on my last close bracket but can't for the life of me figure what I'm doing wrong here.
...............................
Kind of a huge "edit" but I'm a bit stuck. I thought that this query would count the number of distinct rows (for both columns) across this table. So for instance the count should return 6 since "P" and "T" are repeated. I thought I had it with this:
select count (*) as 'Distinct' from (
select distinct p.IDD, p.co
from PERF1 p
inner join MAST1 m
on (m.id_table = p.id_table and m.SOURCE = p.SOURCE)
where m.DATE > '2012-12-31' ) TempTable
but I'm getting a much higher number than I think I should so I'm hoping that my query is incorrect.
+------+------+
| IDD | CO |
+------+------+
| 11 | P |
| 12 | P |
| 13 | T |
| 14 | T |
| 15 | R |
| 16 | S |
| 17 | U |
| 18 | K |
You need to add a alias name for the subquery
select count (*) as 'Distinct' from (
select distinct p.IDD, p.num
from PERF1 p
inner join MAST1 m
on (m.id_table = p.id_table and m.SOURCE_TABLE = p.SOURCE_TABLE)
where m.DATE > '2012-12-31' ) TempTable