Is it possible to use ISNULL twice for the same column?
ISNULL(ISNULL(column, SELECT sum(column2) FROM table WHERE type = '1')), SELECT sum(column2) FROM table WHERE type = '2'))
Or should I be doing this in a different way with IF ELSE somehow? How would that look like?
Look at the coalesce operator. Your query then becomes:
COALESCE(column,
(SELECT sum(column2) FROM table WHERE type = '1'),
(SELECT sum(column2) FROM table WHERE type = '2'))
It returns the first non-null result from its arguments.
Yes, it is possible.
(The issue I see with the expression in the question is unbalanced parens; two extra closing parens)
The ISNULL function takes two arguments. Either (or both) of those arguments can be expressions, and an ISNULL function is an expression. So yes, you can nest ISNULL functions two, three, four, or more levels deep as you need.
A SELECT statement that returns a single row containing a single column can (usually) be used as an expression. And multiple SELECT statements (subqueries) within a query can reference the same table(s) and same column(s).
In many cases, when we see a query of this type, it points either to a model that isn't working well, or it points to looking at a different way of getting an equivalent result.
EDIT
As other answers have pointed out, the more portable, ANSI-standard compliant COALESCE function can be used to return an equivalent result.
These three expressions are equivalent:
ISNULL(ISNULL(a,b),c)
ISNULL(a,ISNULL(b,c))
COALESCE(a,b,c)
which are also equivalent to these (unnecessarily redundant) expressions:
COALESCE(COALESCE(a,b),c)
COALESCE(a,(COALESCE(b,c))
Test case:
create table tst (id int, a int, b int, c int);
insert into tst values
(1,NULL,NULL,NULL)
,(2,21,NULL,NULL)
,(3,NULL,32,NULL)
,(4,NULL,NULL,43)
,(5,51,52,NULL)
,(6,61,NULL,63)
,(7,NULL,72,73)
,(8,81,82,83);
-- SQL Server
SELECT ISNULL(ISNULL(a,b),c) AS t1
, ISNULL(a,ISNULL(b,c)) AS t2
, COALESCE(a,b,c) AS t3
FROM tst
ORDER BY id
-- MySQL
SELECT IFNULL(IFNULL(a,b),c) AS t1
, IFNULL(a,IFNULL(b,c)) AS t2
, COALESCE(a,b,c) AS t3
FROM tst
ORDER BY id
-- Oracle
SELECT NVL(NVL(a,b),c) AS t1
, NVL(a,NVL(b,c)) AS t2
, COALESCE(a,b,c) AS t3
FROM tst
ORDER BY id
Try using COALESCE (if you are using SQL Server). http://msdn.microsoft.com/en-us/library/ms190349.aspx
It will give you the first non-null value from a list of values. Do you need more help structuring the SQL also?
Related
We have a PostgreSQL database where I have two tables with column text[] datatype. When I use an inner join to get details I do not see it is matching with any row.
for e.g.
create table test{
names text[],
id
}
create table test_b{
names text[],
id
}
now when I run below query,
SELECT t.* from test t inner join test_b tt
where t.names=tt.names;
I don't see any results. I even tried normal query with
SELECT * FROM test where names='{/test/test}';
It also did not work.
Any suggestions?
I suspect that you want array overlap operator &&. Also, since you don't need to return anything from test_b, using exists with a correlated subquery is more relevant than a join:
select t.*
from test t
where exists (select 1 from test_b b where t.names && b.names)
I observe a behaviour with CTEs which I did not expect (and seems inconsistent).
Not quite sure that it is correct...
Basically, through a CTE, I filter rows to avoid a particular problem, then use the result of that CTE to perform calculations that would break on the problematic rows which I thought I eliminated in my CTE...
Take a simple table with a varchar column that often has a number in it, but not always
CREATE TABLE MY_TABLE(ROW_ID INTEGER NOT NULL
, GOOD_ROW BOOLEAN NOT NULL
, SOME_VALUE VARCHAR NOT NULL);
INSERT INTO MY_TABLE(ROW_ID, GOOD_ROW, SOME_VALUE)
VALUES(1, TRUE, '1'), (2, TRUE, '2'), (3, FALSE, 'ABC');
I also create a small table with just numbers to join on
CREATE TABLE NUMBERS(NUMBER_ID INTEGER NOT NULL);
INSERT INTO NUMBERS(NUMBER_ID) VALUES(1), (2), (3);
Joining these two tables on SOME_VALUE results in an error because 'ABC' is not numeric and it appears that the JOIN is evaluated BEFORE the WHERE clause (BAD implications on performance here...)
SELECT *
FROM MY_TABLE
INNER JOIN NUMBERS ON NUMBERS.NUMBER_ID = TO_NUMBER(SOME_VALUE)
WHERE ROW_ID < 3; --> ERROR
So, I try to filter my first table through a CTE which only return rows for which SOME_VALUE is numeric
WITH ONLY_GOOD_ONES
AS (
SELECT SOME_VALUE
FROM MY_TABLE
WHERE GOOD_ROW = TRUE
)
SELECT *
FROM ONLY_GOOD_ONES;
Now, I would expect to be able to use the result of this CTE with SOME_VALUE being numeric.
WITH ONLY_GOOD_ONES
AS (
SELECT SOME_VALUE
FROM MY_TABLE
WHERE GOOD_ROW = TRUE
)
SELECT *
FROM ONLY_GOOD_ONES
INNER JOIN NUMBERS ON NUMBERS.NUMBER_ID = TO_NUMBER(SOME_VALUE);
Miracle!!!
It worked!
I get my 2 expected records.
So far so good...
However, if I had defined my CTE slightly differently (WHERE clause which filters the same records)
WITH ONLY_GOOD_ONES
AS (
SELECT SOME_VALUE
FROM MY_TABLE
WHERE ROW_ID < 3
)
SELECT *
FROM ONLY_GOOD_ONES;
This CTE returns exactly the same thing as before
But if I try to join, it Fails!
WITH ONLY_GOOD_ONES
AS (
SELECT *
FROM MY_TABLE
WHERE ROW_ID < 3
)
SELECT *
FROM ONLY_GOOD_ONES
INNER JOIN NUMBERS ON NUMBERS.NUMBER_ID = TO_NUMBER(SOME_VALUE);
I get the following error...
SQL Error [100038] [22018]: Numeric value 'ABC' is not recognized
Is there a particular explanation to this second version of the CTE behaving differently???
The actual answer is because snowflake does not follow the SQL standard, and execute SQL in the order given.
They apply transforms to data prior to filtering when there optimizer decides it wants to.
So for your table MY_TABLE when you do
SELECT some_value::NUMBER FROM my_table WHERE row_id IN (1,2);
You will under some cases have the as_number cast happen on all row, and explode on the 'ABC'. Which is violating SQL rules, that WHERE are evaluated before SELECT transforms are done, but Snowflake have known this for years, and it's intentional, as it makes things run faster.
The solution is to understand you have mixed data and therefore assume the code can and will be ran out of order, and thus use the protective versions of the functions like TRY_TO_NUMBER
The kicker is you can write a few nested SELECTs to avoid the problem and then put something like a window funcation around the code and the optimizer jump back into this behavour and you SQL explodes again. Thus the solution is to understand if you have mixed data, and handle it. Oh and complain it's a bug.
This is because you're getting a different execution plan with the different queries.
Here's how the query is executed with the working query:
... and here is how it's executed with the query generating a failure. The error comes from the fact that the join filter is applied directly on the table scan before the ROW_ID < 3 filter is applied, compared to the working query.
You can see these plans under history, clicking the query id and then the 'profile' tab.
It looks like the join filter is applied so early, maybe because of a wrong estimation. When I run the queries on my test database, they completed without any error.
To overcome the issue, you can always "Error-handling Conversion Functions":
SELECT *
FROM MY_TABLE
INNER JOIN NUMBERS ON NUMBERS.NUMBER_ID = TRY_TO_NUMBER(SOME_VALUE)
WHERE ROW_ID < 3;
More information:
https://docs.snowflake.com/en/sql-reference/functions-conversion.html#label-try-conversion-functions
In Sql Server, NULL IS NOT EQUAL TO NULL.(Why does NULL = NULL evaluate to false in SQL server)
Then why the following code returns single NULL.
CREATE TABLE #TEMP1
(ID INT)
INSERT INTO #TEMP1
SELECT NULL
UNION ALL
SELECT NULL
SELECT DISTINCT ID FROM #TEMP1
DROP TABLE #TEMP1
ID
------
NULL
I expected
ID
------
NULL
NULL
The handling of NULLs for DISTINCT comparison is clearly called out in the documentation. Excerpt:
DISTINCT
Specifies that only unique rows can appear in the result set.
Null values are considered equal for the purposes of the DISTINCT
keyword.
Not only is it called out in the documentation, it’s a different kind of equality test. Does unknown equal unknown? Who knows, maybe yes, maybe no. But that is exactly why Distinct should return only 1. If it returned 2 different values, that would be saying that the unknowns were different. There exists at least one unknown value so, it should be in the list, whether it is different from any of the other unknown values is, well, unknown.
Null values compare as equal for Intersect as well.
Select 1
Where exists (select null intersect select null)
I want to compare two multiple value strings with each other to see if one of the values exists in the other string.
I have a table with a nvarchar row with pipe separated values, e.g.
'value1|value2|value3'
I also have an nvarchar variable with a comma separated string, e.g.
'value2,value3'
until now the column in the table had one value, I used a table function to spit the string in the variable and used the IN clause to see if the value was in the generated table. e.g.
select * from table1
WHERE column in (select val from dbo.split(#variable,','))
this won't work if the column also contains more values.
select * from table1
WHERE (select val from dbo.split(column,'|')) in (select val from dbo.split(#variable,','))
here it tries to compare 2 generated tables with each other which fails. I have tried this using joins, but can't find a way to properly do this. I'm using MSSQL 2008R2
Maybe this can help you:
select * from table1 where exists
(select * from
(select val from dbo.split(table1.column,'|')) a,
(select val from dbo.split(#variable,',')) b
where a.val=b.val)
I just answered this: Generate scripts with new ids (also for dependencies)
My first attempt was this:
DECLARE #Form1 UNIQUEIDENTIFIER=NEWID();
DECLARE #Form2 UNIQUEIDENTIFIER=NEWID();
DECLARE #tblForms TABLE(id UNIQUEIDENTIFIER,FormName VARCHAR(100));
INSERT INTO #tblForms VALUES(#Form1,'test1'),(#Form2,'test2');
DECLARE #tblFields TABLE(id UNIQUEIDENTIFIER,FormId UNIQUEIDENTIFIER,FieldName VARCHAR(100));
INSERT INTO #tblFields VALUES(NEWID(),#Form1,'test1.1'),(NEWID(),#Form1,'test1.2'),(NEWID(),#Form1,'test1.3')
,(NEWID(),#Form2,'test2.1'),(NEWID(),#Form2,'test2.2'),(NEWID(),#Form2,'test2.3');
--These are the originalIDs
SELECT frms.id,frms.FormName
,flds.id,flds.FieldName
FROM #tblForms AS frms
INNER JOIN #tblFields AS flds ON frms.id=flds.FormId ;
--The same with new ids
WITH FormsWithNewID AS
(
SELECT NEWID() AS myNewFormID
,*
FROM #tblForms
)
SELECT frms.myNewFormID, frms.id,frms.FormName
,NEWID() AS myNewFieldID,flds.FieldName
FROM FormsWithNewID AS frms
INNER JOIN #tblFields AS flds ON frms.id=flds.FormId
The second select should deliver - at least I thought so - two values in "myNewFormID", each three times... But it comes up with 6 different values. This would mean, that the CTE's "NEWID()" is done for each row of the final result set. What am I missing?
Your understanding of CTEs is wrong. They are not simply a table variable that's filled with the results of the query - instead, they are a query on their own. Note that CTEs can be used recursively - this would be quite a sight with table variables :)
From MSDN:
A common table expression (CTE) can be thought of as a temporary result set that is defined within the execution scope of a single SELECT, INSERT, UPDATE, DELETE, or CREATE VIEW statement. A CTE is similar to a derived table in that it is not stored as an object and lasts only for the duration of the query. Unlike a derived table, a CTE can be self-referencing and can be referenced multiple times in the same query.
The "can be thought" of is a bit deceiving - sure, it can be thought of, but it's not a result set. You don't see this manifesting when you're only using pure functions, but as you've noticed, newId is not pure. In reality, it's more like a named subquery - in your example, you'll get the same thing if you just move the query from the CTE to the from clause directly.
To illustrate this even further, you can add another join on the CTE to the query:
WITH FormsWithNewID AS
(
SELECT NEWID() AS myNewFormID
,*
FROM #tblForms
)
SELECT frms.myNewFormID, frms.id,frms.FormName
,NEWID() AS myNewFieldID,flds.FieldName,
frms2.myNewFormID
FROM FormsWithNewID AS frms
INNER JOIN #tblFields AS flds ON frms.id=flds.FormId
left join FormsWithNewID as frms2 on frms.id = frms2.id
You'll see that the frms2.myNewFormID contains different myNewFormIDs.
Keep this in mind - you can only treat the CTE as a result set when you're only using pure functions on non-changing data; in other words, if executing the same query in a serializable transaction isolation level twice will produce the same result sets.
NEWID() returns a value every time it is executed. Whenever you use it you get a new value
For example,
select top 5 newid()
from sys.tables
order by newid()
You will not see them order by because the selected field is produced with different values than the Order By field