I want to write a simple calculation to return a value based on a heirarchy of fields.
If the first field is empty, I want it to return the second, and if the second is empty, the third. I have tried the following but it only returns the first value.
If (IsEmpty (Field1 = 1) ; Field2;
If (IsEmpty (Field2 = 1); Field3; Field1))
I was able to get the first or third value to appear by using:
If (IsEmpty (Field1) &
If (IsEmpty (Field2); Field3; Field1))
But of course this doesn't show the Field2 at all.
Is there something along the lines of:
If (IsEmpty (Field1) &
If (IsEmpty (Field2); Field3; Field1, Field2))
which I can use? This obviously doesn't work because there are too many parameters in the function.
Any help would be very much appreciated! :-)
You need to nest your calc a bit more :
Case (
IsEmpty ( Field1 & Field2 ) ; Field3 ;
IsEmpty ( Field1 ) ; Field2 ;
Field1
)
In your examples, you had IsEmpty (Field1 = 1) which will test Field1=1, which is either True or False, but never empty. And the & is a concatentation operator, if you're wanting logical and then use and instead.
You could rewrite this in a more transparent way:
Case(
not isEmpty(Field1); Field1;
not isEmpty(Field2); Field2;
Field3
)
(this would be much easier to maintain/read in the future)
Related
In TSQL, the string in the database record is 'A/A/A' or 'A/B/A' (examples). I want to parse the string and for the first instance return '1'; in the 2nd instance, return '2'. That is, if all the values between the separators are the same, return a value; otherwise return another value. What is the best way to do this?
A bit blind answer:
Read the whole value in a variable. Read the first value part in another:
declare #entire nvarchar(max), #single nvarchar(max)
select/set #entire=....
set #single=left(#entire,charindex('/',#entire)-1)
Compare entire with #single replicated after removing slashes:
set #entire=replace(#entire,'/','')
select case when replicate(#single,len(#entire)/len(#single))=#entire
then 1 else 0 end as [What you want]
Something like this should work:
SELECT
x.*,
CASE
WHEN N > 1 THEN 0
ELSE 1
END Result
FROM (
SELECT
t.Column1,
t.Column2,
t.Column3,
t.SomeColumn,
COUNT(DISTINCT s.value) N
FROM dbo.YourTable t
OUTER APPLY STRING_SPLIT(t.SomeColumn,'/') s
GROUP BY
t.Column1,
t.Column2,
t.Column3,
t.SomeColumn
) x
;
Based on your simple example (no edge cases accounted for) the following should work for you:
select string, iif(replace(s,v,'')='',1,0) as Result
from t
cross apply (
values(left(string,charindex('/', string)-1),(replace(string,'/','')))
)s(v,s);
Example Fiddle
original query looks like this :
UPDATE reponse_question_finale t1, reponse_question_finale t2 SET
t1.nb_question_repondu = (9-(ISNULL(t1.valeur_question_4)+ISNULL(t1.valeur_question_6)+ISNULL(t1.valeur_question_7)+ISNULL(t1.valeur_question_9))) WHERE t1.APPLICATION = t2.APPLICATION;
I know you cannot update 2 tables in a single query so i tried this :
UPDATE reponse_question_finale t1
SET nb_question_repondu = (9-(COALESCE(t1.valeur_question_4,'')::int+COALESCE(t1.valeur_question_6,'')::int+COALESCE(t1.valeur_question_7)::int+COALESCE(t1.valeur_question_9,'')::int))
WHERE t1.APPLICATION = t1.APPLICATION;
But this query gaves me an error : invalid input syntax for integer: ""
I saw that the Postgres equivalent to MySQL is COALESCE() so i think i'm on the good way here.
I also know you cannot add varchar to varchar so i tried to cast it to integer to do that. I'm not sure if i casted it correctly with parenthesis at the good place and regarding to error maybe i cannot cast to int with coalesce.
Last thing, i can certainly do a co-related sub-select to update my two tables but i'm a little lost at this point.
The output must be an integer matching the number of questions answered to a backup survey.
Any thoughts?
Thanks.
coalesce() returns the first non-null value from the list supplied. So, if the column value is null the expression COALESCE(t1.valeur_question_4,'') returns an empty string and that's why you get the error.
But it seems you want something completely different: you want check if the column is null (or empty) and then subtract a value if it is to count the number of non-null columns.
To return 1 if a value is not null or 0 if it isn't you can use:
(nullif(valeur_question_4, '') is null)::int
nullif returns null if the first value equals the second. The IS NULL condition returns a boolean (something that MySQL doesn't have) and that can be cast to an integer (where false will be cast to 0 and true to 1)
So the whole expression should be:
nb_question_repondu = 9 - (
(nullif(t1.valeur_question_4,'') is null)::int
+ (nullif(t1.valeur_question_6,'') is null)::int
+ (nullif(t1.valeur_question_7,'') is null)::int
+ (nullif(t1.valeur_question_9,'') is null)::int
)
Another option is to unpivot the columns and do a select on them in a sub-select:
update reponse_question_finale
set nb_question_repondu = (select count(*)
from (
values
(valeur_question_4),
(valeur_question_6),
(valeur_question_7),
(valeur_question_9)
) as t(q)
where nullif(trim(q),'') is not null);
Adding more columns to be considered is quite easy then, as you just need to add a single line to the values() clause
I have been working on figuring out how to accomplish the following for a few days now:
I have one table which has alternate addresses, but I only need to pull the city. However the city might be in field5,field4, or field3. What I would like to do, and have failed miserably, is to populate another field called city with the value found through evaluating the three fields for <> NULL.
This is what I have so far, although I'm not receiving any errors, the only value that appears is field4, since field5 is null or ''. But if field4 is also null, the city field is blank. For some reason my query is not looking at field3 and if there is a value, the value won't populate. Help please!
This is the 1st attempt:
case when altcity='Y' and (field5 IS null OR field5=' ' )
then field4 else
case when altcity='Y' and (field4 IS null OR field4=' ')
then field3
else field5
end
end as city <- Field5 or Field4 appears as it should, but if both fields are null, the field is blank. Looks as if the query doesn't look at field3.
Here is the second attempt:
case when altcity='y' then coalesce(field5,field4,field3) end as city
Same thing here, the value in field5 or field4 is populated, but the value for field3 does not.
Thank you already very much for assisting!
A bit long handed but what does the following look like when run against your data:
CASE
WHEN altcity='y' THEN
CASE
WHEN field5 is not null AND field5 != '' THEN field5
WHEN ((field5 is null) OR (field5 = '')) and ((field4 is not null) AND (field4 != '')) then field4
WHEN (((field5 is null) OR (field5 = '')) and ((field4 is null) OR (field4 = ''))) and ((field3 is not null) AND (field3 != '')) then field3
ELSE '?'
END
END
Having seen an example of your data it now makes sense that the COALESCE didn't work as that is reliant on NULL's in the data. In C# there is a string function IsNullOrEmpty that handles these cases. T-SQL doesn't have this but there are examples like this that help create that functionality and could tidy up the case statement
Which statement is perfect or better when dealing with billion of records for comparing NULL's in merge statement. I have tried with SET ANSI_NULLS OFF but that didn't work in merge statement. Here is my two ways
ISNULL(SRCColumn,-11111) = ISNULL(DSTColumn, -11111)
Or
SRCColumn = DSTColumn OR (SRCColumn IS NULL AND DSTColumn IS NULL)
Please let me know if there is any better way to deal with it. As I have around 15 columns to compare.
SRCColumn = DSTColumn OR (SRCColumn IS NULL AND DSTColumn IS NULL)
I'd suggest that you use this version because it most accurately expresses what you want SQL Server to do.
Both statements are logically equivalent (unless -11111 is a legal value for the column), however this statement is much more recognizable, and there's probably only a negligible difference in the run-time performance of the two statements.
If you are more concerned with succinctness than performance, CHECKSUM() is also an option. It will match NULL -> NULL:
MERGE A
USING B
ON A.Key = B.Key
WHEN MATCHED AND CHECKSUM(A.Col1, A.Col2, ... ) <> CHECKSUM(B.Col1, B.Col2, ... )
THEN UPDATE SET Col1 = B.Col1, Col1 = B.Col2, ...
How about using the NOT comparison on matching:
MERGE [TGT]
USING [SRC]
ON [SRC].Key = [TGT]. Key
…
WHEN MATCHED AND
(
NOT ([TGT].[dw_patient_key] = [SRC].[dw_patient_key] OR ([TGT].[dw_patient_key] IS NULL AND [SRC].[dw_patient_key] IS NULL))
OR NOT ([TGT].[dw_patient_key] = [SRC].[dw_patient_key] OR ([TGT].[dw_patient_key] IS NULL AND [SRC].[dw_patient_key] IS NULL))
...
)
THEN UPDATE
...
I have written a recursive function and depending on the output I need to select different fields. My question is now, how can I do this multiple times without having to call the function more then once? What I'm doing right now is just using the CASE WHEN... condition and checking every time what the functions return. (This is only a pseudo code and doesn't do anything real, it's just for understanding)
SELECT
id,
(CASE WHEN (function(id) > 0)
THEN field1
ELSE field2
END) as value1,
(CASE WHEN (function(id) > 0)
THEN field3
ELSE field4
END) as value2,
(CASE WHEN (function(id) > 0)
THEN field5
ELSE field6
END) as value3
FROM table1
...
How can I optimize this query and call the function only once?
Thanks in advance!
If the function is declared IMMUTABLE, it is safe to call it many times, as it will not be reevaluated.
From the docs:
IMMUTABLE indicates that the function cannot modify the database and always returns the same result when given the same argument values; that is, it does not do database lookups or otherwise use information not directly present in its argument list. If this option is given, any call of the function with all-constant arguments can be immediately replaced with the function value.
use a subquery :
SELECT foo, bar, result
FROM (
SELECT ..., function(id) AS result
....
) as tmp
You may be able to use some funky tuple thing like:
SELECT id,
CASE WHEN function(id) > 0
THEN (field1, field3, field5)
ELSE (field2, field4, field6)
END as (value1, value2, value3)
but I have no experience with this syntax