Passing default parameter value vs not passing parameter at all? - sql-server

Here's what I want to do:
Given a table
PeopleOutfit (id int primary key, boots int, hat int)
And a stored procedure
UpdateOutfit #id int, #newBoots int = null, #newHat = null
Is there a way to tell whether I called this procedure as
exec UpdateOutfit #id=1, #newBoots=null, #newHat=5
effectively telling that the person with id of 1 must now be barefoot and wear fifth hat from
exec UpdateOutfit #id=1, #newHat=5
that instructs this person to wear fifth hat keeping his current boots?
In other words, I want to tell (within the stored procedure) if "the default value was used because it was not specified" from "I explicitly called this procedure passing the value that happens to be the same as default one".
I know there are several ways of accomplishing what I want to do such as passing XML or bitmask of fields being updated, but for the moment I just want to make sure whether this exact technique is possible or not.
Edit: Passing reserved values does not work for fields with small range types such as bit. Overloading procedures is also an option that's not acceptable. Creating user-defined type that extends NULL paradigm with additional "NotAValue" value might be an answer, but I need some more guidance on how to implement it.

My guess is no, you can't tell those two things apart.
My suggestion is to use a default value that you would never pass in as an argument. i.e. if the default is null, then maybe you could pass in 0 as the value for #newBoots

no, the default null "looks" the same as a passed in null
possibly make your default -1 and use logic to do something different.

Strictly, no, there's no real facility to accomplish this. You could, however, try using some sort of reserved value for the parameter (a very small negative number, for example) to indicate this.

Never done this myself; introduced the 3 state bit (using an integer) into some code to handle the bit situation. I don't have access to a sql server but i do like latteral thinking sometimes; but i think you might be able to figure it out ysubg a string manipulation over some management views / functions. You would need to run with a heck of a lot of privilege but if its absolutely neccessary i don;t see why you cant work it out from st.text using something like this
SELECT
st.text
FROM
sys.dm_exec_requests r
CROSS APPLY
sys.dm_exec_sql_text(sql_handle) AS st
WHERE
r.session_id = ##SPID

As stated, TSQL doesn't distinguish between supplying the default value and not supplying a value. I think the engine basically substitutes the default values for any missing parameters (or params called with the DEFAULT keyword.)
Instead, use 0 as "No Hat", and NULL as no parameter specified. This is the prefered use of NULL, where it means value unknown or not specified. By using NULL as "No Hat", you've co-opted it into adding an extra value to the range of your data type.
Think of it in terms of the BIT datatype. The datatype is defined to represent a binary value (1 or 0, or T/F if you prefer to think of it as a boolean.) By treating NULL as a valid value, you have extended the datatype beyond the binary options (now have three options, 1/0/NULL.) My recommendation is always that if you find you've run out of values in the current datatype, you're using too small a type.
Back to the stored procedure calling; if you set your default values to NULL, and treat NULL as unset or not specified, then callers should always specify a non-null value when calling the proc. If you get a NULL, assume they didn't supply a value, supplied a NULL, or used the DEFAULT keyword.

Related

ORA-22835: Buffer too small and ORA-25137: Data value out of range

We are using a software that has limited Oracle capabilities. I need to filter through a CLOB field by making sure it has a specific value. Normally, outside of this software I would do something like:
DBMS_LOB.SUBSTR(t.new_value) = 'Y'
However, this isn't supported so I'm attempting to use CAST instead. I've tried many different attempts but so far these are what I found:
The software has a built-in query checker/validator and these are the ones it shows as invalid:
DBMS_LOB.SUBSTR(t.new_value)
CAST(t.new_value AS VARCHAR2(10))
CAST(t.new_value AS NVARCHAR2(10))
However, the validator does accept these:
CAST(t.new_value AS VARCHAR(10))
CAST(t.new_value AS NVARCHAR(10))
CAST(t.new_value AS CHAR(10))
Unfortunately, even though the validator lets these ones go through, when running the query to fetch data, I get ORA-22835: Buffer too small when using VARCHAR or NVARCHAR. And I get ORA-25137: Data value out of range when using CHAR.
Are there other ways I could try to check that my CLOB field has a specific value when filtering the data? If not, how do I fix my current issues?
The error you're getting indicates that Oracle is trying to apply the CAST(t.new_value AS VARCHAR(10)) to a row where new_value has more than 10 characters. That makes sense given your description that new_value is a generic audit field that has values from a large number of different tables with a variety of data lengths. Given that, you'd need to structure the query in a way that forces the optimizer to reduce the set of rows you're applying the cast to down to just those where new_value has just a single character before applying the cast.
Not knowing what sort of scope the software you're using provides for structuring your code, I'm not sure what options you have there. Be aware that depending on how robust you need this, the optimizer has quite a bit of flexibility to choose to apply predicates and functions on the projection in an arbitrary order. So even if you find an approach that works once, it may stop working in the future when statistics change or the database is upgraded and Oracle decides to choose a different plan.
Using this as sample data
create table tab1(col clob);
insert into tab1(col) values (rpad('x',3000,'y'));
You need to use dbms_lob.substr(col,1) to get the first character (from the default offset= 1)
select dbms_lob.substr(col,1) from tab1;
DBMS_LOB.SUBSTR(COL,1)
----------------------
x
Note that the default amount (= length) of the substring is 32767 so using only DBMS_LOB.SUBSTR(COL) will return more than you expects.
CAST for CLOB does not cut the string to the casted length, but (as you observes) returns the exception ORA-25137: Data value out of range if the original string is longert that the casted length.
As documented for the CAST statement
CAST does not directly support any of the LOB data types. When you use CAST to convert a CLOB value into a character data type or a BLOB value into the RAW data type, the database implicitly converts the LOB value to character or raw data and then explicitly casts the resulting value into the target data type. If the resulting value is larger than the target type, then the database returns an error.

Handle NULL values in SQL Server Reporting Services

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

What to use when not using Boolean type?

Ok this might be a brain dead question but I am curious.
I often find I have a situation where I need to store 1 of 3 values in a database. For example if my application asks "Is this house for sale?", the user needs to be able to say "yes", "no" but sometimes the user doesn't know. So there is also "I don't know".
I am always tempted to set my data type as Boolean, but of course that only gives me yes or no and usually this is set to no for default.
I am curious as to what data type is set as common practice in this scenario?
I could use an integer and store "1", "2" or "3". Or I could store a string value, or , or ,or.
This is probably a silly question and there maybe a million ways that this is done. but if anyone knows a best practices method and why that would be helpful. Thanks.
In a database a boolean value can actually expresses three states - true, false, and NULL (provided you allow NULL in the column).
Use NULL for "I don't know".
(And probably set the default for the column to NULL as well)
EDIT: Thinking about this for a moment though, this can be problematic depending on your use case. Some high level languages (Java, for one) would end up converting the NULL to false in the query result set.
::shrug:: Use a varchar(1) ('t','f','u') or the smallest integer value available (for space considerations) ... either is fine.
Using an enum is another option, but be aware that it isn't portable (Oracle being the notable problem child).
You could use NULL for "I don't know". You should still be able to have TRUE and FALSE for your boolean type column.
Also you can use
ENUM('first','second','third')
it's can be helpful not just with integer numbers, and with string values too
I'd advise against using NULL for "I don't know" for the following reasons:
In code, you typically check the value of a bool by asking something like this:
IF myBool THEN...
Or
IF NOT myBool THEN...
...as such, you can't tell the difference between false and 'i don't know'
Additionally, I've found it to be a best-practice for my own situation to always have a default value (false) for bools in my database. One direct effect of nulls in a bool field can be errors in 3rd-party tools (such as MS Access making an ODBC connection to a SQL-Server backend) that can't handle nulls in bool fields gracefully / accurately.
So as for what I do recommend -- I'd say find your smallest number data type (like a tinyint in TSQL) and go with that. Then you can have 0 = false, 1 = true, 2 = IDK.
This depends on what data types the server supports. Every data type that is 1 byte can hold up to 255 different values. In MySQL for example, you can use TINYINT. Define numeric constants in your code and use the smalled integer representation possible.

SQL Server: Null VS Empty String

How are the NULL and Empty Varchar values stored in SQL Server. And in case I have no user entry for a string field on my UI, should I store a NULL or a '' ?
There's a nice article here which discusses this point. Key things to take away are that there is no difference in table size, however some users prefer to use an empty string as it can make queries easier as there is not a NULL check to do. You just check if the string is empty. Another thing to note is what NULL means in the context of a relational database. It means that the pointer to the character field is set to 0x00 in the row's header, therefore no data to access.
Update
There's a detailed article here which talks about what is actually happening on a row basis
Each row has a null bitmap for columns that allow nulls. If the row in
that column is null then a bit in the bitmap is 1 else it's 0.
For variable size datatypes the acctual size is 0 bytes.
For fixed size datatype the acctual size is the default datatype size
in bytes set to default value (0 for numbers, '' for chars).
the result of DBCC PAGE shows that both NULL and empty strings both take up zero bytes.
Be careful with nulls and checking for inequality in sql server.
For example
select * from foo where bla <> 'something'
will NOT return records where bla is null. Even though logically it should.
So the right way to check would be
select * from foo where isnull(bla,'') <> 'something'
Which of course people often forget and then get weird bugs.
The conceptual differences between NULL and "empty-string" are real and very important in database design, but often misunderstood and improperly applied - here's a short description of the two:
NULL - means that we do NOT know what the value is, it may exist, but it may not exist, we just don't know.
Empty-String - means we know what the value is and that it is nothing.
Here's a simple example:
Suppose you have a table with people's names including separate columns for first_name, middle_name, and last_name. In the scenario where first_name = 'John', last_name = 'Doe', and middle_name IS NULL, it means that we do not know what the middle name is, or if it even exists. Change that scenario such that middle_name = '' (i.e. empty-string), and it now means that we know that there is no middle name.
I once heard a SQL Server instructor promote making every character type column in a database required, and then assigning a DEFAULT VALUE to each of either '' (empty-string), or 'unknown'. In stating this, the instructor demonstrated he did not have a clear understanding of the difference between NULLs and empty-strings. Admittedly, the differences can seem confusing, but for me the above example helps to clarify the difference. Also, it is important to understand the difference when writing SQL code, and properly handle for NULLs as well as empty-strings.
An empty string is a string with zero length or no character.
Null is absence of data.
NULL values are stored separately in a special bitmap space for all the columns.
If you do not distinguish between NULL and '' in your application, then I would recommend you to store '' in your tables (unless the string column is a foreign key, in which case it would probably be better to prohibit the column from storing empty strings and allow the NULLs, if that is compatible with the logic of your application).
NULL is a non value, like undefined. '' is a empty string with 0 characters.
The value of a string in database depends of your value in your UI, but generally, it's an empty string '' if you specify the parameter in your query or stored procedure.
if it's not a foreign key field, not using empty strings could save you some trouble. only allow nulls if you'll take null to mean something different than an empty string. for example if you have a password field, a null value could indicate that a new user has not created his password yet while an empty varchar could indicate a blank password. for a field like "address2" allowing nulls can only make life difficult. things to watch out for include null references and unexpected results of = and <> operators mentioned by Vagif Verdi, and watching out for these things is often unnecessary programmer overhead.
edit: if performance is an issue see this related question: Nullable vs. non-null varchar data types - which is faster for queries?
In terms of having something tell you, whether a value in a VARCHAR column has something or nothing, I've written a function which I use to decide for me.
CREATE FUNCTION [dbo].[ISNULLEMPTY](#X VARCHAR(MAX))
RETURNS BIT AS
BEGIN
DECLARE #result AS BIT
IF #X IS NOT NULL AND LEN(#X) > 0
SET #result = 0
ELSE
SET #result = 1
RETURN #result
END
Now there is no doubt.
How are the "NULL" and "empty varchar" values stored in SQL Server.
Why would you want to know that? Or in other words, if you knew the answer, how would you use that information?
And in case I have no user entry for a string field on my UI, should I store a NULL or a ''?
It depends on the nature of your field. Ask yourself whether the empty string is a valid value for your field.
If it is (for example, house name in an address) then that might be what you want to store (depending on whether or not you know that the address has no house name).
If it's not (for example, a person's name), then you should store a null, because people don't have blank names (in any culture, so far as I know).

How to detemine if the default value was supplied to stored procedure?

There's a stored procedure with the parameter that may take default value
CREATE PROCEDURE [dbo].[FooSP]
#FooParam INT = 15
AS
So is there a way to determine whether that SP was called with or without setting the #FooParam value.
IOW, is there a way to determine if the user called
exec dbo.FooSP(15)
or
exec dbo.FooSP()
Thanks!
If you need to do that, you are usually better off changing the default value to some kind of sentinal value (NULL often works well in db stored procedures,) and putting some logic in the procedure to set the value to you old default (15 in this case) if NULL is received.
That way, you get the benefit of a default value, and the ability to know what was passed in.
UPDATE
If there is no possible sentinal value -- say, because the parameter is very constrained, where all possible inputs are legitimate, then you will have to either do some stack introspection, or change the function signature.
If you can change the calling code, then make the procedure with the default parameter actually a different procedure, which then calls your original procedure with the default set explicitly.
You might want to vote for this related Connect item - getting call stack information can be really useful to be able to instrument code, and debug situations.

Resources