Round to nearest 5 in SQL Server - sql-server-2008

I have a Money column in my SQL Server 2008 table. In my below query how can I round it to nearest 5$
select FineAmount from tickets
Thanks

select round(FineAmount*2,-1)/2 from tickets
or to put nicholaides suggestion in sql
select round(FineAmount/5,0)*5 from tickets
The example assumes that FineAmount is of type money.
The second approach is probably better as the first one works with the limit of maximum_value_of_money_type/2
More on ROUND

A general math solution:
Divide by 5, round to the nearest integer, then multiply by 5.

If you want to truncate (round-down) to a grouping of 5 use the modulo function; in Microsoft SQL Server this is %
ie: field1 - (field1 % 5)
If you had field1 == 3, then the calc would be:
3 - (3 % 5) = 0
if it were 13:
13 - (13 % 5) = 10
Simply add 5 if you want to round up
See also 'MOD' is not a recognized built-in function name

My first solution was
create function dbo.udf_RoundToNearest(#x int, #to int)
returns int
with schemabinding as begin
return #to * convert(int, round(convert(float, #x) / convert(float, #to), 0))
end
This works, but is considered by MSSQL as 'imprecise' since it uses floating point numbers internally. That stops it being used in indexed views. You can instead do the job only with integer arithmetic:
create function dbo.udf_RoundToNearest(#x int, #to int)
returns int
with schemabinding as begin
declare #m int
set #m = abs(#x) % abs(#to)
declare #trunc int
set #trunc = abs(#x) - #m
declare #r int
set #r = case when #m * 2 >= abs(#to) then #trunc + abs(#to) else #trunc end
return case when #x < 0 then -#r else #r end
end

Round to next greater 5
(CAST(#Amount/5 AS INT) + IIF(CAST(ROUND(#Amount,0) AS INT) % 5>1,1,0))*5)

DECLARE #Amount DECIMAL(18,3) ; SET #Amount = 7818.32
SELECT(Round((#Amount-CAST(#Amount AS INT))*100 /5,0)*5 /100)
+ CAST( #Amount AS INT)
-- you'll get 7818.30

Use ROUND function
SELECT ROUND(FineAmount,5)
FROM tickets

Related

storing the numbers after the decimal point into a variable in MySQL

lets say i want to store the number after the point which is (76) into a variable now. How am i supposed to do that?
I will give a scenario below.
declare x (3,2);
set x = 323.76;
declare y int;
select cast(substring_index(x, '.', -1) as unsigned) into y;
Any help would be appreciated.
As Procedure or a finction and trigger, you can use your code(with a little change)
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `mantisse`()
BEGIN
declare x DECIMAL(8,2);
declare y int;
set x = 323.76;
select cast(substring_index(x, '.', -1) as unsigned) into y;
INSERT INTO mytable VALUE (y);
END$$
DELIMITER ;
Or if you want to use it in a query you can use user defined variables
set #x = 323.96;
select cast(substring_index(#x, '.', -1) as unsigned) into #y;
INSERT INTO mytable VALUE (#y);
you have alreadya string so use SUBSTRING to get the 9
set #x = 323.96;
select cast(SUBSTRING(substring_index(#x, '.', -1),1,1) as unsigned) into #y;
SELECT #y;
INSERT INTO mytable VALUE (#y);
That works in the Proecdure too of course
You can easily get decimals from a number using MOD function:
SET #num = 323.76;
SET #decimals = MOD(#num, 1) * 100;
SELECT #decimals; -- 76.00
Dividing by 1, you can get the remainder with MOD function, which is 0.76, and then you only need to multiply it by 100.
If I'm understanding the specification, it seems rather bizarre. I'd use the substring_index function to trim off everything before and including the dot. But I would do the math to arrive at a value v, 0 <= v < 1
Following the MySQL stored program pseudo-code given in the question, something like this:
DECLARE x DECIMAL(5,2);
DECLARE y BIGINT;
SET x := 323.76;
SET y := SUBSTRING_INDEX( ABS(x)-FLOOR(ABS(x)) ,'.',-1) + 0;
There might be a simpler way to do it, but this is an approach that satisfies my understanding of the specification.
As a demonstration of the expression that derives the value of y, consider:
SELECT _x
, SUBSTRING_INDEX( ABS(_x)-FLOOR(ABS(_x)) ,'.',-1) + 0 AS _y
FROM ( SELECT 0 AS _x
UNION ALL SELECT 0.1
UNION ALL SELECT 2.0
UNION ALL SELECT 3.3
UNION ALL SELECT -4.00
UNION ALL SELECT -5.55
UNION ALL SELECT 623.76
UNION ALL SELECT -723.76
) t
returns
_x _y
------- -----
0.00 0
0.10 10
2.00 0
3.30 30
-4.00 0
-5.55 55
623.76 76
-723.76 76

How to make uppercase only the odd indexes of a string in MySQL?

I'm trying to make only the odd indexes of a string in uppercase (whereas the even indexes to be in lowercase) in MySQL.
For example: StackOverflow -> StAcKoVeRfLoW or hello -> HeLlO.
I found a way to this by extracting a letter at a time using the mid function, then concatenating based on which index the letter is at:
SET #x='hello';
SELECT #x as Initial,
Concat(ucase(mid(#x,1,1)),lcase(mid(#x,2,1)),ucase(mid(#x,3,1)),lcase(mid(#x,4,1)),ucase(mid(#x,5,1)))
as Final;
However I'm interested if there is a way to simplify this, since if the string would be larger then we would have some problems. So basically is there a way to modify it to something like:
Concat(ucase(mid(#x,odd index,1)),lcase(mid(#x,even index,1)))?
This is probably most simply done in your application, but can be achieved in MySQL. For MySQL 8+ you can use a recursive CTE to extract the individual letters from the string and GROUP_CONCAT to put them back together, changing the case on an alternating basis:
WITH RECURSIVE INITIAL AS (
SELECT 'StackOverflow' AS x
),
CTE AS (
SELECT 1 AS upper, SUBSTRING(x, 1, 1) AS letter, SUBSTRING(x, 2) AS remainder
FROM INITIAL
UNION ALL
SELECT 1 - upper, SUBSTRING(remainder, 1, 1), SUBSTRING(remainder, 2)
FROM CTE
WHERE LENGTH(remainder) > 0
)
SELECT GROUP_CONCAT(CASE WHEN upper THEN UPPER(letter) ELSE LOWER(letter) END SEPARATOR '') AS new
FROM CTE
Output:
StAcKoVeRfLoW
In versions lower than 8, you can use a user-defined function:
DELIMITER //
CREATE FUNCTION AlterCase(initial TEXT)
RETURNS TEXT
DETERMINISTIC
BEGIN
DECLARE i INT DEFAULT 1;
DECLARE l CHAR(1);
DECLARE new TEXT DEFAULT '';
WHILE i <= LENGTH(initial) DO
SET l = SUBSTRING(initial, i, 1);
SET new = CONCAT(new,
CASE WHEN i % 2 = 1 THEN UPPER(l) ELSE LOWER(l) END);
SET i = i + 1;
END WHILE;
RETURN new;
END //
DELIMITER ;
And call it as
SELECT AlterCase('StackOverflow')
Output:
StAcKoVeRfLoW
Note the function will work in MySQL 8+ too.
Demo on dbfiddle

stored procedure returns wrong value

I have a stored procedure that keeps giving me wrong answer. I asked the procedure to return the value of motor insurance. I run the procedure and give me the total of motor insurance premium but if I run it for the 4th time it give me the ageRange select statement value.
I moved the code into a new procedure but still the same.
My code
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `cal_motor_qoute`(in
coverID int , in dob date,
in sumMotor double , out QMsg varchar(200))
BEGIN
declare policy_cover , total , insRatio, ageExtra double;
declare ageRange int;
declare price_list varchar(200);
SELECT DATEDIFF(NOW(),dob) / 365.25 AS ageRange from dual;
if (coverID = 1) then
set policy_cover = 0.002;
elseif (coverID = 2) then
set policy_cover = 0.0025;
elseif (coverID = 3) then
set policy_cover = 0.003;
elseif (coverID = 4) then
set policy_cover = 0.0035;
end if;
if ( ageRange < 25) then
set ageExtra = 0.0005;
else
set ageExtra = 0.000;
end if;
set insRatio = policy_cover + ageExtra;
set total = (sumMotor * insRatio )* 10;
set QMsg = concat('total Premium is: ',total);
select #QMsg;
END
Any help please..
SELECT DATEDIFF(NOW(),dob) / 365.25 AS ageRange from dual;
will not set the variable ageRange, but it will do a select (of the calculated value) and name the column of the resultset ageRange.
The (or rather: one) way to set the value of your variable is to use into:
SELECT DATEDIFF(NOW(),dob) / 365.25 into ageRange from dual;
Although this is probably not the most precise way to calculate the age of a person anyway. You might want to replace
if ( ageRange < 25) then
with
if ( dob > date_sub(now(), interval 25 year) ) then

In mssql Summing values in a cell with '$$' separated Integers, but data type is varchar

I have below table with Table data
ID |Department | SaleMngrs(VARCHAR(200)) |
---------------------------------------------
1 |Technics | '1$$2$$3$$4'|
2 |Development| '10$$21$$43'|
I want the result as given below
ID |Department | SaleMngrs | sum
-------------------------------------
1 |Technics | '1$$2$$3$$4'| 10
2 |Development| '10$$21$$43'| 74
If you are using SQL server then use Solution number #1 or if you are using MySql then go for solution number #2
Solution #1
For SQL server it would be better to use Function to split the string and get the values from it, you need to Create the Below function
Function SplitStrings
CREATE FUNCTION SplitStrings
(
#Input NVARCHAR(MAX),
#Character CHAR(2)
)
RETURNS #Output TABLE (
Item NVARCHAR(1000)
)
AS
BEGIN
DECLARE #StartIndex INT, #EndIndex INT
SET #StartIndex = 1
IF SUBSTRING(#Input, LEN(#Input) - 1, LEN(#Input)) <> #Character
BEGIN
SET #Input = #Input + #Character
END
WHILE CHARINDEX(#Character, #Input) > 0
BEGIN
SET #EndIndex = CHARINDEX(#Character, #Input)
INSERT INTO #Output(Item)
SELECT SUBSTRING(#Input, #StartIndex, #EndIndex - 1)
SET #Input = SUBSTRING(#Input, #EndIndex + 1, LEN(#Input))
END
RETURN
END
GO
After Executing this function run this below query
SELECT ID, Department, SaleMngrs,
(SELECT SUM(CONVERT(INT,Item)) FROM dbo.SplitString(SaleMngrs, '$$')) AS Sum
From Temp_Table
solution #2
SELECT ID, Department, SaleMngrs, SUBSTRING_INDEX(SaleMngrs,'$$',1) +
SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT(SaleMngrs,'$$0'),'$$',2),'$$',-1) +
SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT(SaleMngrs,'$$0'),'$$',3),'$$',-1) +
SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT(SaleMngrs,'$$0'),'$$',4),'$$',-1) +
SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT(SaleMngrs,'$$0'),'$$',5),'$$',-1) AS SUM
From Temp_Table;
Here is the SQL fiddle URL, you can check here.
http://sqlfiddle.com/#!2/de5066/1
You can convert your data (VARCHAR to INTEGER) and after that get your sum.
By the way could you explain it more? Because Your question is a little puzzling.

sql server 2008 not generating random value

I'm having a problem with random values being generated for each row in a result set in SQL Server 2008. I found a similar question here, but upon implementing the proposed answer, I saw the same problem as before. When running the query I have provided below, it seems that the same values will sometimes show up in consecutive rows, even though I'm calling for a new NEWID() with each row.
DECLARE #Id int = 0
DECLARE #Counter int = 1
DECLARE #Value int
CREATE TABLE #Table1
(
id int identity(1,1)
,Value int
)
WHILE #Counter < 100000
BEGIN
INSERT INTO #Table1 (Value)
SELECT CAST(RAND(CHECKSUM(NEWID())) * 100000 as INT)
SET #Counter += 1
END
SET #Counter = 0
WHILE #Counter < 5
BEGIN
SELECT
#Value = T.Value
,#Id = T.id
FROM #Table1 T
WHERE T.id = CAST(RAND(CHECKSUM(NEWID())) * 100000 as INT) + 1 + #Counter
IF #Id <> 0
SELECT #Value AS Value ,#Id as ID
SET #Counter += 1
END
DROP TABLE #Table1
If I change the INT to a BIGINT, as suggested in the link I provided, nothing is solved, so I don't believe that it's an "overflow" issue.
If I take the calculation out of the select, I don't get the doubled rows:
DECLARE #Id int = 0
DECLARE #Counter int = 1
DECLARE #Value int
-- new variable
DECLARE #DERIVED INT
CREATE TABLE #Table1
(
id int identity(1,1)
,Value int
)
WHILE #Counter < 100000
BEGIN
INSERT INTO #Table1 (Value)
SELECT CAST(RAND(CHECKSUM(NEWID())) * 100000 as INT)
SET #Counter += 1
END
SET #Counter = 0
WHILE #Counter < 5
BEGIN
--set here to remove calculation from the select
SET #DERIVED = CAST(RAND(CHECKSUM(NEWID())) * 100000 as INT) + 1 + #Counter;
SELECT
#Value = T.Value
,#Id = T.id
FROM #Table1 T
WHERE T.id = #DERIVED
IF #Id <> 0
SELECT #Value AS Value ,#Id as ID;
SET #Counter += 1
END
DROP TABLE #Table1
I'm seeing the duplicates every time with the pseudorandom generator inside the select. Oddly enough, I get about the same frequency of duplicates on the insert loop whether or not the calculation is inside the insert... select. It could be coincidence, since we are dealing with a randomly selected number. Note also, that since you're adding to the pseudorandom result, the results aren't technically duplicates. They're descending sequences:
11111 + 1 + 1 = 11113
11110 + 1 + 2 = 11113
Same overall result, different pseudorandom result. However, if I change
CAST(RAND(CHECKSUM(NEWID())) * 100000 as INT) + 1 + #Counter
to
CAST(RAND(CHECKSUM(NEWID())) * 100000 as INT) + #Counter + #Counter
I still consistently get duplicates. That implies that the optimizer may be caching/re-using values, at least on the select. I'd call that improper for a non-deterministic function call. I get similar results on 10.0.1600 and 10.50.1600 (2008 RTM and 2008R2 RTM).