Sum of a subquery? - sql

Background:
We are doing a price adjustment in our database. The price adjustment is based on the bill of materials, and what each materials' new cost is. So I have to select the item from the item table, its BOM, and the new price table, and adjust the price based on the total of the materials it takes to make each item. Items are comprised of varying quantities of varying materials, so the master query will call this subquery to get the total adjustment:
select
ta.Code,
ta.Quantity,
(select tb.cost - tb.NewCost * ta.Quantity ) as BOMEffect
from BOM TA
inner join ITEM TC on tC.ItemCode = tA.Father
inner join NewPriceTable TB on tA.Code = tB.Item
where TA.Father = '100-01'
and tc.PriceList = 3
order by ta.Father
Which gives me the results (this is all of the materials of one item, quantity of each material, and the price adjustment):
Code Quantity BOMEffect
D .003 56.000000 -95.08
D .004 28.000000 -62
D .005 20.000000 -54.6
d .006 2.000000 -3.3
D .01 2.000000 -5.5
D .015 4.000000 -25.52
D .02 4.000000 -34
All I need from this query is the total BOMEffect (-280). But if I just replace the select statement with:
select
sum((select tb.cost - tb.NewCost * ta.Quantity )) as BOMEffect
from BOM TA
inner join ITEM TC on tC.ItemCode = tA.Father
inner join NewPriceTable TB on tA.Code = tB.Item
where TA.Father = '100-01'
and tc.PriceList = 3
order by ta.Father
I get:
Cannot perform an aggregate function on an expression containing an aggregate or a subquery.
I know what it's saying, but I don't know any other way to do it. How else can I get this total? This is a small subquery that is part of a larger query, so I only need to return the total. I could probably use a view or a temp table, but I would like to stay away from that if possible.

remove the select inside the sum and order by
select
sum(tb.cost - tb.NewCost * ta.Quantity) as BOMEffect
from BOM TA
inner join ITEM TC on tC.ItemCode = tA.Father
inner join NewPriceTable TB on tA.Code = tB.Item
where TA.Father = '100-01'
and tc.PriceList = 3

If you want to include Code and Quantity you would have to include those in the GROUP BY clause:
select
ta.Code,
ta.Quantity,
sum(tb.cost - tb.NewCost * ta.Quantity) as BOMEffect
from BOM TA
inner join ITEM TC on tC.ItemCode = tA.Father
inner join NewPriceTable TB on tA.Code = tB.Item
where TA.Father = '100-01'
and tc.PriceList = 3
GROUP BY ta.Code, ta.Quantity

Related

having an and condition on multiple rows - sql server

i have the following query where i'm trying to select one value based on a condition
select distinct tblItemTypePropValue.ItemType_Fkey, tblItemPrice.price, dbo.fnGetDiscountItemType(tblItemTypePropValue.ItemType_Fkey) as finalPrice
from tblItemTypePropValue
inner join tblItemPrice
on tblItemTypePropValue.ItemType_Fkey = tblItemPrice.itemType_fkey
where Value_Fkey in (2097,2131)
and tblitemtypepropvalue.ItemType_Fkey in (select id from tblItemType where Item_Fkey = 12241)
the problem is i n the where in value_fkey in (2097,2131)
is there a way to write an and condition on multiple rows where value_fkey = 2097 and = 2131.
and also, can it be done without a join since my parameters inside the in can vary in count.
sample result
itemtype_fkey price finalPrice
6191 100 93
so the query will only return the ItemType_Fkey that has both Value_Fkeys specified in the query
Value
thanks,
You can do two INNER JOINs and check for the existence of the 2097 and 2131 keys on them.
Being INNER JOINS, only the Prices having both of those keys will be returned.
select distinct P.ItemType_Fkey, P.price,
dbo.fnGetDiscountItemType(P.ItemType_Fkey) as finalPrice
from tblItemType T
inner join tblItemPrice P on P.ItemType_Fkey = T.id
inner join tblItemTypePropValue V1 on V1.ItemType_Fkey = P.ItemType_Fkey and V1.Value_Fkey = 2097
inner join tblItemTypePropValue V2 on V2.ItemType_Fkey = P.ItemType_Fkey and V2.Value_Fkey = 2104
where T.Item_Fkey = 12241
select distinct tblItemTypePropValue.Value_Fkey,tblItemTypePropValue.ItemType_Fkey, tblItemPrice.price, dbo.fnGetDiscountItemType(tblItemTypePropValue.ItemType_Fkey) as finalPrice
from tblItemTypePropValue
inner join tblItemPrice
on tblItemTypePropValue.ItemType_Fkey = tblItemPrice.itemType_fkey
where ((Value_Fkey = 2097) or (Value_Fkey = 2131))
and tblitemtypepropvalue.ItemType_Fkey in (select id from tblItemType where Item_Fkey = 12241)
You probably want those items where rows for both Value_Fkey exist like this
SELECT
v.ItemType_Fkey,
max(p.price), -- max/min/avg?
dbo.fnGetDiscountItemType(v.ItemType_Fkey) AS finalPrice
FROM tblItemTypePropValue AS v
INNER JOIN tblItemPrice AS p
ON v.ItemType_Fkey = p.itemType_fkey
WHERE Value_Fkey IN (2097,2131) --- list of values
AND v.ItemType_Fkey
IN ( SELECT id
FROM tblItemType
WHERE Item_Fkey = 12241
)
GROUP BY v.ItemType_Fkey
HAVING COUNT(DISTINCT Value_Fkey) = 2 -- number of values in list

altering query in db2 to fix count from a join

I'm getting an aggregated count of records for orders and I'm getting the expected count on this basic query:
SELECT
count(*) as sales_180,
180/count(*) as velocity
FROM custgroup g
WHERE g.cstnoc = 10617
AND g.framec = 4847
AND g.covr1c = 1763
AND g.colr1c = 29
AND date(substr(g.extd1d,1,4)||'-'||substr(g.EXTD1d,5,2)||'-'||substr(g.EXTD1d,7,2) ) between current_Date - 180 DAY AND current_Date
But as soon as I add back in my joins and joined values then my count goes from 1 (which it should be) to over 200. All I need from these joins is the customer ID and the manager number. so even if my count is high, I'm basically just trying to say "for this cstnoc, give me the slsupr and xlsno"
How can I perform this below query without affecting the count? I only want my count (sales_180 and velocity) coming from the custgroup table based on my where clause, but I then just want one value of the xcstno and xslsno based on the cstnoc.
SELECT
count(*) as sales_180,
180/count(*) as velocity,
c.xslsno as CustID,
cr.slsupr as Manager
FROM custgroup g
inner join customers c
on g.cstnoc = c.xcstno
inner join managers cr
on c.xslsno = cr.xslsno
WHERE g.cstnoc = 10617
AND g.framec = 4847
AND g.covr1c = 1763
AND g.colr1c = 29
AND date(substr(g.extd1d,1,4)||'-'||substr(g.EXTD1d,5,2)||'-'||substr(g.EXTD1d,7,2) ) between current_Date - 180 DAY AND current_Date
GROUP BY c.xslsno, cr.slsupr
You are producing multiple rows when joining, so your count is now counting all the resulting rows with all that [unintended] multiplicity.
The solution? Use a table expression to pre-compute your count, and then you can join it to the other tables, as in:
select
g2.sales_180,
g2.velocity,
c.xslsno as CustID,
cr.slsupr as Manager
from customers c
join managers cr on c.xslsno = cr.xslsno
join ( -- here the Table Expression starts
SELECT
count(*) as sales_180,
180/count(*) as velocity
FROM custgroup g
WHERE g.cstnoc = 10617
AND g.framec = 4847
AND g.covr1c = 1763
AND g.colr1c = 29
AND date(substr(g.extd1d,1,4)||'-'||substr(g.EXTD1d,5,2)
||'-'||substr(g.EXTD1d,7,2) )
between current_Date - 180 DAY AND current_Date
) g2 on g2.cstnoc = c.xcstno
You can also use a Common Table Expression (CTE) that will produce the same result:
with g2 as (
SELECT
count(*) as sales_180,
180/count(*) as velocity
FROM custgroup g
WHERE g.cstnoc = 10617
AND g.framec = 4847
AND g.covr1c = 1763
AND g.colr1c = 29
AND date(substr(g.extd1d,1,4)||'-'||substr(g.EXTD1d,5,2)
||'-'||substr(g.EXTD1d,7,2) )
between current_Date - 180 DAY AND current_Date
)
select
g2.sales_180,
g2.velocity,
c.xslsno as CustID,
cr.slsupr as Manager
from customers c
join managers cr on c.xslsno = cr.xslsno
join g2 on g2.cstnoc = c.xcstno

Stock balance from items in Boms

I have Bom table like
ItemCode MatCode Quantity
032490 M10506 3.0
032490 M10507 1.0
032490 M10509 1.0
030515 M10506 2.0
030515 M10508 5.0
045660 M10507 2.0
045660 M10510 3.0
Stock balance is known for assembled items in Item table.
ItemCode StockBalance
032490 10
030515 15
045660 8
M10506
M10507
M10508
M10509
M10510
I need to calculate balance for all materials (MatCode) in Stock as part of assembled products. So in my example for M10506 it would be 10*3+15*2.
I'm stuck with this, looping the query and right now there's only 1 level boms but in future there will be 2 and 3 level boms.
My query:
Select sum(item.StockBalance * bom.Quantity) from item
inner join bom on bom.ItemCode=item.itemcode
Where exists (select * from bom WHERE Item.itemCode = bom.ItemCode)
Group by bom.ItemCode
Your data suggests a join and aggregation:
select b.matCode, sum(b.quantity * i.StockBalance)
from bom b join
item i
on b.itemCode = i.itemCode
group by b.matCode;

How to SUM an AS column in SQL

I have the following code...
SELECT WF.Word, WF.Frequency, WW.Weight, (WF.Frequency * WW.Weight) AS Product
FROM WF
INNER JOIN WW ON WW.Word = WF.word
Which outputs the following...
WORD | FREQUENCY | WEIGHT | PRODUCT
Fat 3 2 6
Ugly 2 4 8
Stupid 1 7 7
I also want to sum the product column at the same time. I understand how to sum an existing column but unsure how to sum a column i'm creating.
SELECT SUM(WF.Frequency * WW.Weight) AS Product
FROM WF
INNER JOIN WW ON WW.Word = WF.word
SELECT WF.Word, SUM(WF.Frequency) Frequency, SUM(WW.Weight) Weight, SUM(WF.Frequency * WW.Weight) AS Product
FROM WF
INNER JOIN WW ON WW.Word = WF.word
GROUP BY WF.Word
;
SELECT SUM(WF.Frequency * WW.Weight) Product
FROM WF
INNER JOIN
WW ON WW.Word = WF.word

Rollup / recursive addition SQL Server 2008

I have a query with rollup that outputs data like (the query is a little busy, but I can post if necessary)
range subCounts Counts percent
1-9 3 100 3.0
10-19 13 100 13.0
20-29 30 100 33.0
30-39 74 100 74.0
NULL 100 100 100.0
How is it possible to keep a running summation total of percent? Say I need to find the bottom 15 percentile, in this case 3+13=16 so I would like for the last row to be returned read
range subCounts counts percent
10-19 13 100 13.0
EDIT1: here the query
select '$'+cast(+bin*10000 + ' ' as varchar(10)) + '-' + cast(bin*10000+9999 as varchar(10)) as bins,
count(*) as numbers,
(select count(distinct patient.patientid) from patient
inner join tblclaims on patient.patientid = tblclaims.patientid
and patient.admissiondate = tblclaims.admissiondate
and patient.dischargedate = tblclaims.dischargedate
inner join tblhospitals on tblhospitals.hospitalnpi = patient.hospitalnpi
where (tblhospitals.hospitalname = 'X')
) as Totals
, round(100*count(*)/cast((select count(distinct patient.patientid) from patient
inner join tblclaims on patient.patientid = tblclaims.patientid
and patient.admissiondate = tblclaims.admissiondate
and patient.dischargedate = tblclaims.dischargedate
inner join tblhospitals on tblhospitals.hospitalnpi = patient.hospitalnpi
where (tblhospitals.hospitalname = 'X')) as float),2) as binsPercent
from
(
select tblclaims.patientid, sum(claimsmedicarepaid) as TotalCosts,
cast(sum(claimsmedicarePaid)/10000 as int) as bin
from tblclaims inner join patient on patient.patientid = tblclaims.patientid
and patient.admissiondate = tblclaims.admissiondate
and patient.dischargedate = tblclaims.dischargedate
inner join tblhospitals on patient.hospitalnpi = tblhospitals.hospitalnpi
where tblhospitals.hospitalname = 'X'
group by tblclaims.patientid
) as t
group by bin with rollup
OK, so for whomever might use this for reference I figured out what I needed to do.
I added row_number() over(bin) as rownum to the query and saved all of this as a view.
Then I used
SELECT *,
SUM(t2.binspercent) AS SUM
FROM t t1
INNER JOIN t t2 ON t1.rownum >= t2.rownum
GROUP BY t1.rownum,
t1.bins, t1.numbers, t1.uktotal, t1.binspercent
ORDER BY t1.rownum
by joining t1.rownum >=t2.rownum you can get the rolling count sort of thing.
This isn't exactly what i was looking for, but it's on the same track:
http://blog.tallan.com/2011/12/08/sql-server-2012-windowing-functions-part-1-of-2-running-and-sliding-aggregates/ and http://blog.tallan.com/2011/12/19/sql-server-2012-windowing-functions-part-2-of-2-new-analytic-functions/ - check out PERCENT_RANK
CUME_DIST
PERCENTILE_CONT
PERCENTILE_DISC
Sorry for the lame answer