Multiply an aggregate function SUM with a column? - sql

Is there a way to multiply an aggregate function? I need to multiply SUM(ma.gewight) for every article (an article is, for example, H114972 which is iron and is 32,1 meters) so the GROUP BY groups all same articles and for every different article I need to multiply a different number (the column that I am using to multiply is e.best_wert which is the meters, mentioned above). So basically I need to multiply the SUM(ma.gewicht) with e.best_wert - but it doesn't work.
PS. Gewicht = weight
PSS. e.best_wert = weight value/meters
SELECT m.artikel, COUNT(ei.bestell_po) bestell_po_menge, SUM(ma.gewicht) AS summe_gewicht--, SUM(e.best_wert*ma.gewicht) AS summe_kg
FROM MATV030 m
INNER JOIN EINV030 e ON e.BESTELL_NR = m.BESTELL_NR
INNER JOIN EINV035 ei ON e.bestell_nr = ei.bestell_nr
INNER JOIN MATV010 ma ON m.ARTIKEL = ma.ARTIKEL
WHERE e.lieferant = '6000176' AND m.menge_buch <> 0
--AND m.artikel = 'H114972'
AND m.bs = 'WE' AND m.budatum >= '20190101' AND m.budatum < '20190201'
GROUP BY m.artikel, ma.gewicht
ORDER BY m.artikel ASC

Try like this
SELECT m.artikel, COUNT(ei.bestell_po) bestell_po_menge, SUM(ma.gewicht) AS summe_gewicht, (e.best_wert * SUM(ma.gewicht)) AS summe_kg
FROM MATV030 m
INNER JOIN EINV030 e ON e.BESTELL_NR = m.BESTELL_NR
INNER JOIN EINV035 ei ON e.bestell_nr = ei.bestell_nr
INNER JOIN MATV010 ma ON m.ARTIKEL = ma.ARTIKEL
WHERE e.lieferant = '6000176' AND m.menge_buch <> 0
--AND m.artikel = 'H114972'
AND m.bs = 'WE' AND m.budatum >= '20190101' AND m.budatum < '20190201'
GROUP BY m.artikel, e.best_wert
ORDER BY m.artikel ASC

Try this,
;With CTE as
(
SELECT ma.ARTIKEL, SUM(ma.gewicht) AS summe_gewicht--, SUM(e.best_wert*ma.gewicht) AS summe_kg
FROM MATV030 m
INNER JOIN MATV010 ma ON m.ARTIKEL = ma.ARTIKEL
WHERE m.menge_buch > 0
--AND m.artikel = 'H114972'
AND m.bs = 'WE' AND m.budatum >= '20190101' AND m.budatum < '20190201'
GROUP BY m.ARTIKEL
--ORDER BY m.artikel ASC
)
SELECT m.artikel, COUNT(ei.bestell_po) bestell_po_menge,summe_gewicht, summe_gewicht*e.best_wert AS summe_kg
FROM CTE C
inner join MATV030 m on m.artikel=c.artikel
INNER JOIN EINV030 e ON e.BESTELL_NR = m.BESTELL_NR
INNER JOIN EINV035 ei ON e.bestell_nr = ei.bestell_nr
WHERE e.lieferant = '6000176'
GROUP BY m.artikel
ORDER BY m.artikel ASC

You can create A CTE (Common Table Expression) table with your aggregate values and then multiply both the tables using CTE.

Related

Query optimization for multiple inner joins and sub-query

I need help regarding query optimization of the below query.
SELECT pr.todate , pr.descr, cmp.company_id
FROM employee AS emp
INNER JOIN company AS cmp ON emp.emp_comp_id = cmp.company_id
INNER JOIN profile AS pr ON emp.acca_id = pr.profile_id
INNER JOIN acondition ON as_id = as_ac_id
WHERE as_closed = 0
AND (pr.ac_act_id = 20)
AND (pr.todate = (SELECT MIN(todate) AS Expr1
FROM profile pro
INNER JOIN employee empl ON empl.acca_id = pro.profile_id
JOIN acondition ON as_id = as_ac_id
WHERE (pro.ac_act_id = 20
AND empl.emp_comp_id = cmp.company_id)
AND as_closed = 0))
Since there are duplicate joins in the main query and sub query, is there any way to remove those joins in the subquery?
Since, as you clarified, your sub-query is almost identical to your main query you might be able to use the window function RANK as a filter condition. RANK assigns the same number to ties, meaning if multiple records per company match you will get them all e.g.
SELECT todate, descr, company_id
FROM (
SELECT pr.todate, pr.descr, cmp.company_id
, RANK() OVER (PARTITION BY cmp.company_id ORDER BY pr.todate ASC) RankNumber
FROM employee AS emp
INNER JOIN company AS cmp ON emp.emp_comp_id = cmp.company_id
INNER JOIN profile AS pr ON emp.acca_id = pr.profile_id
INNER JOIN acondition ON as_id = as_ac_id
WHERE as_closed = 0 AND pr.ac_act_id = 20
) X
where RankNumber = 1;
Does this work for you?
SELECT ca.todate , pr.descr, cmp.company_id
FROM employee AS emp
INNER JOIN company AS cmp ON emp.emp_comp_id = cmp.company_id
CROSS APPLY (
SELECT TOP(1) pr.todate
FROM profile pr
INNER JOIN acondition ON as_id = as_ac_id
WHERE emp.acca_id = pr.profile_id AND (pr.ac_act_id = 20) AND as_closed = 0
ORDER BY pr.todate ASC
) AS ca

Trying "over partition by" and its not working

This works:
select top 100 cti.tradingitemid, pe.pricingDate, pe.priceClose, pe.adjustmentFactor, cc.countryId, cc.companyId
from ciqcompany cc
join ciqfinperiod cfp on cfp.companyid=cc.companyid
join ciqfininstance cfi on cfi.financialperiodid=cfp.financialperiodid
join ciqsecurity cs on cs.companyid = cc.companyid
join ciqtradingitem cti on cti.securityid=cs.securityid
join ciqPriceEquity pe on pe.tradingItemId=cti.tradingItemId
where pe.pricingDate >= '1974-12-31 00:00:00.000' and pe.pricingDate <'1975-02-01 00:00:00.000' and cc.countryId = 213
order by cc.companyId
The problem is that I get hundreds of lines for the same companyID. I just want one per companyID, the first one would work fine.
I've been looking around and this is the best I could come up with:
select *
FROM (
Select cti.tradingitemid,
pe.pricingDate,
pe.priceClose,
pe.adjustmentFactor,
cc.countryId
Row_number() OVER (PARTITION BY cc.companyId ORDER BY pe.pricingDate) RN
from ciqcompany cc
join ciqfinperiod cfp on cfp.companyid=cc.companyid
join ciqfininstance cfi on cfi.financialperiodid=cfp.financialperiodid
join ciqsecurity cs on cs.companyid = cc.companyid
join ciqtradingitem cti on cti.securityid=cs.securityid
join ciqPriceEquity pe on pe.tradingItemId=cti.tradingItemId
where pe.pricingDate >= '1974-12-31 00:00:00.000' and pe.pricingDate <'1975-02-01 00:00:00.000' and cc.countryId = 213
order by cc.companyId
)
Where rn = 1
The syntax at Row_number() is throwing it off, but to me it looks like other examples I see.
Any ideas would be much appreciated.
Would this work?
SELECT *
FROM (
.....
---- missing comma after cc.countryId
---- remove order by
) A
WHERE
A.RN = 1
ORDER BY cc.companyId
Three things:
Your subquery needs a table alias.
The subquery cannot contain order by.
And you need a comma before row_number().
So:
select t.*
from ( Select cti.tradingitemid,
pe.pricingDate,
pe.priceClose,
pe.adjustmentFactor,
cc.countryId,
Row_number() OVER(PARTITION BY cc.companyId ORDER BY pe.pricingDate) RN
from ciqcompany cc
join ciqfinperiod cfp on cfp.companyid=cc.companyid
join ciqfininstance cfi on cfi.financialperiodid=cfp.financialperiodid
join ciqsecurity cs on cs.companyid = cc.companyid
join ciqtradingitem cti on cti.securityid=cs.securityid
join ciqPriceEquity pe on pe.tradingItemId=cti.tradingItemId
where pe.pricingDate >= '1974-12-31' and pe.pricingDate < '1975-02-01' and
cc.countryId = 213
) t
order by cc.companyId;
I removed the time component from the dates. It is not needed for the logic.

UPDATE all records from existing SELECT query

I have query to select data from related tables.
SELECT
s.id,
CASE
WHEN count(DISTINCT e.id) <> 0
THEN count(DISTINCT o.id) / count(DISTINCT e.id)
END OrdersAverageNumber
FROM
[School] s
JOIN
[SchoolStore] ss ON ss.SchoolId = s.Id
JOIN
[Event] e ON e.SchoolId = ss.SchoolId
AND e.IsDeleted = 0
AND e.Status = 1
AND e.Date >= #startDate
AND e.Date <= #endDate
JOIN
[Order] o ON o.EventId = e.id
AND o.OrderStatus = 1
AND o.CreatedDate >= #startDate
AND o.CreatedDate <= #endDate
GROUP BY
s.id;
But I can't understand what I need to change to update all OrdersAverageNumber records in School table with values from selection above.
You can use update:
with q as (< your query here >)
update s
set OrdersAverageNumber = q.OrdersAverageNumber
from school s join
q
on s.id = q.id;

Query for logistic regression, multiple where exists

A logistic regression is a composed of a uniquely identifying number, followed by multiple binary variables (always 1 or 0) based on whether or not a person meets certain criteria. Below I have a query that lists several of these binary conditions. With only four such criteria the query takes a little longer to run than what I would think. Is there a more efficient approach than below? Note. tblicd is a large table lookup table with text representations of 15k+ rows. The query makes no real sense, just a proof of concept. I have the proper indexes on my composite keys.
select patient.patientid
,case when exists
(
select c.patientid from tblclaims as c
inner join patient as p on p.patientid=c.patientid
and c.admissiondate = p.admissiondate
and c.dischargedate = p.dischargedate
where patient.patientid = p.patientid
group by c.patientid
having count(*) > 1000
)
then '1' else '0'
end as moreThan1000
,case when exists
(
select c.patientid from tblclaims as c
inner join patient as p on p.patientid=c.patientid
and c.admissiondate = p.admissiondate
and c.dischargedate = p.dischargedate
where patient.patientid = p.patientid
group by c.patientid
having count(*) > 1500
)
then '1' else '0'
end as moreThan1500
,case when exists
(
select distinct picd.patientid from patienticd as picd
inner join patient as p on p.patientid= picd.patientid
and picd.admissiondate = p.admissiondate
and picd.dischargedate = p.dischargedate
inner join tblicd as t on t.icd_id = picd.icd_id
where t.descrip like '%diabetes%' and patient.patientid = picd.patientid
)
then '1' else '0'
end as diabetes
,case when exists
(
select r.patientid, count(*) from patient as r
where r.patientid = patient.patientid
group by r.patientid
having count(*) >1
)
then '1' else '0'
end
from patient
order by moreThan1000 desc
I would start by using subqueries in the from clause:
select q.patientid, moreThan1000, moreThan1500,
(case when d.patientid is not null then 1 else 0 end),
(case when pc.patientid is not null then 1 else 0 end)
from patient p left outer join
(select c.patientid,
(case when count(*) > 1000 then 1 else 0 end) as moreThan1000,
(case when count(*) > 1500 then 1 else 0 end) as moreThan1500
from tblclaims as c inner join
patient as p
on p.patientid=c.patientid and
c.admissiondate = p.admissiondate and
c.dischargedate = p.dischargedate
group by c.patientid
) q
on p.patientid = q.patientid left outer join
(select distinct picd.patientid
from patienticd as picd inner join
patient as p
on p.patientid= picd.patientid and
picd.admissiondate = p.admissiondate and
picd.dischargedate = p.dischargedate inner join
tblicd as t
on t.icd_id = picd.icd_id
where t.descrip like '%diabetes%'
) d
on p.patientid = d.patientid left outer join
(select r.patientid, count(*) as cnt
from patient as r
group by r.patientid
having count(*) >1
) pc
on p.patientid = pc.patientid
order by 2 desc
You can then probably simplify these subqueries more by combining them (for instance "p" and "pc" on the outer query can be combined into one). However, without the correlated subqueries, SQL Server should find it easier to optimize the queries.
Example of left joins as requested...
SELECT
patientid,
ISNULL(CondA.ConditionA,0) as IsConditionA,
ISNULL(CondB.ConditionB,0) as IsConditionB,
....
FROM
patient
LEFT JOIN
(SELECT DISTINCT patientid, 1 as ConditionA from ... where ... ) CondA
ON patient.patientid = CondA.patientID
LEFT JOIN
(SELECT DISTINCT patientid, 1 as ConditionB from ... where ... ) CondB
ON patient.patientid = CondB.patientID
If your Condition queries only return a maximum one row, you can simplify them down to
(SELECT patientid, 1 as ConditionA from ... where ... ) CondA

mysql query to three tables, want to use JOIN instead subquery

I want to use join instead subquery to find the trade id not exist on trade_log filtered by ip and current date for mysql syntax below.
SELECT plug.id as a,
plug.url as b,
trade.id as c
FROM plug, trade
WHERE trade.id = plug.trade_id
AND trade.id NOT IN (SELECT trade_log.trade_id
FROM trade_log
WHERE trade_log.ip = '".$ip."'
AND trade_log.log_date = CURDATE())
AND trade.status = 1
AND plug.status = 1
ORDER BY plug.weight DESC
LIMIT 1
Please help me...
Use:
SELECT p.id as a,
p.url as b,
t.id as c
FROM PLUG p
JOIN TRADE t ON t.id = p.trade_id
AND t.status = 1
LEFT JOIN TRADE_LOG tl ON tl.trade_id = t.id
AND tl.ip = mysql_real_escape_string($ip)
AND tl.log_date = CURDATE()
WHERE p.status = 1
AND tl.ip IS NULL
ORDER BY p.weight DESC
LIMIT 1
Same as OMG Ponies:
SELECT p.id as a,
p.url as b,
t.id as c
FROM plug p
INNER JOIN trade t ON (t.id = p.trade_id AND t.status = 1)
LEFT JOIN trade_log l ON (l.trade_id = t.id AND l.ip = '".$ip."' AND l.log_date = CURDATE())
WHERE p.status = 1 AND l.trade_id IS NULL
ORDER BY p.weight DESC
LIMIT 1