Update with inner join not working in db2 - sql

I have a structure similar to a working query in mysql that is an update based on an inner join with counts
update schema.daily_totals ct
inner JOIN (
SELECT
COUNT (*) AS contacted,
SUM( CASE WHEN f.follow_up_date BETWEEN CURRENT_DATE AND CURRENT_DATE + 7 DAYS THEN 1 ELSE 0 END ) AS potentials,
CAST (ROUND((SUM( CASE WHEN f.follow_up_date BETWEEN CURRENT_DATE AND CURRENT_DATE + 7 DAYS THEN 1.0 ELSE 0 END )/ COUNT (*)) * 100.00, 2) AS DECIMAL (12, 2)) AS PERCENT,
u.user_id as userID,
FROM schema.users u
INNER JOIN schema.notated n
ON n.user_identifier = u.user_id
INNER JOIN schema.comms m
ON n.comms_ID = m.comms_ID
LEFT JOIN schema.FDates f
ON f.dNumber = n.dNumber
WHERE code <> 'none'
AND n.created_at >= CURRENT_DATE - 1 DAYS
GROUP BY u.user_id, u.first_name, u.last_name
) as cu
on cu.userID = ct.ext_id
set ct.contacted_contacted = cu.contacted,
ct.percent_up_to_date = cu.percent
where ct.date_of_report >= current_date;
But it won't run, it seems to break around the final 'on' where I'm joining on the subquery.
Am I not able to run this in db2 at all?

Use MERGE statement instead.
MERGE INTO schema.daily_totals ct
USING (
SELECT
COUNT (*) AS contacted,
SUM( CASE WHEN f.follow_up_date BETWEEN CURRENT_DATE AND CURRENT_DATE + 7 DAYS THEN 1 ELSE 0 END ) AS potentials,
CAST (ROUND((SUM( CASE WHEN f.follow_up_date BETWEEN CURRENT_DATE AND CURRENT_DATE + 7 DAYS THEN 1.0 ELSE 0 END )/ COUNT (*)) * 100.00, 2) AS DECIMAL (12, 2)) AS PERCENT,
u.user_id as userID,
FROM schema.users u
INNER JOIN schema.notated n
ON n.user_identifier = u.user_id
INNER JOIN schema.comms m
ON n.comms_ID = m.comms_ID
LEFT JOIN schema.FDates f
ON f.dNumber = n.dNumber
WHERE code <> 'none'
AND n.created_at >= CURRENT_DATE - 1 DAYS
GROUP BY u.user_id, u.first_name, u.last_name
) as cu
on cu.userID = ct.ext_id and ct.date_of_report >= current_date
WHEN MATCHED THEN UPDATE
set contacted_contacted = cu.contacted, percent_up_to_date = cu.percent;

Related

How to apply filter using a joined table

I'm trying to apply a filter to my query (accounts.provider = 'z') using the accounts table. The query I have at the moment is not applying the filter correctly, the full list of payments is being added up, regardless of the provider condition. The reason why I'm using table x to join the accounts table is because table t doesn't have the account_id column to allow me to join it with the accounts table.
This is my current query
SELECT
distinct on (x.day) x.day,
coalesce(pending_payments,0)
from
(( SELECT day::date
FROM generate_series(timestamp '2017-03-13', current_date + interval '1 week', interval '1 day') day
) d
left JOIN (
SELECT date_trunc('day', payment_date)::date AS day,
sum(case when payment_amount > 0
and description not ilike '%credit%'
and state = 'pending'
then payment_amount end) as pending_payments
FROM payments
GROUP BY 1
) t USING (day) inner join payments on payments.payment_date = t.day) x
inner join accounts on accounts.id = x.account_id and accounts.provider = 'z'
where day <= current_date + interval '1 week'
and day >= current_date - interval'6 months'
ORDER BY x.day desc
Thanks for your help
Updated query based on suggestions in the comments but it's not producing the right outcome (see comments).
SELECT
distinct on (t.day) t.day as day,
coalesce(pending_payments,0)
from
( SELECT day::date
FROM generate_series(timestamp '2017-03-13', current_date + interval '1 week', interval '1 day') day
) d
left JOIN (
SELECT date_trunc('day', t.payment_date)::date AS day,
sum(case when t.payment_amount > 0
and t.description not ilike '%credit%'
and t.state = 'success'
then t.payment_amount end) as pending_payments
FROM payments t
inner join payments p on p.payment_date = date_trunc('day', t.payment_date)::date
inner join accounts on accounts.id = p.account_id and accounts.provider = 'z'
where date_trunc('day', t.payment_date)::date <= current_date + interval '1 week'
and date_trunc('day', t.payment_date)::date >= current_date - interval'1 months'
GROUP BY 1
) t USING (day)
ORDER BY day desc
You are calculating the pending_payments (In sub-query) before applying the accounts.provider = 'z' condition.
You should replace this code:
....
....
left JOIN (
SELECT date_trunc('day', payment_date)::date AS day,
sum(case when payment_amount > 0
and description not ilike '%credit%'
and state = 'pending'
then payment_amount end) as pending_payments
FROM payments
GROUP BY 1
) t USING (day) inner join payments on payments.payment_date = t.day) x
inner join accounts on accounts.id = x.account_id and accounts.provider = 'z'
....
....
with
....
....
left JOIN (
SELECT date_trunc('day', t.payment_date)::date AS day,
sum(case when t.payment_amount > 0
and t.description not ilike '%credit%'
and t.state = 'pending'
then t.payment_amount end) as pending_payments
FROM payments t
inner join payments p on p.payment_date = date_trunc('day', t.payment_date)::date
inner join accounts on accounts.id = p.account_id and accounts.provider = 'z'
GROUP BY 1
) t
....
....

Problem with a query in Postgres that I want to move to SQL Server

I have the following query in postgres that I now need to run on SQL Server. Obviously I have already changed the trunc per round and basic things, but mainly I have a problem in the principle select distinct on (c.cod_socio) tbl. * Since SQL Server does not recognize that syntax.
select distinct on (c.cod_socio)
tbl.*, h.cod_oficina, h.cod_transaccion, h.num_transaccion,
h.num_sec, h.fec_movimiento
from
sgf_det_mov_his h
inner join
sgf_cuenta c on c.cod_producto = h.cod_producto and c.cod_cuenta = h.cod_cuenta
inner join
sgf_tran t on t.cod_transaccion = h.cod_transaccion and t.cod_oficina = h.cod_oficina and t.cod_tipo_transaccion in ('DA', 'DP','NC')
inner join
(select
sgf_cuenta.cod_socio,
sum(trunc(sgf_det_mov_his.val_efectivo, 0) + trunc(sgf_det_mov_his.val_cheques, 0)) as total
from
sgf_det_mov_his, sgf_cuenta, sgf_tran
where
sgf_cuenta.cod_producto = sgf_det_mov_his.cod_producto
and sgf_cuenta.cod_cuenta = sgf_det_mov_his.cod_cuenta
and sgf_det_mov_his.sts_mov = 'A'
and sgf_tran.cod_transaccion = sgf_det_mov_his.cod_transaccion
and sgf_tran.cod_oficina = sgf_det_mov_his.cod_oficina
and sgf_cuenta.cod_producto <> 2
and sgf_tran.cod_tipo_transaccion in ('DA', 'DP','NC')
and isnull(sgf_tran.cod_uaf, 0) > 0
and isnull(sgf_tran.cod_uaf, 0) not in (71)
and sgf_cuenta.cod_cuenta not in (select cod_cuenta
from sgf_credito
where sgf_credito.cod_producto = sgf_cuenta.cod_producto
and sgf_credito.cod_cuenta = sgf_cuenta.cod_cuenta
and sts_operacion in ('A'))
and date(sgf_det_mov_his.fec_movimiento) between '2015-01-01' and '2019-01-01'
group by
sgf_cuenta.cod_socio
having
sum(trunc(sgf_det_mov_his.val_efectivo,0) + trunc(sgf_det_mov_his.val_cheques,0)) >= 5000) tbl on tbl.cod_socio = c.cod_socio
where
date(h.fec_movimiento) between '2015-01-01' and '2019-01-01'
order by
c.cod_socio, h.fec_movimiento desc
distinct on (...) simply retains the "first row" which may be emulated using row_number() over(...) and a following where clause predicate that limits to one row per partition.. Note that distinct on relies on the order by clause to decide the "first row", so you need the equivalent conditions in the over clause. Also note if you after greater query compatibility between the two databases you could use the same row_number approach in PostgreSQL.
SELECT
*
FROM (
SELECT
ROW_NUMBER() OVER (PARTITION BY c.cod_socio ORDER BY h.fec_movimiento DESC) AS rn
, c.cod_socio
, tbl.*
, h.cod_oficina
, h.cod_transaccion
, h.num_transaccion
, h.num_sec
, h.fec_movimiento
FROM sgf_det_mov_his h
INNER JOIN sgf_cuenta c ON c.cod_producto = h.cod_producto
AND c.cod_cuenta = h.cod_cuenta
INNER JOIN sgf_tran t ON t.cod_transaccion = h.cod_transaccion
AND t.cod_oficina = h.cod_oficina
AND t.cod_tipo_transaccion IN ('DA', 'DP', 'NC')
INNER JOIN (
SELECT
sgf_cuenta.cod_socio
, SUM(trunc(sgf_det_mov_his.val_efectivo, 0) + trunc(sgf_det_mov_his.val_cheques, 0)) AS total
FROM sgf_det_mov_his
, sgf_cuenta
, sgf_tran
WHERE sgf_cuenta.cod_producto = sgf_det_mov_his.cod_producto
AND sgf_cuenta.cod_cuenta = sgf_det_mov_his.cod_cuenta
AND sgf_det_mov_his.sts_mov = 'A'
AND sgf_tran.cod_transaccion = sgf_det_mov_his.cod_transaccion
AND sgf_tran.cod_oficina = sgf_det_mov_his.cod_oficina
AND sgf_cuenta.cod_producto <> 2
AND sgf_tran.cod_tipo_transaccion IN ('DA', 'DP', 'NC')
AND ISNULL(sgf_tran.cod_uaf, 0) > 0
AND ISNULL(sgf_tran.cod_uaf, 0) NOT IN (71)
AND sgf_cuenta.cod_cuenta NOT IN (
SELECT
cod_cuenta
FROM sgf_credito
WHERE sgf_credito.cod_producto = sgf_cuenta.cod_producto
AND sgf_credito.cod_cuenta = sgf_cuenta.cod_cuenta
AND sts_operacion IN ('A')
)
AND DATE(sgf_det_mov_his.fec_movimiento) BETWEEN '2015-01-01' AND '2019-01-01'
GROUP BY
sgf_cuenta.cod_socio
HAVING SUM(trunc(sgf_det_mov_his.val_efectivo, 0) + trunc(sgf_det_mov_his.val_cheques, 0)) >= 5000
) tbl ON tbl.cod_socio = c.cod_socio
WHERE DATE(h.fec_movimiento) BETWEEN '2015-01-01' AND '2019-01-01'
) AS d
WHERE d.rn = 1
ORDER BY
d.cod_socio
, d.fec_movimiento DESC

Sum of resulting set of rows in SQL

I've got the following query:
SELECT DISTINCT CU.permit_id, CU.month, /*CU.year,*/ M.material_id, M.material_name, /*MC.chemical_id, C.chemical_name,
C.precursor_organic_compound, C.non_precursor_organic_compound,*/
/*MC.chemical_percentage,*/
POC_emissions =
CASE
WHEN (C.precursor_organic_compound = 'true')
THEN (CU.chemical_usage_lbs / CU.material_density) * M.VOC
ELSE 0
END,
NON_POC_emissions =
CASE
WHEN (C.non_precursor_organic_compound = 'true')
THEN CU.chemical_usage_lbs * (MC.chemical_percentage / 100)
ELSE 0
END
FROM material M
LEFT OUTER JOIN material_chemical MC ON MC.material_id = M.material_id
LEFT OUTER JOIN chemical_usage CU ON CU.material_id = MC.material_id
LEFT OUTER JOIN chemical C ON C.chemical_id = MC.chemical_id
WHERE (CU.month >=1 AND CU.month <= 2)
AND CU.year = 2013
AND M.material_id = 52
--AND CU.permit_id = 2118
--GROUP BY CU.permit_id, M.material_id, M.material_name, CU.month, MC.chemical_id, MC.chemical_id, C.chemical_name, C.precursor_organic_compound, C.non_precursor_organic_compound
--ORDER BY C.chemical_name ASC
Which returns:
But what I need is to return one row per month per material adding up the values of POC per month and NON_POC per month.
So, I should end up with something like:
Month material_id material_name POC NON_POC
1 52 Krylon... 0.107581 0.074108687
2 52 Krylon... 0.143437 0.0988125
I tried using SUM but it sums up the same result multiple times:
SELECT /*DISTINCT*/ CU.permit_id, CU.month, /*CU.year,*/ M.material_id, M.material_name, /*MC.chemical_id, C.chemical_name,
C.precursor_organic_compound, C.non_precursor_organic_compound,*/
--MC.chemical_percentage,
POC_emissions = SUM(
CASE
WHEN (C.precursor_organic_compound = 'true')
THEN (CU.chemical_usage_lbs / CU.material_density) * M.VOC
ELSE 0
END),
NON_POC_emissions = SUM(
CASE
WHEN (C.non_precursor_organic_compound = 'true')
THEN CU.chemical_usage_lbs * (MC.chemical_percentage / 100)
ELSE 0
END)
FROM material M
LEFT OUTER JOIN material_chemical MC ON MC.material_id = M.material_id
LEFT OUTER JOIN chemical_usage CU ON CU.material_id = MC.material_id
LEFT OUTER JOIN chemical C ON C.chemical_id = MC.chemical_id
WHERE M.material_id = 52
--AND CU.permit_id = 187
AND (CU.month >=1 AND CU.month <= 2)
AND CU.year = 2013
GROUP BY CU.permit_id, M.material_id, M.material_name, CU.month/*, CU.year, MC.chemical_id, C.chemical_name, C.precursor_organic_compound, C.non_precursor_organic_compound*/
--ORDER BY C.chemical_name ASC
The first query has a DISTINCT clause. What is the output without the DISTINCT clause. I suspect you have more rows than shows in your screenshot.
Regardless, you could try something like this to get the desired result.
select permit_id, month, material_id, material_name,
sum(poc_emissions), sum(non_poc_emissions)
from (
SELECT DISTINCT CU.permit_id, CU.month, M.material_id, M.material_name,
POC_emissions =
CASE
WHEN (C.precursor_organic_compound = 'true')
THEN (CU.chemical_usage_lbs / CU.material_density) * M.VOC
ELSE 0
END,
NON_POC_emissions =
CASE
WHEN (C.non_precursor_organic_compound = 'true')
THEN CU.chemical_usage_lbs * (MC.chemical_percentage / 100)
ELSE 0
END
FROM material M
LEFT OUTER JOIN material_chemical MC ON MC.material_id = M.material_id
LEFT OUTER JOIN chemical_usage CU ON CU.material_id = MC.material_id
LEFT OUTER JOIN chemical C ON C.chemical_id = MC.chemical_id
WHERE (CU.month >=1 AND CU.month <= 2)
AND CU.year = 2013
AND M.material_id = 52
) main
group by permit_id, month, material_id, material_name
Explanation
Since the results you retrieved by doing a DISTINCT was consider source-of-truth, I created an in-memory table by making it a sub-query. However, this subquery must have a name of some kind...whatever name. I gave it a name main. Subqueries look like this:
select ... from (sub-query) <give-it-a-table-name>
Simple Example:
select * from (select userid, username from user) user_temp
Advanced Example:
select * from (select userid, username from user) user_temp
inner join (select userid, sum(debits) as totaldebits from debittable) debit
on debit.userid = user_temp.userid
Notice how user_temp alias for the subquery can be used as if the sub-query was a real table.
Use above query in subquery and group by (month) and select sum(POC_emissions) and sum(NON_POC_emissions )

Sql Loop - Other than that

I've read the threads about loops (pros & cons) in and around a SQL statements. I need to "Loop" through 12 hours, hr by hr and was wondering if there is a better way than "Do-While-Loop, etc.) Here is my SQL...
SELECT R.FinalProd
, MIN(r.seqnumber) Seq
, MIN(S.RollRecID) RID
, COUNT(DISTINCT S.RollRecID) Pieces
, (MIN(r.seqnumber) + COUNT(DISTINCT S.RollRecID) - 1) endd
FROM NYS1Reheat1 R INNER JOIN NYS1SawPieces S ON R.RecordID = S.RollRecID INNER JOIN TensileProducts T ON R.FinalProd = T.SQLProduct
where s.ShiftIdent = '05/22/15154D' and r.Location = 'HISTORY'
and datepart(hour,s.prodtime) > 17 and datepart(hour,s.prodtime) < 19
GROUP BY R.FinalProd, T.FootWeight
order by RID
I need to start at 1700 and go hr by hr to 0600.
My plan, as of now, is to "loop" 12 times through the code.
Thanks,
Doug
You can cross join your time intervals:
;with loop12 as (
select 17 as f, 19 as t union all
select 19 as f, 21 as t union all
...
)
SELECT R.FinalProd
, MIN(r.seqnumber) Seq
, MIN(S.RollRecID) RID
, COUNT(DISTINCT S.RollRecID) Pieces
, (MIN(r.seqnumber) + COUNT(DISTINCT S.RollRecID) - 1) endd
, loop12.f
FROM NYS1Reheat1 R
INNER JOIN NYS1SawPieces S ON R.RecordID = S.RollRecID
INNER JOIN TensileProducts T ON R.FinalProd = T.SQLProduct
CROSS JOIN loop12
WHERE s.ShiftIdent = '05/22/15154D' and r.Location = 'HISTORY'
and datepart(hour,s.prodtime) > loop12.f and datepart(hour,s.prodtime) < loop12.t
GROUP BY R.FinalProd, T.FootWeight
order by RID, loop12.f

Is this possible to simplify this SQL query?

I have designed the following sql query to get the percentage of
100 - ((reportedDate – Submission Date) / TotalNumOfVisits) * 100
Is there any way to simplify it? Like combine the two queries into one?
SELECT
q1.VisitMonth,q1.TotalVisit, ISNULL(q2.diff,0) AS DIFF
,100 - ISNULL( (CAST((q2.diff * 1.0 / q1.TotalVisit ) * 100 AS FLOAT)),0) PERC
FROM
(
SELECT DATENAME(MONTH,v.VisitDate) as VisitMonth, count(v.VisitID) as TotalVisit
FROM Visits v
INNER JOIN Assignments a ON a.AssignmentID = v.AssignmentID
WHERE a.ClientID IN (33,46)
AND v.VisitDate BETWEEN '01/01/2013' AND '31/12/2013'
group by DATENAME(MONTH,v.VisitDate)
) q1
LEFT OUTER JOIN
(
SELECT DATENAME(MONTH,v.VisitDate) as MonthName,COUNT(*) as diff
FROM Visits v
INNER JOIN Assignments a ON a.AssignmentID = v.AssignmentID
WHERE a.ClientID IN (33,46)
AND v.VisitDate BETWEEN '01/01/2013' AND '31/12/2013'
AND DATEDIFF(DAY,v.ReportDate,v.SubmissionDate) > 2
group by DATENAME(MONTH,v.VisitDate)
) q2
ON q1.VisitMonth = q2.MonthName
Result:
Try this :-
Select
VisitMonth,isnull(diff,0) as DIFF,
100 - ISNULL( (CAST((diff * 1.0 / Nullif(TotalVisit,0) ) * 100 AS FLOAT)),0) PERC
from
(
SELECT
VisitMonth = Datename(month,visitDate) ,
Diff = Sum(case when DATEDIFF(DAY,v.ReportDate,v.SubmissionDate) > 2 then 1
else 0 end) ,
TotalVisit = Count(v.VisitID)
FROM Visits v
INNER JOIN Assignments a ON a.AssignmentID = v.AssignmentID
WHERE a.ClientID IN (33,46)
AND v.VisitDate BETWEEN '01/01/2013' AND '31/12/2013'
group by DATENAME(MONTH,v.VisitDate)
)a