ISNULL with aggregate function - sql

What is the best way to go about using these two together? In my case if a userID is null I want to return zero, and users can have multiple ID's so we want to have get the lowest (the original) one.
ISNULL(MIN(UserId),0)
Or,
MIN(ISNULL(UserId),0)
Thank you.
Is the answer indicative of all aggregate functions?

Those statements do not necessarily produce the same output:
the first takes the minimum that exists and only if that is null, uses 0.
the second checks each user id and if that is null uses 0 - it then takes the minimum of those (and unless a user ID can be negative, a user with a 5 and a null, would output 0)
A quick script can demonstrate this :
with testData as (
select 1 as SomeKey, 5 as userID
union all
select 1 as SomeKey, null as userID
union all
select 2 as SomeKey, 6 as userID
union all
select 2 as SomeKey, 5 as userID
)
select
somekey
, isnull(min(userid),0) as firstScenario
, min(isnull(userid,0)) as SecondScenario
from testdata
group by somekey
Results:
Somekey firstScenario secondScenario
1 5 0
2 5 5
The first scenario is the most likely one you were after, but the phrasing of the question makes it a bit ambiguous as to what the desired behaviour was.
(http://sqlfiddle.com/#!6/9eecb7db59d16c80417c72d1e1f4fbf1/10170)

It depends on what you want to do. But I am biased towards COALESCE() because it is the ANSI standard function.
Your two options are:
COALESCE(MIN(UserId), 0)
MIN(COALESCE(UserId, 0))
These do not do the same thing. The first returns the minimum user id. If all user ids are NULL, then this expression returns 0.
The second replaces each NULL with 0. Assuming the user ids are positive, then this returns 0 if any user ids are NULL.
Based on my understanding of your logic, you want the second version.

I suppose you use SQL Server, bacause ISNULL is a T-Sql function.
To use a function accross DBMS you can use COALESCE
NULL values are not included in MIN functions.
So If you want to prevent NULL result, I advice you to use the first solution
ISNULL(MIN(UserId), 0)

Related

Ignore a column in MS ACCESS query if the WHERE clause is not set

Problem
I've got a dropdown list, which shows all the Article_Group_ID's that are linked to a specific brand, using the following Query:
SELECT TbArticle.Article_Group_ID, TbArticle.Article_Brand_ID
FROM TbArticle
GROUP BY TbArticle.Article_Group_ID, TbArticle.Article_Brand_ID,
HAVING (((TbArticle.Article_Brand_ID)=1))
This works as expected, it returns the following:
Query results
Article_Brand_ID
Article_Group_ID
1
1
1
2
But, if a user does not wish to specify a specific Article_Brand_ID, the query results look like this:
Query
Article_Brand_ID
Article_Group_ID
1
1
2
1
3
1
1
2
As you can see, the same Article_Group_ID is returned three times. Because of this, the user now sees the same group three times, instead of just once. If I were to remove the Article_Brand_ID from the query, the results would look like this:
Article_Group_ID
1
2
Is there any way to achieve the same behavior, by "ignoring" the Article_Brand_ID column, if it's WHERE clause is not set?
Database layout
TbArticle
Article_Brand_ID
Article_Group_ID
1
1
2
1
3
1
1
2
A single query cannot return a variable number of columns. So, strictly speaking you cannot do what you want with a single query. However, if you are willing to accept the second column as NULL when the brand is not provided, then you can adjust the aggregation.
Let me denote the parameter by ?:
SELECT a.Article_Group_ID,
IIF(? IS NOT NULL, a.Article_Brand_ID, NULL) as Article_Brand_ID
FROM TbArticle as a
WHERE a.Article_Brand_ID = ? OR
? IS NULL
GROUP BY a.Article_Group_ID,
IIF(? IS NOT NULL, a.Article_Brand_ID, NULL);
Note: It is usually better to filter before aggregating (i.e. using WHERE) rather than filtering afterwards (i.e. using HAVING).

Oracle SQL - Multiple return from case

I may be trying it wrong. I am looking for any approach which is best.
Requirement:
My Query joins 4-5 tables based on few fields.
I have a column called product id. In my table there are 1.5 million rows. Out of those only 10% rows has product ids with the following attribute
A300X-%
A500Y-%
300,500, 700 are valid model numbers. X and Y are classifications. My query picks all the systems.
I have a check as follows
CASE
WHEN PID LIKE 'A300X%'
THEN 'A300'
...
END AS MODEL
Similarly
CASE
WHEN PID LIKE 'A300X%'
THEN 'X'
...
END AS GENRE
I am looking for the best option from the below
How do I Combine both case statement and add another[third] case which will have these two cases. i.e
CASE
WHEN desc in ('AAA')
First Case
Second Case
ELSE
don't do anything for other systems
END
Is there any regex way of doing this? Before first - take the string. Look for X, Y and also 300,500,700.
Is there any other way of doing this? Or doing via code is the best way?
Any suggestions?
EDIT:
Sample desc:
AAA,
SoftwARE,
sw-app
My query picks all the desc. But the case should be running for AAA alone.
And Valid models are
A300X-2x-P
A500Y-5x-p
A700X-2x-p
A50CE-2x-P
I have to consider only 300,500,700. And the above two cases.
Expected result:
MODEL GENRE
A300 X
A500 Y
A300 Y
Q: How do I Combine both CASE statement expressions
Each CASE expression will return a single value. If the requirement is to return two separate columns in the resultset, that will require two separate expressions in the SELECT list.
For example:
DESC PID model_number genre
---- ---------- ------------ ------
AAA A300X-2x-P 300 X
AAA A500Y-5x-p 500 Y
AAA A700X-2x-p 700 X
AAA A50CE-2x-P (NULL) (NULL)
FOO A300X-2x-P (NULL) (NULL)
There will need to be an expression to return the model_number column, and a separate expression to return the genre column.
It's not possible for a single expression to return two separate columns.
Q: and add another[third] case which will have these two cases.
A CASE expression returns a value; we can use a CASE expression almost anywhere in a SQL statement where we can use a value, including within another CASE expression.
We can also combine multiple conditions in a WHEN test with AND and OR
As an example of combining conditions and nesting CASE expressions ditions...
CASE
WHEN ( ( t.PID LIKE '_300%' OR t.PID LIKE '_500%' OR t.PID LIKE '_700%' )
AND ( t.DESC = 'AAA' )
)
THEN CASE
WHEN ( t.PID LIKE '____X%' )
THEN 'X'
WHEN ( t.PID LIKE '____Y%' )
THEN 'Y'
ELSE NULL
END
ELSE NULL
END AS `genre`
There are other expressions that will return an equivalent result; the example shown here isn't necessarily the best expression. It just serves as a demonstration of combining conditions and nesting CASE expressions.
Note that to return another column model we would need to include another expression in the SELECT list. Similar conditions will need to be repeated; it's not possible to reference the WHEN conditions in another CASE expression.
Based on your sample data, logic such as this would work:
(CASE WHEN REGEXP_LIKE(PID, '^A[0-9]{3}[A-Z]-')
THEN SUBSTR(PID, 1, 4)
ELSE PID
END) AS MODEL
(CASE WHEN REGEXP_LIKE(PID, '^A[0-9]{3}[A-Z]-')
THEN SUBSTR(PID, 5, 1)
ELSE PID
END) AS GENRE
This assumes that the "model number" always starts with "A" and is followed by three digits (as in your example data). If the model number is more complicated, you may need regexp_substr() to extract the values you want.

PostgreSQL: order by column, with specific NON-NULL value LAST

When I discovered NULLS LAST, I kinda hoped it could be generalised to 'X LAST' in a CASE statement in the ORDER BY portion of a query.
Not so, it would seem.
I'm trying to sort a table by two columns (easy), but get the output in a specific order (easy), with one specific value of one column to appear last (got it done... ugly).
Let's say that the columns are zone and status (don't blame me for naming a column zone - I didn't name them). status only takes 2 values ('U' and 'S'), whereas zone can take any of about 100 values.
One subset of zone's values is (in pseudo-regexp) IN[0-7]Z, and those are first in the result. That's easy to do with a CASE.
zone can also take the value 'Future', which should appear LAST in the result.
In my typical kludgy-munge way, I have simply imposed a CASE value of 1000 as follows:
group by zone, status
order by (
case when zone='IN1Z' then 1
when zone='IN2Z' then 2
when zone='IN3Z' then 3
.
. -- other IN[X]Z etc
.
when zone = 'Future' then 1000
else 11 -- [number of defined cases +1]
end), zone, status
This works, but it's obviously a kludge, and I wonder if there might be one-liner doing the same.
Is there a cleaner way to achieve the same result?
Postgres allows boolean values in the ORDER BY clause, so here is your generalised 'X LAST':
ORDER BY (my_column = 'X')
The expression evaluates to boolean, resulting values sort this way:
FALSE (0)
TRUE (1)
NULL
Since we deal with non-null values, that's all we need. Here is your one-liner:
...
ORDER BY (zone = 'Future'), zone, status;
Related:
Sorting null values after all others, except special
Select query but show the result from record number 3
SQL two criteria from one group-by
I'm not familiar postgreSQL specifically, but I've worked with similar problems in MS SQL server. As far as I know, the only "nice" way to solve a problem like this is to create a separate table of zone values and assign each one a sort sequence.
For example, let's call the table ZoneSequence:
Zone | Sequence
------ | --------
IN1Z | 1
IN2Z | 2
IN3Z | 3
Future | 1000
And so on. Then you simply join ZoneSequence into your query, and sort by the Sequence column (make sure to add good indexes!).
The good thing about this method is that it's easy to maintain when new zone codes are created, as they likely will be.

How to get three count values from same column using SQL in Access?

I have a table that has an integer column from which I am trying to get a few counts from. Basically I need four separate counts from the same column. The first value I need returned is the count of how many records have an integer value stored in this column between two values such as 213 and 9999, including the min and max values. The other three count values I need returned are just the count of records between different values of this column. I've tried doing queries like...
SELECT (SELECT Count(ID) FROM view1 WHERE ((MyIntColumn BETWEEN 213 AND 9999));)
AS Value1, (SELECT Count(ID) FROM FROM view1 WHERE ((MyIntColumn BETWEEN 500 AND 600));) AS Value2 FROM view1;
So there are for example, ten records with this column value between 213 and 9999. The result returned from this query gives me 10, but it gives me the same value of 10, 618 times which is the number of total records in the table. How would it be possible for me to only have it return one record of 10 instead?
Use the Iif() function instead of CASE WHEN
select Condition1: iif( ), condition2: iif( ), etc
P.S. : What I used to do when working with Access was have the iif() resolve to 1 or 0 and then do a SUM() to get the counts. Roundabout but it worked better with aggregation since it avoided nulls.
SELECT
COUNT(CASE
WHEN MyIntColumn >= 213 AND MyIntColumn <= 9999
THEN MyIntColumn
ELSE NULL
END) AS FirstValue
, ??? AS SecondValue
, ??? AS ThirdValue
, ??? AS FourthValue
FROM Table
This doesn't need nesting or CTE or anything. Just define via CASE your condition within COUNTs argument.
I dont really understand what You want in the second, third an fourth column. Sounds to me, its very similar to the first one.
Reformatted, your query looks like:
SELECT (
SELECT Count(ID)
FROM view1
WHERE MyIntColumn BETWEEN 213 AND 9999
) AS Value1
FROM view1;
So you are selecting a subquery expression that is not related to the outer query. For each row in view1, you calculate the number of rows in view1.
Instead, try to do the calculation once. You just have to remove your outer query:
SELECT Count(ID)
FROM view1
WHERE MyIntColumn BETWEEN 213 AND 9999;
OLEDB Connection in MS Access does not support key words CASE and WHEN .
You can only use iif() function to count two, three.. values in same columns
SELECT Attendance.StudentName, Count(IIf([Attendance]![Yes_No]='Yes',1,Null)) AS Yes, Count(IIf([Attendance]![Yes_No]='No',1,Null)) AS [No], Count(IIf([Attendance]![Yes_No]='Not',1,Null)) AS [Not], Count(IIf([Attendance]![Yes_No],1,Null)) AS Total
FROM Attendance
GROUP BY Attendance.StudentName;

Avoiding the null values to replace 0 values in report

I am using SQL Server 2005 BOXIR2.
My doubt, from universe table there is an eventcode having different types of codes like Enquiry,FollowUp,LostofSales,Contact,etc
I make a measure that is from object properties formula count(Tablename.EventCode)save and export it, when I used this EventCode in Webireport, it show values for paricular EventCode, but zero values are not read it show null blank as below example .
I WANT TO GET THE ZERO VALUES FOR WHICH IT IS IN BLANK(NULL).
count(Tablename.EventCode)
Enquiry,FollowUp,LostofSales,Contact
10 20 15
5 12 5
6 4 3
Can u please help me how to get get zero values for null,Formula
I'm not sure exactly what you are asking, but I think you may be looking for ISNULL()
SELECT ISNULL(table_name.column_name, 0)
will return 0 if table_name.column_name is null
If you're getting NULLs when performing an aggregate, it's probably because one of elements is NULL. All you need to do is coalesce those entries to a known value (such as zero).
SELECT COUNT(COALESCE(Tablename.EventCode, 0)) FROM Tablename