SQL issue with NULL values on SUM - sql

I'm currently working on some sql stuff, but running in a bit of an issue.
I've got this method that looks for cash transactions, and takes off the cashback but sometimes there are no cash transactions, so that value turns into NULL and you can't subtract from NULL.
I've tried to put an ISNULL around it, but it still turns into null.
Can anyone help me with this?
;WITH tran_payment AS
(
SELECT 1 AS payment_method, NULL AS payment_amount, null as tran_header_cid
UNION ALL
SELECT 998 AS payment_method, 2 AS payment_amount, NULL as tran_header_cid
),
paytype AS
(
SELECT 1 AS mopid, 2 AS mopshort
),
tran_header AS
(
SELECT 1 AS cid
)
SELECT p.mopid AS mopid,
p.mopshort AS descript,
payment_value AS PaymentValue,
ISNULL(DeclaredValue, 0.00) AS DeclaredValue
from paytype p
LEFT OUTER JOIN (SELECT CASE
When (tp.payment_method = 1)
THEN
(ISNULL(SUM(tp.payment_amount), 0)
- (SELECT ISNULL(SUM(ABS(tp.payment_amount)), 0)
FROM tran_payment tp
INNER JOIN tran_header th on tp.tran_header_cid = th.cid
WHERE payment_method = 998
) )
ELSE SUM(tp.payment_amount)
END as payment_value,
tp.payment_method,
0 as DeclaredValue
FROM tran_header th
LEFT OUTER JOIN tran_payment tp
ON tp.tran_header_cid = th.cid
GROUP BY payment_method) pmts
ON p.mopid = pmts.payment_method

Maybe COALESCE() can help you?
You can try this:
SUM(COALESCE(tp.payment_amount, 0))
or
COALESCE(SUM(tp.payment_amount), 0)
COALESCE(arg1, arg2, ..., argN) returns the first non-null argument from the list.

try to put ISNULL inside SUM and ABS, i.e. around the actual field, like this
SUM(ISNULL(tp.payment_amount, 0))
SUM(ABS(ISNULL(tp.payment_amount, 0)))

I don't have MS SQL to test here, but would it work to put the ISNULL around the SELECT? Maybe, ISNULL isn't triggered at all, if there are no matching rows...

Related

Query division returns 0

I am working on a query that devide 2 columns, I tried CAST and CONVERT but still returns 0. Will apperciate your help
SELECT a.Disposition,a.[Disposition Reason Breakdown],a.CSP,b.Total FROM
(
SELECT a.[Disposition],a.[Disposition Reason Breakdown],a.[CSP] FROM
(
SELECT [Disposition],[Disposition Reason Breakdown],COUNT(*) as CSP FROM [dbo].[Disposition]
WHERE [Disposition] <> 'Interested'
GROUP BY [Disposition],[Disposition Reason Breakdown]
) a
)a
INNER JOIN
(
SELECT a.Disposition,SUM(a.CSP) as Total FROM
(
SELECT [Disposition],[Disposition Reason Breakdown],COUNT(*) as CSP FROM [dbo].[Disposition]
WHERE [Disposition] <> 'Interested'
GROUP BY [Disposition],[Disposition Reason Breakdown]
)a
GROUP BY a.Disposition
)b ON a.Disposition = b.Disposition
I am using sql
I solved it, it turns out that I just used the wrong data type which in my case is decimal I should've thought of REAL here is the final query a.CSP/CAST(b.Total as REAL)

Adding a new computed variable back to main dataset in SQL

I am trying to compute a variable (say last_week) and add it back to my main dataset (say new_j). I managed to join it to new_j. However, if I want to use that variable (last_week) now for further calculations, it does not recognise it. Here's my code:
SELECT [Weekkey] AS weekkey
,[article / colour] as prod_id
,[Current MP Department No/Desc] as prod_dept
,[Total Stock] as total_stock
INTO #new_j
FROM [J_20160831] --(that’s the db in server and I created a temp db #new_j)
SELECT prod_id, max(weekkey) as last_week
into #lastweeksales
FROM #new_j
group by prod_id
select *
from #new_j
left join #lastweeksales
on #lastweeksales.prod_id = #new_j.prod_id
So, I joined both successfully and if I run this code, I see column last_week. Now what I want to do is this:
select *
,case
when last_week = max(weekkey) then total_stock
else 0
end as last_stock_position
from #new_j
But it says last_week is not found in new_j. I also tried #lastweeksales.last_week instead of just last_week in the last bit of code, but it didn't either. What's the best way out here? Moreover, is there a better way to do it instead?. The output I am looking to have at the end is a table with these variables: WeekKey, prod_dept, prod_id, total_stock, last_week, last_stock_position
Thanks for the help!!! Much appreciate it.
This normal behaviour of joins..
by selecting this
select * from #new_j left join #lastweeksales
on #lastweeksales.prod_id = #new_j.prod_id'
all the columns of newj and lastweekales will be displayed in same order (first new_j columns and then lastweeksales columns ).So 'last_week' is the last column of lastweeksales.
Secondly,
select *,
case when last_week = max(weekkey) then total_stock
else 0
end as last_stock_position
from #new_j
in above query,your are selecting 'last_week' column which belongs to the table #lastweeksales.
Be careful while selecting the columns.
I guess your expecting,
select a.WeekKey, a.prod_dept, a.prod_id, a.total_stock, b.last_week,
case
when b.last_week = max(a.weekkey) then total_stock
else 0
end as last_stock_position
from #new_j as a
left join #lastweeksales as b
on b.prod_id = a.prod_id
group by a.weekkey,a.prod_dept,a.prod_id,a.total_stock,b.last_week

Select the last non-NULL value when current row is NULL

I know that there are a lot of solutions for this but unfortunately I cannot use partition or keyword TOP. Nothing I tried on earlier posts works.
My table looks like this:
The result I want is when any completion percentage is NULL it should get the value from last non-value completion percentage, like this:
I tried this query but nothing works. Can you tell me where I am going wrong?
SELECT sequence,project_for_lookup,
CASE WHEN completion_percentage IS NOT NULL THEN completion_percentage
ELSE
(SELECT max(completion_percentage) FROM [project_completion_percentage] AS t2
WHERE t1.project_for_lookup=t2.project_for_lookup and
t1.sequence<t2.sequence and
t2.completion_percentage IS NOT null
END
FROM [project_completion_percentage] AS t1
SQL Server 2008 doesn't support cumulative window functions. So, I would suggest outer apply:
select cp.projectname, cp.sequence,
coalesce(cp.completion_percentage, cp2.completion_percentage) as completion_percentage
from completion_percentage cp outer apply
(select top 1 cp2.*
from completion_percentage cp2
where cp2.projectname = cp.projectname and
cp2.sequence < cp.sequence and
cp2.completion_percentage is not null
order by cp2.sequence desc
) cp2;
Does this work? It seems to for me. You were missing a parenthesis and had the sequence backwards.
http://sqlfiddle.com/#!3/465f2/4
SELECT sequence,project_for_lookup,
CASE WHEN completion_percentage IS NOT NULL THEN completion_percentage
ELSE
(
SELECT max(completion_percentage)
FROM [project_completion_percentage] AS t2
WHERE t1.project_for_lookup=t2.project_for_lookup
-- sequence was reversed. You're on the row t1, and want t2 that is from a prior sequence.
and t2.sequence<t1.sequence
and t2.completion_percentage IS NOT null
--missing a closing paren
)
END
FROM [project_completion_percentage] AS t1

IsNull is slow, do I have other option?

Having the following query:
select
tA.Name
,tA.Prop1
,tA.Prop2
( select sum(tB.Values)
from tableB tB
where tA.Prop1 = tB.Prop1
and tA.Prop2 = tB.Prop2
) as Total
from tableA tA
This query is taking me 1 second to run, BUT it will give me wrong SUM when Prop2 is null
I change the query to use IsNULL
and ISNULL(tA.Prop2,-1) = ISNULL(tB.Prop2,-1)
the data is correct now, but takes almost 7 seconds.....
Is there a fastest way to do this?
Note: this is just a partial simplified version of a more complex query.... but the base idea is here.
From your description, = is using an index, but the isnull() blocks the use of an index. This is a bit hard to get around in SQL server.
One way is to break the logic into two sums:
select tA.Name, tA.Prop1, tA.Prop2
(isnull((select sum(tB.Values)
from tableB tB
where tA.Prop1 = tB.Prop1 and tA.Prop2 = tB.Prop2
), 0) +
isnull((select sum(tB.Values)
from tableB tB
where tA.Prop1 = tB.Prop1 and
tA.Prop2 is null and tB.Prop2 is null
), 0)
) as Total
from tableA tA;
I ended up using
AND (
(tA.Prop2= tB.Prop2)
OR (tA.Prop2 IS NULL AND tB.Prop2 IS NULL )
)

sql sum group by

I have following sql query
DECLARE #frameInt INT
SELECT TOP 1 #frameInt = gs.FrameGenerationInterval
FROM dbo.GlobalSettings gs
SELECT mti.InternalId,
b.InternalId AS BrandId,
CASE
WHEN DATEDIFF(second, mti.StartTime, mti.EndTime) / #frameInt > 0 THEN DATEDIFF(second, mti.StartTime, mti.EndTime) / #frameInt
ELSE 1
END AS ExposureAmount,
c.InternalId AS ChannelId,
c.Name AS ChannelName,
COALESCE( (p.Rating *
CASE
WHEN DATEDIFF(second, mti.StartTime, mti.EndTime) / #frameInt > 0 THEN DATEDIFF(second, mti.StartTime, mti.EndTime) / #frameInt
ELSE 1
END * CAST (17.5 AS decimal(8,2))
),CAST( 0 as decimal(8,2)) ) AS Equivalent
FROM dbo.MonitorTelevisionItems mti
LEFT JOIN dbo.Brands b ON mti.BrandId = b.InternalId
LEFT JOIN dbo.Channels c ON mti.ChannelId = c.InternalId
LEFT JOIN dbo.Programs p ON mti.ProgramId = p.InternalId
--WHERE mti.Date >= #dateFromLocal AND mti.Date <= #dateToLocal
GROUP BY mti.InternalId, mti.EndTime, mti.StartTime,
c.Name, p.Name, p.Rating,b.InternalId, c.InternalId
It gives following result
I would like it to return 1 row with sums of exposure amount and equivalent from all rows. Rest of the cells are the same apart from InternalId that I dont really need (i can remove it from query)
I am not very good at sql. Thank for help.
For the sake of posterity (and because it's cool to learn something new), here's the general solution to your problem (instead of a copy-and-paste ready solution for your specific case):
SELECT group_field1, group_field2, ...,
SUM(sum_field1), SUM(sum_field2), ...
FROM (...your original SQL...) AS someArbitraryAlias
GROUP BY group_field1, group_field2, ...
In your specific case, the group fields would be BrandId, ChannelId and ChannelName; the sum fields would be ExposureAmount and Equivalent.
Note: To ease readability (since your original SQL is quite complex), you can use a common table expression:
WITH someArbitraryAlias AS (
...your original SQL...
)
SELECT group_field1, group_field2, ...,
SUM(sum_field1), SUM(sum_field2), ...
FROM someArbitraryAlias
GROUP BY group_field1, group_field2, ...
Note that, when using common table expressions, the immediately preceding statement must be terminated with a semicolon.