Handle SQL SUM operator on varchar operands - sql

I want to calculate aggregate functions SUM and COUNT on some table columns. The problem is that the columns are selected dynamically and hence they can be of varchar type. In this situation, COUNT is appliable but SUM isn't. How can I handle this using SQL? I want somthing like:
IF varcharColumn IS typeof(varchar) THEN -1 ELSE SUM(varcharColumn)

So you can do something like this:
DECLARE #varCharColumn VARCHAR(MAX) = (SELECT TOP 1 varcharColumn FROM SomeTable)
IF (ISNUMERIC(#varCharColumn) = 1 )
BEGIN
SELECT COUNT(varcharColumn) AS CountNumber, SUM(varcharColumn) AS SumNumber
FROM SomeTable
END
ELSE
BEGIN
SELECT COUNT(varcharColumn) AS CountNumber, -1 AS SumNumber
FROM SomeTable
END
Here are some info on ISNUMERIC

Related

COUNT inside CASE WHEN is causing Invalid Column error

select case
when COUNT(*)>0 THEN (select TOP 1 A.a1)
else 'none'
end
from A
where A.a1 > 10
order by A.a1
Code above is causing the following error:
Column 'A.a1' is invalid in the select list because it is not
contained in either an aggregate function or the GROUP BY clause.
I could not understand why.
My intention is as follows: If there are rows where A.a1 is greater than 10, order them and take the top rows's a1 value. If there is no such row, select 'none'.
EDIT: It is a simplified version of the actual code that is going to be used in a subquery. So, I cannot use IF..ELSE statements.
You have an aggregation query because you have count(*). These are tricky with subqueries. Instead, you can use:
select (case when count(*) > 0 then A1.a1 else 'none'
end)
from A left join
(select top 1 A.a1) A1
on 1 = 1
where A1.a1 > 10
The logic of this is a bit non-sensical. I assume your actual query is more useful.
I suggest you try with the UNION ALL to solve your Issue. Below Code is for your ref.
DECLARE #T TABLE(ID INT,NAME VARCHAR(200))
INSERT INTO #T VALUES(11,'HAI')
INSERT INTO #T VALUES(12,'H')
SELECT * FROM #T
DECLARE #VAL INT=10
SELECT TOP 1 T.NAME
FROM #T T
WHERE T.ID>#VAL
UNION ALL
SELECT 'None'
WHERE NOT EXISTS (SELECT TOP 1 ID FROM #T WHERE ID>#VAL)
RE: Column 'A.a1' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
COUNT(*) is an aggregation. SQL Server expects all columns to be in a GROUP BY.
RE: If there are rows where A.a1 is greater than 10, order them and take the top rows's a1 value. If there is no such row, select 'none'.
RE: EDIT: It is a simplified version of the actual code that is going to be used in a subquery. So, I cannot use IF..ELSE statements.
ORDER by a1 value and take TOP 1 == MAX(a1).
Assuming a1 is int orfloat, then the value must be converted to
varchar to handle the 'none' output. LOWER works and requires less typing than CAST or CONVERT.
Code:
SELECT ISNULL(LOWER(MAX(a1)), 'none') FROM A WHERE A.a1 > 10

IF equals or clause

I have a stored procedure that runs two separate queries and puts the data into two temporary tables. I then have an IF statement below that, depending on the outcome will display one of the two tables.
DECLARE #DATASET1 AS FLOAT
DECLARE #DATASET2 AS FLOAT
SET #DATASET1 = (SELECT SUM(PREM) FROM #Prem1)
SET #DATASET2 = (SELECT SUM(PREM) FROM #Prem2)
IF (#DATASET1 = 0)
BEGIN
SELECT DATE,
SUM(PREM) AS PREM
FROM #DATASET2
GROUP BY YEAR, MONTH, DATE
ORDER BY YEAR, MONTH
END
IF (#DATASET2 = 0)
BEGIN
SELECT DATE,
SUM(PREM) AS PREM
FROM #DATASET1
GROUP BY YEAR, MONTH, DATE
ORDER BY YEAR, MONTH
END
This was working well until I hit some output on dataset1 that didn't produce 0 but just produced no data at all.
So I was wondering if it is possible to update this part of the query to almost say:
IF (#DATASET1 = 0 or '')
I have tried something like that but doesn't seem to work, hence my question.
Your scalar aggregate query (SELECT SUM(PREM) FROM #Prem1) will return a NULL value if there is no record in the table or of column PREM contains only NULL values.
You can handle NULL with IS NULL, like so:
IF (#DATASET1 IS NULL OR #DATASET1 = 0)
You can also use COALESCE():
IF (COALESCE(#DATASET1, 0) = 0)
You can achieve it by using ISNULL()
IF (ISNULL(#DATASET1, 0) = 0)
Syntax: ISNULL(expression, value)
Parameter Description
expression Required. The expression to test whether is NULL
value Required. The value to return if expression is NULL

SQL Assign a variable in a SELECT and do a CASE with the variable

Is there any way in doing something like this
declare a variable, assign a value to it in a select statement, then use it in a case ?
Something like this
DECLARE #result BIGINT;
SELECT #result = (SELECT count(_t.Id) FROM T _t WHERE _t.T2Id = _t2.Id GROUP BY _t.T2Id)
CASE WHEN #result IS NULL THEN 0 ELSE #result END AS ColNAme
FROM T2 _t2
The idea is that I would like to avoid doing the select count query twice.
CASE
WHEN (SELECT COUNT(_t.Id) FROM T _t WHERE _t.T2Id = _t2.Id GROUP BY _t.T2Id) IS NULL THEN 0
ELSE (SELECT COUNT(_t.Id) FROM T _t WHERE _t.T2Id = _t2.Id GROUP BY _t.T2Id) END AS ColNAme
I don't want to use functions.
The result set should contain the count of all T items that have a T2 reference by T2 Id
ID | Count
2 | 0
4 | 12
Yes, you can use a variable defined in one select later in the script. But you still use two select queries (note the following doesn't work so read the whole answer):
DECLARE #result BIGINT;
SELECT #result = (SELECT count(_t.Id) FROM T _t WHERE _t.T2Id = _t2.Id GROUP BY _t.T2Id);
SELECT (CASE WHEN #result IS NULL THEN 0 ELSE #result END) AS ColNAme
FROM T2 _t2;
Notes:
The first query will return an error because _t2 is not defined.
The second query should just use COALESCE(#result, 0).
#result will never be NULL, because COUNT() never returns a NULL value. So the second query could really be just select #result.
Returning the same value for every row in T2 is strange. I imagine your actually query is more interesting.
There is no reason to use a variable, unless you need the value later in the script.
So, based on your desired results, I think you simply want a left join and group by:
select t2.id, count(t.id)
from t2 left join
t
on t2.id = t.t2id
group by t2.id;

If value return the value. If a record not exists or when column is null, return 0 in Sql Server - different ways

I want to return 0 if there is no record or if the Column1 is null.
select #var = Column1
from myschema.mytable
where Id = #suppliedId;
select isnull(#var, 0);
The above code outputs 0 if if Column1 is null. Or if a row is not found
Whereas I tried to save some keystrokes but it resulted in,
select isnull(Column1, 0)
from myschema.mytable
where Id = #suppliedId;
The above code outputs null if Column1 is null or when there is no row
Any ideas what is wrong here ? Or is there any shorter way of writing the first code ?
You can do
SELECT #var = ISNULL(MAX(Column1), 0)
FROM myschema.mytable
WHERE Id = #suppliedId;
A scalar aggregate always returns a single row even if the underlying query returns zero rows.
Not really saving key strokes, but something like this could help :-)
SELECT TOP 1 tbl.field
FROM
(
SELECT 0 AS inx, 'no record' AS field
--if only one row is possible, than set '1' literally
UNION SELECT ROW_NUMBER() OVER(ORDER BY mytable.orderfield), ISNULL(mytable.Land,'is null')
FROM mytable
WHERE IDENTITY = #suppliedID
) AS tbl
ORDER BY tbl.inx DESC

Return zero if no record is found

I have a query inside a stored procedure that sums some values inside a table:
SELECT SUM(columnA) FROM my_table WHERE columnB = 1 INTO res;
After this select I subtract res value with an integer retrieved by another query and return the result. If WHERE clause is verified, all works fine. But if it's not, all my function returns is an empty column (maybe because I try to subtract a integer with an empty value).
How can I make my query return zero if the WHERE clause is not satisfied?
You could:
SELECT COALESCE(SUM(columnA), 0) FROM my_table WHERE columnB = 1
INTO res;
This happens to work, because your query has an aggregate function and consequently always returns a row, even if nothing is found in the underlying table.
Plain queries without aggregate would return no row in such a case. COALESCE would never be called and couldn't save you. While dealing with a single column we can wrap the whole query instead:
SELECT COALESCE( (SELECT columnA FROM my_table WHERE ID = 1), 0)
INTO res;
Works for your original query as well:
SELECT COALESCE( (SELECT SUM(columnA) FROM my_table WHERE columnB = 1), 0)
INTO res;
More about COALESCE() in the manual.
More about aggregate functions in the manual.
More alternatives in this later post:
How to return a value from a function if no value is found
I'm not familiar with postgresql, but in SQL Server or Oracle, using a subquery would work like below (in Oracle, the SELECT 0 would be SELECT 0 FROM DUAL)
SELECT SUM(sub.value)
FROM
(
SELECT SUM(columnA) as value FROM my_table
WHERE columnB = 1
UNION
SELECT 0 as value
) sub
Maybe this would work for postgresql too?
You can also try: (I tried this and it worked for me)
SELECT ISNULL((SELECT SUM(columnA) FROM my_table WHERE columnB = 1),0)) INTO res;
You can use exists clause.
IF EXISTS(SELECT FROM my_table WHERE columnB = 1)
THEN
res := SUM(columnA);
ELSE
res := 0
END IF;