Sum on multiple columns with nullable values - sql

I have to edit a stored procedure who has to return the sums of three columns having nullable values. If there is a null value, I need to cast it to 0
Here is a screenshot of data :
And here is the originial request using the first column only :
SELECT SUM(reglProj.Montant) /* SUM of 'Montant', 'FraisMagasing', 'FraisVendeur' instead */ AS SommeReglement
FROM Projet.LigneEcheancierProjet ligne
INNER JOIN Projet.ReglementProjetEcheance reglProj ON reglProj.LigneEcheancierProjetId = ligne.LigneEcheancierProjetId
....
Do you have some best practices using the sum and case conditions in T-SQL ?

--ANSI standard
SELECT SUM(COALESCE(col1,0)) + SUM(COALESCE(col2,0)) + SUM(COALESCE(col3,0))
--SQL Server Style
SELECT SUM(ISNULL(col1,0)) + SUM(ISNULL(col2,0)) + SUM(ISNULL(col3,0))
--The one wthout functions. It will work the same as previous OR FASTER.
SELECT SUM(CASE WHEN col1 IS NULL THEN 0 ELSE col1 END) + SUM(CASE WHEN col2 IS NULL THEN 0 ELSE col2 END) + SUM(CASE WHEN col3 IS NULL THEN 0 ELSE col3 END)
Choose one for yourself.
OR you might need following (if you want to add sums by row):
--ANSI standard
SELECT SUM(COALESCE(col1,0) +COALESCE(col2,0) + COALESCE(col3,0))
--SQL Server Style
SELECT SUM(ISNULL(col1,0)+ ISNULL(col2,0) + ISNULL(col3,0))
--The one wthout functions. It will work the same as previous OR FASTER.
SELECT SUM(CASE WHEN col1 IS NULL THEN 0 ELSE col1 END + CASE WHEN col2 IS NULL THEN 0 ELSE col2 END + CASE WHEN col3 IS NULL THEN 0 ELSE col3 END)

In Sql Server, (and probably in most if not all relational databases) the SUM Aggregation function ignores null values by default, so there really is no need to use coalesce or isnull inside it.
If you want the sum of all 3 columns for every single row, then you need to use isnull:
SELECT ISNULL(reglProj.Montant,0) +
ISNULL(reglProj.FraisMagasing ,0) +
ISNULL(reglProj.FraisVendeur,0)
FROM Projet.LigneEcheancierProjet ligne
INNER JOIN Projet.ReglementProjetEcheance reglProj
ON reglProj.LigneEcheancierProjetId = ligne.LigneEcheancierProjetId
If you need the aggregated sum of all 3 columns you can simply do it like this:
SELECT ISNULL(SUM(reglProj.Montant), 0) +
ISNULL(SUM(reglProj.FraisMagasing), 0) +
ISNULL(SUM(reglProj.FraisVendeur), 0)
FROM Projet.LigneEcheancierProjet ligne
INNER JOIN Projet.ReglementProjetEcheance reglProj
ON reglProj.LigneEcheancierProjetId = ligne.LigneEcheancierProjetId

It seems you are looking for ISNULL actually
SELECT SUM( ISNULL(reglProj.Montant,0) + ISNULL(FraisMagasing,0)+ ISNULL(FraisVendeur,0)) AS SommeReglement
FROM Projet.LigneEcheancierProjet ligne
INNER JOIN Projet.ReglementProjetEcheance reglProj ON reglProj.LigneEcheancierProjetId = ligne.LigneEcheancierProjetId

Related

Select NULL if a calculated column value is negative in SQL

I have the following piece of sql below. The second line (commented out) contains my addition of a check to see if the calculation returns a negative value in which case it should select NULL. This case is within a block of multiple other case statements. Since my approach means running the same calculation twice is there a better alternative or more efficient method to selecting NULL if the value of this calculated column is negative, rather than doing two similar calculations?
Thanks
CASE
WHEN M.ALPHA = 'B' OR T.CT IN (0.001, 0.002) THEN NULL
-- WHEN ((M.VAL / NULLIF (M.VAL2, 0)) / (NULLIF (T.VAL, 0) / T.VAL2)) < 0 THEN NULL
ELSE (M.VAL / NULLIF (M.VAL2, 0)) / (NULLIF (T.VAL, 0) / T.VAL2)
END As WORLD
You could move the calculation to a subquery. For example:
select case
when CalculatedColumn > 42 then 'Hot'
when CalculatedColumn < 42 then 'Cold'
else 'Answer'
end as Description
from (
select 2 * col1 + 3 as CalculatedColumn
from YourTable
) SubQuery
Sometimes it's clearer to define the subquery in a with clause:
; with SubQuery as
(
select 2 * col1 + 3 as CalculatedColumn
from YourTable
) SubQuery
select case
when CalculatedColumn > 42 then 'Hot'
when CalculatedColumn < 42 then 'Cold'
else 'Answer'
end as Description
from SubQuery

Check any 4 out 10 conditions are satisfied in SQL

I apologize for asking a very vague question but here it is.
I have to write a SQL query in SQL Server as follows.
I have a table say tblA having 10 columns from col1, col2,.....col10.
Each column is not null and definitely holds some value and all of type int.
The query should be to select all such records in which at least 4 columns are matching with given filter criteria, where the filter criteria has values for all 10 columns.
I googled dint get a clue. It needs to be done in SQL server and single query.
Please suggest.
Thanks in advance.
SELECT *
FROM
yourtable
WHERE
case col1 when #value1 then 1 else 0 end +
case col2 when #value2 then 1 else 0 end +
...
case col10 when #value10 then 1 else 0 end
>=4
You can use CASE expressions to determine if four or more columns match:
SELECT *
FROM YourTable
WHERE CASE WHEN Col1 = Filter1 THEN 1 ELSE 0 END +
CASE WHEN Col2 = Filter2 THEN 1 ELSE 0 END +
....
CASE WHEN Col10 = Filter10 THEN 1 ELSE 0 END >= 4
You can do something like this:
select *
from (select t.*,
(case when col1 <whatever> then 1 else 0 end) as col01_matches,
(case when col2 <whatever> then 1 else 0 end) as col02_matches,
. . .
from t
) t
where (col1_matches + col2_matches + col3_matches . . .) >= 4
This creates a separate indicator variable for each match. You could also do the sum in the subquery, in a single variable. I would prefer to have each match separately, just in case the logic gets more complicated or I want to see what matches.

In SQL (Tsql) what's a good way to check mutually exclusive options are correct

EDIT: damien the unbeliever, my apologies, trying to be terse I omitted saying that the design of the table is not under my control; this table is a "dump" of data we receive from another vendor, and I have to convert it from their format to ours. The reason I need a query is to find out if the data is consistent with assumptions in other parts of code. The solutions proposed looking for length or exact match of the concatenated strings are better than my pair of queries for the problem I described.
I have a working pair of queries for my problem, but I wondered if there's something a bit prettier. Exactly one of taxidflag1, taxidflag2, taxidflag3 should be filled in with * in each row. So I confirm they all have two blanks and one * like this. All fields are are non nullable.
select * from acct where 2 <>
(case when taxidFlag1 <> '' then 1 else 0 end) +
(case when taxidFlag2 <> '' then 1 else 0 end) +
(case when taxidFlag3 <> '' then 1 else 0 end)
select * from acct where 1 <>
(case when taxidFlag1 = '*' then 1 else 0 end) +
(case when taxidFlag2 = '*' then 1 else 0 end) +
(case when taxidFlag3 = '*' then 1 else 0 end)
You could do this:
select * from acct where taxidFlag1 + taxidFlag2 + taxidFlag3 = '*';
This condition is only true if two are empty ('') and one is a asterisk (*).
select *
from
acct a1
where
(select count(*) from acct unpivot (foo for taxidFlag in (taxidFlag1, taxidFlag2, taxidFlag3)) as unp where unp.row_id = a1.row_id and foo = '*') <> 1
;
where row_id is your primary key field.

SQL Server query - loop question

I'm trying to create a query that would generate a cross-check table with about 40 custom columns that show Y or N. Right now I have
SELECT DISTINCT [Company],
[Option1],
[Option2],
[Option3],
CASE
WHEN [Table1].[ID1] IN (SELECT ID2 FROM Table2 WHERE Variable = 1 AND Bit = 1) THEN
'Y'
ELSE 'N'
END AS 'CustomColumn1:',
CASE
WHEN [Table1].[ID1] IN (SELECT ID2 FROM Table2 WHERE Variable = 2 AND Bit = 1) THEN
'Y'
ELSE 'N'
END AS 'CustomColumn1:',
CASE
WHEN [Table1].[ID1] IN (SELECT ID2 FROM Table2 WHERE Variable = 3 AND Bit = 1) THEN
'Y'
ELSE 'N'
END AS 'CustomColumn1:',
.............
-- REPEAT ANOTHER 40 times
FROM [Table1]
WHERE [Table1].[OtherCondition] = 'True'
ORDER BY [Company]
So my question is, how do I create a loop (while? for?) that will loop on variable and assign Y or N to the row based on the condition, rather than creating 40+ Case statements?
You couldn't use a loop, but you could create a stored procedure/function to perform the sub-select and case expression and call that 40 times.
Also, you could improve performance of the sub-select by changing it to
SELECT 1 FROM Table2 WHERE EXISTS [Table2].[ID2] = [Table1.ID1] AND Variable = 3 AND Bit = 1
A loop (that is, iterating through a cursor) works on rows, not columns. You will still have to have 40 expressions, one for each column, and the performance will be terrible.
Let SQL Server do its job. And do your bit by telling exactly what you need and creating proper indices. That is, replace
CASE WHEN [Table1].[ID1] IN (SELECT ID2 FROM Table2 WHERE Variable = 2 AND Bit = 1)
with
CASE WHEN EXISTS (SELECT 0 FROM Table2 WHERE ID2 = [Table1].[ID1] AND Variable = 2 AND Bit = 1)
If the output is so vastly different than the schema, there is a question as to whether the schema properly models the business requirements. That said, I would recommend just writing the SQL. You can simplify the SQL like so:
Select Company
, Option1, Option2, Option3
, Case When T2.Variable = 1 Then 'Y' Else 'N' End As CustomCol1
, Case When T2.Variable = 2 Then 'Y' Else 'N' End As CustomCol2
, Case When T2.Variable = 3 Then 'Y' Else 'N' End As CustomCol3
, Case When T2.Variable = 4 Then 'Y' Else 'N' End As CustomCol4
...
From Table1 As T1
Left Join Table2 As T2
On T2.ID2 = T1.ID
And T2.Bit = 1
Where T1.OtherCondition = 'True'
Group By T1.Company
Order By T1.Company
If you want to write something that can help you auto-gen those Case statements (and you are using SQL Server 2005+), you could do something like:
With Numbers As
(
Select 0 As Value
Union All
Select Value + 1
From Numbers
Where Value < 41
)
Select ', Case When T2.Variable = ' + Cast(N.Value As varchar(10)) + ' Then ''Y'' Else ''N'' End As CustomCol' + Cast(N.Value As varchar(10))
From Numbers As N
You would run the query and copy and paste the results into your procedure or code.
One way could have been to use Pivot statement, which is in MS SQL 2005+. But even in that you have to put 1 ... 40 hardcoded columns in pivot statement.
Other way i can think of is to create dynamic SQL, but it is not so much recommended, So what we can do is we can create a dynamic sql query by running a while loop on table and can create the big sql and then we can execute it by using sp_execute. So steps would be.
int #loopVar
SET #loopVar = 0
int #rowCount
varchar #SQL
SET #SQl = ''
Select #rowcount = Count(ID2) from Table2
WHILE(#loopVar <= #rowCount)
BEGIN
// create ur SQL here
END
sp_execute(#SQL)

SQL: sum 3 columns when one column has a null value?

SELECT
sum(TotalHoursM)
+ (TotalHoursT)
+ (TotalHoursW)
+ (TotalHoursTH)
+ (TotalHoursF)
AS TOTAL
FROM LeaveRequest
If the column has a 0 value, you are fine, my guess is that you have a problem with a Null value, in that case you would need to use IsNull(Column, 0) to ensure it is always 0 at minimum.
The previous answers using the ISNULL function are correct only for MS Sql Server. The COALESCE function will also work in SQL Server. But will also work in standard SQL database systems. In the given example:
SELECT sum(COALESCE(TotalHoursM,0))
+ COALESCE(TotalHoursT,0)
+ COALESCE(TotalHoursW,0)
+ COALESCE(TotalHoursTH,0)
+ COALESCE(TotalHoursF,0) AS TOTAL FROM LeaveRequest
This is identical to the ISNULL solution with the only difference being the name of the function. Both work in SQL Server but, COALESCE is ANSI standard and ISNULL is not. Also, COALESCE is more flexible.
ISNULL will only work with two parameters. If the first parameter is NULL then the value of the second parameter is returned, else the value of the first is returned.
COALESCE will take 2 to 'n' (I don't know the limit of 'n') parameters and return the value of the first parameter that is not NULL. When there are only two parameters the effect is the same as ISNULL.
SELECT sum(isnull(TotalHoursM,0))
+ isnull(TotalHoursT,0)
+ isnull(TotalHoursW,0)
+ isnull(TotalHoursTH,0)
+ isnull(TotalHoursF,0))
AS TOTAL FROM LeaveRequest
Just for reference, the equivalent statement for MySQL is: IFNull(Column,0).
This statement evaluates as the column value if not null, otherwise it is evaluated as 0.
You can use ISNULL:
ISNULL(field, VALUEINCASEOFNULL)
looks like you want to SUM all the columns (I'm not sure where "sum 3 columns" comes from), not just TotalHoursM, so try this:
SELECT
SUM( ISNULL(TotalHoursM ,0)
+ ISNULL(TotalHoursT ,0)
+ ISNULL(TotalHoursW ,0)
+ ISNULL(TotalHoursTH ,0)
+ ISNULL(TotalHoursF ,0)
) AS TOTAL
FROM LeaveRequest
You can also use nvl(Column,0)
I would try this:
select sum (case when TotalHousM is null then 0 else TotalHousM end)
+ (case when TotalHousT is null then 0 else TotalHousT end)
+ (case when TotalHousW is null then 0 else TotalHousW end)
+ (case when TotalHousTH is null then 0 else TotalHousTH end)
+ (case when TotalHousF is null then 0 else TotalHousF end)
as Total
From LeaveRequest
If you want to avoid the null value use IsNull(Column, 1)