Using CAST and bigint - sql-server

I am trying to understand what does this statement does
SUM(CAST(FILEPROPERTY(name, 'SpaceUsed') AS bigint) * 8192.)/1024 /1024
Also why is there a dot after 8192? Can anybody explain this query bit by bit. Thanks!

FILEPROPERTY() returns an int value. Note that the SpaceUsed property is not in bytes but in "pages" - and in SQL Server a page is 8KiB, so multiplying by 8192 to get the size in KiB is appropriate.
I've never encountered a trailing dot without fractional digits before - the documentation for constants/literals in T-SQL does not give an example of this usage, but reading it implies it's a decimal:
decimal constants are represented by a string of numbers that are not enclosed in quotation marks and contain a decimal point.
Thus multiplying the bigint value by a decimal would yield a decimal value, which may be desirable if you want to preserve fractional digits when dividing by 1024 (and then 1024 again), though it's odd that those numbers are actually int literals, so the operation would just be truncation-division.
I haven't tested it, but you could try just this:
SELECT
SUM( FILEPROPERTY( name, 'SpaceUsed' ) ) * ( 8192.0 / 10485760 ) AS TotalGigabytes
FROM
...
If you're reading through code and you need to do research to understand what it's doing - do a favour for the next person who reads the code by adding an explanatory comment to save them from having to do research, e.g. "gets the total number of 8KiB pages used by all databases, then converts it to gigabytes".

The dot . after an Integer converts it implicitly to decimal value. This is most likely here to force the output to also be decimal (not an integer). In this case you only need one part of the operation to be converted to force the output to be in that type.
This probably has to do with bytes/pages since the numbers 8192 and 1024 (most likely for converting to larger unit). One could also imply this by the value of property which indicates how much space is being used by a file.
A page fits within 8kB which means that multiplying pages value by 8192 does convert the output to bytes being used. Then division two times by 1024 succesfully converts the output to gigabytes.
Explanation on functions used:
FILEPROPERTY returns a value for a file name which is stored within database. If a file is not present, null value is returned
CAST is for casting the value to type bigint
SUM is an aggregate function used in a query to sum values for a specified group

Related

Generate integer number 1,2,3 in SQL Server

I used this query:
SELECT FLOOR(3 * RAND(CONVERT(varbinary, NEWID())))
Can someone please explain how it works? I know all the functions used, but I'm unable to link them.
Tne newid() function doesn't actually generate a string, it generates a uniqueidentifier, also known as a "globally unique identifier", or "GUID". This is a pseudo-random value.
The rand(#seed) function generates a value >= 0 and < 1.
Rand doesn't generate a seed, it accepts the seed as an input parameter. The result depends on the seed. If you pass the same seed value as the input parameter, the result of rand() is always the same. If rand() is called without any seed value, SQL server itself will produce a psuedo-random seed.
Now, this probably seems confusing already. Obvious questions are:
If newid() is already pseudo-random, why do we need rand()?
In the code presented in your question, rand() isn't actually being used to generate random values, that job is really being done by newid(). What rand() is doing is mapping the newid() value to a floating point value between zero and one.
OK then, if rand() with no input seed is already psuedo-random, why do we need newid()??
In the specific sample code in your question, where we are only working with a single scalar value, there's actually no need. The same thing could be accomplished with:
select floor(3 * RAND() + 1)
However, when you are working with multiple rows of data, rand() doesn't get re-seeded by SQL Server for every row, it only gets seeded once. So if you do something like this:
select rand() from sys.objects
Then every row in the result set will have the same value.
The newid() function is different. SQL will generate a different uniqueidentifier for every single row (it sort of "has to by law" - part of the definition of a GUID is that the same GUID should never be generated twice).
So the newid() function is providing a psuedo-random seed value to rand(), and then rand() is mapping that to some floating point value between 0 and 1 (excluding 1).
What is the convert to varbinary doing?
If an argument is passed to rand(), the argument has to be an integer. A uniqueidentifier cannot be implicitly converted to an integer. But a uniqueidentifier can be converted to a varbinary, and then the varbinary can be implicitly converted to an integer. If we make that conversion explicit, it looks like this:
select convert(int, convert(varbinary, newid()))
In your sample code, the integer conversion is being done implicitly. A uniqueidentifier is 16 bytes long, so it gets converted to a 16 byte varbinary. 12 of those bytes then get silently truncated (thrown away), because an integer is only 4 bytes long. The remaining 4 bytes are implicitly converted to the integer.
Note that this truncation could theoretically weaken the randomness of the result. People often use checksum() to convert to an integer rather than casting through a varbinary, because checksum will make use of all of the bytes in the GUID.
what is the multiplication by 3 doing?
Since the rand() function returns a value between 0 and 1, but you want a value "between" 1 and 3, we have to multiply the result of rand():
Values >= 0 and < 1/3 will map to values >= 0 and < 1.
Values >= 1/3 and < 2/3 will map to values >= 1 and < 2.
Values >= 2/3 and < 1 will map to values >= 2 and < 3.
What is floor() doing?
The value we have right now is some floating point value anywhere between 0 and 3 (excluding 3). But you want only the integer values 1 or 2 or 3. So we have to add 1 and shave off the decimal. This is what the + 1 and floor() are doing. You could also get rid of the + 1 and replace floor() with ceiling().
I used this query:
select floor(3 * RAND(convert(varbinary, newid())))+1
RAND() : returns some seed decimal number like 0.405615055347678
newid(): returns some string: 29CADAD4-F9F5-4B79-98F0-33DE745954FC
varbinary : converting data from a string data type to a binary or varbinary data type of unequal length :
eg:
select convert(varbinary, newid())
output:
0x321A7CBE6FBACC41B1EE5BC3C5219B2C
We can generate a random number, using the NEWID() function of SQL
Server. Random number generated by NEWID() method will be a 32 byte
Hexadecimal number, which is unique for your whole system.
The unique identifier generated by the NEWID() can be converted to
VARBINARY using CONVERT() which in turn can be converted to an integer
number USING FLOOR().
select rand()-result will be a random decimal
The FLOOR() function returns the largest integer value that is smaller
than or equal to a number.
select floor(rand()*N) —The generated number is like this: 12.0
The number range of method: 0 to n-1, such as cast (floor (rand() *100)
will generate any integer between 0 and 99.So here our N=3 ... we
will be generating an integer between 0 and 3.
SO, the link is NEWID()>CONVERT()>RAND()>FLOOR>SELECT

Weired Behavior of Round function in MSSQL Database for Real column only

I found weird or strange behavior of Round function in MSSQL for real column type. I have tested this issue in Azure SQL DB and SQL Server 2012
Why #number=201604.125 Return 201604.1 ?
Why round(1.12345,10) Return 1.1234500408 ?
-- For Float column it working as expected
-- Declare #number as float,#number1 as float;
Declare #number as real,#number1 as real;
set #number=201604.125;
set #number1=1.12345;
select #number as Realcolumn_Original
,round(#number,2) as Realcolumn_ROUND_2
,round(#number,3) as Realcolumn_ROUND_3
, #number1 as Realcolumn1_Original
,round(#number1,6) as Realcolumn1_ROUND_6
,round(#number1,7) as Realcolumn1_ROUND_7
,round(#number1,8) as Realcolumn1_ROUND_8
,round(#number1,9) as Realcolumn1_ROUND_9
,round(#number1,10) as Realcolumn1_ROUND_10
Output for real column type
I suspect what you are asking here is why does:
DECLARE #n real = 201604.125;
SELECT #n;
Return 201604.1?
First point of call for things like this should be the documentation: Let's start with float and real (Transact-SQL). Firstly we note that:
The ISO synonym for real is float(24).
If we then look further down:
float [ (n) ] Where n is the number of bits that are used to store the
mantissa of the float number in scientific notation and, therefore,
dictates the precision and storage size. If n is specified, it must be
a value between 1 and 53. The default value of n is 53. n value
Precision Storage size
1-24 7 digits 4 bytes
So, now we know that a real (aka a float(24)) has precision of 7. 201604.125 has a precision of 9, that's 2 too many; so off come that 2 and 5 in the return value.
Now, ROUND (Transact-SQL). That states:
Returns a numeric value, rounded to the specified length or precision.
When using real/float those digits aren't actually lost, as such, due to the floating point. When you use ROUND, you are specifically stating "I want this many decimal places". This is why you can then see the .13 and the .125, as you have specifically asked for those. When you just returned the value of #number it had a precision of 7, due to being a real, so 201604.1 was the value returned.

Get the complete number when divide two fields

I was trying to round some fields. When I have 59 days, I want to change it to 60.
The problem is that when I use this code, the 59 is changed to 30 because the round it is 1.
select round(1.9666,0)*30, round(59/30,0)*3'
The result of that query is 60 for the first field and 30 for the second one. The problem is that when I've tried:
select 59/30
The result is 1 and I need the entire answer that is 1.9666...
How can I make it?
Because the number you are dividing by is an INT (the data type of the left side is irrelevant), SQL Server will return an INT as the answer.
If you want a number with a decimal place as your result, you'll need to divide by one.
Don't cast to a FLOAT as the answer is probably not what you want (floats are generally not accurate and are 'approximations'):
SELECT 59 / CAST(30 AS FLOAT) -- = 1.96666666666667
CAST the right-hand side of the division to a DECIMAL:
SELECT 59 / CAST(30 AS DECIMAL(10, 2)) -- = 1.96666
SELECT cast(59 AS FLOAT) / cast(30 AS FLOAT)
Because the original figures are whole numbers, SQL presumes you want a whole number output.
To ensure you get one with the decimal places, you need to first change the data type from an integer int to a floating point float.
This is what the CAST command does.
EDIT: Commenter suggests you cast to DECIMAL instead. The principle is the same, but you need to supply more arguments. To cast to a decimal use something like:
cast(59 as DECIMAL(18, 3))
The first argument (the 18) is the total number of figures you want to permit in the decimal. The second argument (the 3) is the number you want after the decimal point.
The suggestion that it's more accurate is correct - as you'll see if you run the SELECT statements in this answer one after the other. But in this particular case, it only makes a tiny difference.

Why SQL binary convert function results a non-0101... value?

Why when I use the command in SQL Server 2005
select convert(varbinary(16),N'123')
results 0x310032003300 and not 1111011?
Basically each letter of '123' gets converted to it's UCS-2(basically the ASCII value padded to make it a double byte) value in the three double bytes of 0x3100, 0x3200, 0x3300, and concatenated together in a varbinar.
Hopefully that answers why you see this behavior. If you convert to an int first you may see what you were perhaps hoping for instead:
select convert(varbinary(16),cast(N'123' as int))
produces hex value 0x0000007B which in binary is 1111011
See http://www.asciitable.com/ the entry for numeric 3, the hex representation is 0x33 which corresponds to the same entry in unicode: http://www.ssec.wisc.edu/~tomw/java/unicode.html (this pattern does not necessarily hold true for all ASCII/unicode characters, but does for the 10 integers).

Appropriate datatype for holding percent values?

What is the best datatype for holding percent values ranging from 0.00% to 100.00%?
Assuming two decimal places on your percentages, the data type you use depends on how you plan to store your percentages:
If you are going to store their fractional equivalent (e.g. 100.00% stored as 1.0000), I would store the data in a decimal(5,4) data type with a CHECK constraint that ensures that the values never exceed 1.0000 (assuming that is the cap) and never go below 0 (assuming that is the floor).
If you are going to store their face value (e.g. 100.00% is stored as 100.00), then you should use decimal(5,2) with an appropriate CHECK constraint.
Combined with a good column name, it makes it clear to other developers what the data is and how the data is stored in the column.
Hold as a decimal.
Add check constraints if you want to limit the range (e.g. between 0 to 100%; in some cases there may be valid reasons to go beyond 100% or potentially even into the negatives).
Treat value 1 as 100%, 0.5 as 50%, etc. This will allow any math operations to function as expected (i.e. as opposed to using value 100 as 100%).
Amend precision and scale as required (these are the two values in brackets columnName decimal(precision, scale). Precision says the total number of digits that can be held in the number, scale says how many of those are after the decimal place, so decimal(3,2) is a number which can be represented as #.##; decimal(5,3) would be ##.###.
decimal and numeric are essentially the same thing. However decimal is ANSI compliant, so always use that unless told otherwise (e.g. by your company's coding standards).
Example Scenarios
For your case (0.00% to 100.00%) you'd want decimal(5,4).
For the most common case (0% to 100%) you'd want decimal(3,2).
In both of the above, the check constraints would be the same
Example:
if object_id('Demo') is null
create table Demo
(
Id bigint not null identity(1,1) constraint pk_Demo primary key
, Name nvarchar(256) not null constraint uk_Demo unique
, SomePercentValue decimal(3,2) constraint chk_Demo_SomePercentValue check (SomePercentValue between 0 and 1)
, SomePrecisionPercentValue decimal(5,2) constraint chk_Demo_SomePrecisionPercentValue check (SomePrecisionPercentValue between 0 and 1)
)
Further Reading:
Decimal Scale & Precision: http://msdn.microsoft.com/en-us/library/aa258832%28SQL.80%29.aspx
0 to 1 vs 0 to 100: C#: Storing percentages, 50 or 0.50?
Decimal vs Numeric: Is there any difference between DECIMAL and NUMERIC in SQL Server?
I agree with Thomas and I would choose the DECIMAL(5,4) solution at least for WPF applications.
Have a look to the MSDN Numeric Format String to know why :
http://msdn.microsoft.com/en-us/library/dwhawy9k#PFormatString
The percent ("P") format specifier multiplies a number by 100 and converts it to a string that represents a percentage.
Then you would be able to use this in your XAML code:
DataFormatString="{}{0:P}"
If 2 decimal places is your level of precision, then a "smallint" would handle this in the smallest space (2-bytes). You store the percent multiplied by 100.
EDIT: The decimal type is probably a better match. Then you don't need to manually scale. It takes 5 bytes per value.
Use numeric(n,n) where n has enough resolution to round to 1.00. For instance:
declare #discount numeric(9,9)
, #quantity int
select #discount = 0.999999999
, #quantity = 10000
select convert(money, #discount * #quantity)

Resources