Handle NULL values in SQL Server Reporting Services - sql-server

I have developed one report using SSRS Report Builder. The report contains several parameters in which one of the parameter contains NULL values apart from the values that it fetches from the database.
Now when I select NON-NULL values from that parameter the report runs fine because I'm using IN Clause (as the parameter values are multi-select). But when I select NULL (and not any other values) from that parameter, the IN clause doesn't works as IN doesn't take NULL.
Hence what/how should I modify my query so that it could handle both --- NULL and NON-NULL Values?

The following forces all NULLs to be included and makes it clear to the user of the report that the value is missing:
SELECT ISNULL([ColumnNameHere],'[ None ]') AS [ColumnNameHere]
WHERE ISNULL([ColumnNameHere],'[ None ]') IN (#MultiParam)

I have run into this problem before.
The solution is not to pass NULL, but to pass a default value that in reality, your DB never uses. It's not ideal, but seems to be a quirk of the implementation.
So, in the case where you're searching for ThingId. ThingId is a surrogate key that we know can never be >0.
You use the 0 as your value for any, and pass it instead of NULL, then change your query like so:-
WHERE
#ThingId = 0 OR ThingId = #ThingId

Related

Using a scalar-valued function as a parameter to another SVF

I have a handful of scalar-valued functions (which I appreciate may not be best practice, but for now, this will suffice, this is simply a development exercise) which I use within various computed columns with MS SQL tables.
I have a scenario whereby I need to use the result of one of these functions, as a parameter to another within a given computed column. For example;
(case when [DynamicQuantity] IS NULL then ([dbo].[GetFiatValue]([Currency],[Quantity])) ELSE ([dbo].[GetFiatValue]([Currency],([dbo].[getStakedQuantity]([currency])))) end)
If the given [DynamicQuantity] field is null (a column value generated by another SVF), use the [Quantity] field. However, if [DynamicQuantity] is populated, the ELSE will fire, and pass the output of the [dbo].[getStakedQuantity] SVF in place of the [Quantity] value.
Is this achievable? I can't seem to get the syntax to work and am questioning whether this is even possible?
Running it via a normal query works;
SELECT case when [DynamicQuantity] IS NULL then [dbo].[GetFiatValue]([Currency],[Quantity]) ELSE [dbo].[GetFiatValue]([Currency],[dbo].[getStakedQuantity]([currency])) end from dbo.cryptoPortfolio
But when used as a computed column formula below;
case when [DynamicQuantity] IS NULL then [dbo].[GetFiatValue]([Currency],[Quantity]) ELSE [dbo].[GetFiatValue]([Currency],[dbo].[getStakedQuantity]([currency])) end
SSMS will not accept it, with Error validating the formula for column 'FiatValue'.

SQL Server Data Masking bug with "FOR JSON PATH" clause

I'm working with a masked database on my QA server using SQL Server Standard (64-bit) 14.0.1000.169. This is my structure:
CREATE TABLE [dbo].[Test](
[Column1] [VARCHAR(64)] NULL,
[Column2] [VARCHAR(64)] NULL
)
GO
INSERT INTO [dbo].[Test]
VALUES ('ABCDEFG', 'HIJKLMN')
I've masked the column with the following code:
ALTER TABLE [dbo].[Test]
ALTER COLUMN [Column1] VARCHAR(64) MASKED WITH (FUNCTION = 'default()');
It works as expected when I perform the following query using a non-allowed user:
SELECT [Column1], [Column2]
FROM [dbo].[Test]
FOR JSON PATH
-- RESULT: '[{"Column1":"xxxx", "Column2":"HIJKLMN"}]'
But it doesn't work when the same non-allowed user saves the result in variable (the main goal):
DECLARE #var VARCHAR(64)
SET #var = (SELECT [Column1], [Column2] FROM [dbo].[Test] FOR JSON PATH)
SELECT #var --it should show a valid JSON...
-- RESULT: 'xxxx' <-- JSON LOSES ITS STRUCTURE
-- DESIRED RESULT: '[{"Column1":"xxxx", "Column2":"HIJKLMN"}]' <-- VALID JSON
Main problem: JSON looses its structure when a masked column appear in the SELECT and "FOR JSON PATH" clause is present.
We want to get a valid JSON even if the data column is masked or not, or even if sa user or not.
I've tested using NVARCHAR or doing a CAST in the masked column, but the only way we get the desired result is using a #tempTable before use the "FOR JSON PATH" clause.
How can I do for SELECT a masked column and save it to
VARCHAR variables without loose JSON structure?
Any help will be appreciated.
NOTE: The SA user is default allowed to see unmasked data (so the JSON doesn't loose its structure), but we want to execute it on a non-allowed user and return a valid JSON, not only 'xxxx'.
It does indeed appear to be a bug. Repro is here. Although see below, not so sure.
When using FOR JSON, or for that matter FOR XML, as a top level SELECT construct, a different code path is used as compared to placing it in a subquery or assigning it to a variable. This is one of the reasons for the 2033-byte limit per row in a bare FOR JSON.
What appears to be happening is that in the case of a bare FOR JSON, the data masking happens at the top of the plan, in a Compute Scalar operator just before the JSON SELECT operator. So the masking happens on just the one column.
PasteThePlan
Whereas when putting inside a subquery, a UDX function operator is used. The problem is that the Compute Scalar is happening after the UDX has created the JSON or XML, whereas it should have been pushed down below the UDX in the plan.
PasteThePlan
I suggest you file the bug with Microsoft, on the Azure Feedback site.
Having gone over this a little, I actually think now that it's not a bug. What does seem to be a bug is the case without nesting.
From the documentation:
Whenever you project an expression referencing a column for which a data masking function is defined, the expression will also be masked. Regardless of the function (default, email, random, custom string) used to mask the referenced column, the resulting expression will always be masked with the default function.
Therefore, when you select any masked column, even in a normal SELECT, if you use a function on the column then the masking always happens after any other functions. In other words, the masking is not applied when the data is read, it is applied when it is finally output to the client.
When using a subquery, the data is fed into a UDX function operator. The compiler now senses that the final resultset is a normal SELECT, just that it needs to mask any final result that came from the masked column. So the whole JSON is masked as one blob, similar to if you did UPPER(yourMaskedColumn). See the XML plan in this fiddle for an example of that.
But when using a bare FOR JSON, it appears to the compiler as a normal SELECT, just that the final output is changed to JSON (the top-level SELECT operator is different). So the masking happens before that point. This seems to me to be a bug.
The bug is even more egregious when you use FOR XML, which uses the same mechanisms. If you use a nested FOR XML ..., TYPE then you get just <masked /> irrespective of whether you nest it or not. Again this is because the query plan shows the masking happening after the UDX. Whereas if you don't use , TYPE then it depends if you nest it. See fiddle.

Merging two columns into one in sql

I have a table with two columns hora_entrada and hora_saida and when running this select statement:
select hora_entrada, hora_saida
from Controlo_de_Entrada_saidas
it shows this result:
What I want to do is to combine these two columns into one that if Hora_saida = "Não saiu", it shows the data in hora_entrada but if hora_saída has an actual hour in there, it shows the data in hora_saida instead.
So basically this should be my result:
I don't know if I'm making myself clear, I actually don't even know where to start or if its even possible but any help would be appreciated.
Try using a CASE expression:
SELECT
hora_entrada,
hora_saida,
CASE WHEN hora_saida = 'Não saiu'
THEN hora_entrada ELSE hora_saida END AS new_column
FROM yourTable;
This logic assumes that any value in the hora_saida column which is not Não saiu is a valid timestamp. If not, then we could add additional logic to check for this as well.
If you are open/able to changing your data model, you might want to consider just storing NULL values for the missing data. SQL Server (and most other databases as well) has a built-in function COALESCE, which can be used to replace NULL with a backup value. This would avoid needing to use a lengthy CASE expression as you see above.

SSRS error NUMBERORA-00932 expected CHAR got NUMBER and Memory corrupted while trying to display all when parameter is null

I know there are existing questions regarding to this error. but my situation looks different!
I'm creating a SSRS2012 report that can manually input UserIDs number (such as 15, 130, etc..) as the IC_ID. I want to ensure when input is null, report can display all IC_ID. I tried two method to approach it.
Data source: Oracle Sql Developer, table:IC, column: IC_ID datatype:NUMBER
Parameter: UserIDs, datatype: Integer, allow null value.
------------Method 1:---------------
SSRS dataset query, I've tried:
select * from IC
where IC_ID=to_number(COALESCE(:UserIDs, IC_ID))
select * from IC
where IC_ID=to_char(COALESCE(:UserIDs, IC_ID))
select * from IC
where IC_ID=COALESCE(:UserIDs, IC_ID)
But all failed with same error: NUMBERORA-00932 expected CHAR got NUMBER. When I run them in Oracle database, show same error as above. So I guess the problem is how to make data type consistent, column IC_ID data type is NUMBER, if I set parameter UserIDs as Integer, why still not consistent?
PS:when I only use query as:
**select * from IC where IC_ID=(:UserIDs)**
no matter I set parameter datatype to text or integer, the report works fine and display info of whatever IC_ID value I manually input. But it didn't show data when parameter is null!
---------Method 2---------
SSRS dataset query, I've tried:
select * from IC
where IC_ID=(:UserIDs) or (:UserIDs) IS NULL
failed with error: attempted to read or write protected memory. this is often an indication that other memory is corrupt. When I run this query in Oracle, it run well with not error, no matter I pass a number or null to (:UserIDs). So I guess the problem is how to use (:UserIDs) TWICE in SSRS dataset query.
I'm very sure it's not because of memory limit, because this report run well displaying all IC_ID info when there is no parameter at all. It seems it doesn't allow one parameter to show up more than once in dataset query, whenever I tried something like: select * from IC where IC_ID=(:UserIDs) AND IC_SubID=(:UserIDs), it will fail with memory corrupt error.
But it works fine if (:UserIDs) only show up once in query.
---------Method 3----------------
Some posts said in order to display all info when parameter is null, we can add a list of all IC_ID as the default value of parameter. I don't think it works for this case, because my dataset query is actually very complex, starting with a long cte. It will be too complex and slow to add this whole cte query into the list as default value.
I would appreciate if anyone could give some help of how to solve any of above errors, or tell me other ways to display all info while parameter is null!
Thank you
Isn't possible to cast the parameter as an integer? Like cast((:UserIDs as int)) (or maybe cast as a char). Don't know if this helps but i would expect that the datatype is always the same.
Or you could maybe use
(:UserIDs) in (case when (:UserIDs) is null then (select UserIDs from table) else (:UserIDs) end)

SSRS 2005 parameters not returning any results when null checked

newbie to SSRS, running 2005. I hope someone can help!
I have a report with a parameter on it, ItemNum. I have it as a string, to allow NULL value and allow blank value, nothing in Available values, and null selected for Default Values. I then have in there WHERE part of my SELECT statement where item_num IN (#ItemNum).
when I preview the report, I get NO results if I:
leave the NULL checkbox checked (and obviously the Item # parameter empty, it's greyed out) or
uncheck the NULL checkbox but don't put a value in the parameter.
the ONLY way I can get ANY results back is to enter a valid item number.
I would think that if you Allow Null values and/or Allow Blank values, it should just disregard that parameter. Perhaps it's because of it being in the where clause?
how can I make this function the way I need it to, i.e. if the NULL checkbox is checked (or unchecked and NO value entered) disregard that parameter (or in other words, return all records)?
also -- as a 2nd question -- is it possible to use the LIKE comparison with a parameter or does it always have to be IN?
Try changing your WHERE clause to:
WHERE #ItemNum is null OR item_num IN (#ItemNum)
You need to explicitly check for NULL in this case.
In fact, why are you using IN for a single value? It's usually used to check for a number of values, e.g. WHERE item_num IN (1, 2, 3, 5, 10). A simple = would suffice.

Resources