Round to the closest .25 - tsql

Does anyone know of a way to round to the closest .25 in t-sql? Currently I am rounding down using
floor(value * 4)/4
My client is changing their algorithm and wants to do a midpoint round to the closest quarter. If the value is less than .125 round to 0.00, if the value is greater than or equal to .125 round up to .25.

use ROUND(value/25, 2) * 25 like this:
Example1:
DECLARE #value DECIMAL(18, 2)
SET #value = 1.126
SELECT CAST(ROUND(#value/25, 2) * 25 as numeric(18,2)) AS rounded_val
Output:
1.25
Example2:
DECLARE #value DECIMAL(18, 2)
SET #value = 1.124
SELECT CAST(ROUND(#value/25, 2) * 25 as numeric(18,2)) AS rounded_val
Output:
1.00

select Sample,
Round( ( Sample + Sign( Sample ) * 0.125 ) * 4, 0, 1 ) / 4.0 as Rounded
from ( values ( 0.0 ), ( 0.1 ), ( 1.125 ), ( 0.25 ), ( 10.5 ),
( -0.75 ), ( -0.875 ), ( -1.12 ), ( -1.125 ) )
as Samples( Sample )
Note that ROUND can be used to truncate the fractional part of a value regardless of the sign. FLOOR will always return a value equal to or less than the original value, which can be problematic when the value is negative.

I needed to round to an arbitrary precision & rounding strategy.
rounding to *.25
DECLARE #roundamt DECIMAL(16,2) = .25, #value DECIMAL(18, 2) = 1.7688
SELECT ROUND( (#value - FLOOR(#value)) / #roundamt, 0 ) * #roundamt + FLOOR(#value)
-- result = 1.750000
different rounding strategies like 0.02, 0.25, 0.5, 0.75
SELECT
x.roundamt, ROUND( (#value - FLOOR(#value)) / roundamt, 0 ) * roundamt + FLOOR(#value)
FROM
( VALUES (0.02), (0.25), (0.5), (0.75) )x(roundamt)

For anyone who need to find the closest divider without a remainder you can use:
SELECT (CAST(ROUND(#value / 0.25, 2) as int)) * 0.25
So basically this will round down to the closest multiple of 0.25

Related

How to calculate the percentage(%) in postgresql

I am not able to calculate the % in postgresql
Below the my code
v_total_repro_count = (select count(pm_task_run_bug_repro_id) from project.pm_task_run_bug_repro
where pm_task_run_bug_detail_id = 605)--5;
v_repro_count = (select count(pm_task_run_bug_repro_id) from project.pm_task_run_bug_repro
where execution_status = 'Reprod' and pm_task_run_bug_detail_id = 605)--3;
v_impact = select (5/3)*100; = answer = 100
v_impact = select (3/5)*100; = answer = 0
the answer getting 0 and 100 instead of 60%
Cast value to numeric
SELECT ROUND(( 3::NUMERIC/5::NUMERIC ) * 100);
If you want floating point results, you should use at least one float in your calculation, such that the entire expression is promoted to floating point:
select (3.0 / 5) * 100; -- returns 60

How to add number of days defined by float variable to a Date in SQL

I have a float variable which describes a term in months.
I need to be able to add this period to a given date.
I created a simple logic such as:
DECLARE #monthsTerm AS float = 2;
Select DATEADD(dd,((CAST(#monthsTerm AS INT) % 1) / 0.25 * 7), (DATEADD(mm, CAST(#monthsTerm AS INT), '2018-01-01'))) AS [Subscription end date]
The problem appears when I want to set the monthsTerm variable to a 1, 2 or 3 weeks, etc. period of time. So for two weeks period, the variable would look like:
DECLARE #monthsTerm AS float = 0.5;
and I expect to get '2018-01-15' as a result, but now it returns '2018-01-01' which is wrong.
I thought about transferring the expression to just a:
Select DATEADD(dd,((#monthsTerm % 1) / 0.25 * 7), (DATEADD(mm, CAST(#monthsTerm AS INT), '2018-01-01'))) AS [Subscription end date]
however, I'm getting an exception:
The data types float and int is incompatible in the modulo operator.
I know that for instance the similar expression would work in C#:
public DateTime EndDate
=> StartDate.AddMonths((int)TermMonths).AddDays((TermMonths % 1) / 0.25 * 7);
Is there any simple workaround to this so I can have only one simple expression with Select DATEADD(.. or I need to end up with some more complex solution? Cheers
Select
cast((CAST(#monthsTerm*100 as int) % 100)*7 /0.25/100 as int) as days ,
(((CAST(#monthsTerm*100 as int) % 100)/100) / 0.25 * 7),
DATEADD(dd,cast((CAST(#monthsTerm*100 as int) % 100)*7 /0.25/100 as int), (DATEADD(mm, CAST(#monthsTerm AS INT), '2018-01-01'))) AS [Subscription end date]
Multiplying with 100, and dividing by 100 after the %

PostGIS: intersections of set of collinear line segments, with counts

I have a set of collinear line segments (may be mutually disjoint, contained, or overlapping).
I want to make a new set of line segments where the segments are disjoint or touching (not overlapping), and each line segment has a count of the original line segments that cover it.
For example, suppose the original set is (drawn non-collinearly for illustration):
A----------------------B
C---------------------------D
E-----F
G-------------H
I-------J
the desired new set would be:
A-------C---E-----F-----B-----------D G-------------H-------J
1 2 3 2 1 1 1
(only the point coordinates matter, the new set does not share point objects with the old set)
How can I achieve this with PostGIS?
Related question: suppose I start with a table of line segments, not all collinear, how do I write the entire query that groups the collinear segments together and then applies the solution to my first question?
Thanks for any help!
Setup (for later queries):
create table lines (
id serial primary key,
label text not null,
line_data geometry(linestring) not null
);
insert into lines(label, line_data)
values ('A-B', ST_MakeLine(ST_MakePoint(-3, -6), ST_MakePoint( 1, 2))),
('D-C', ST_MakeLine(ST_MakePoint( 2, 4), ST_MakePoint(-2, -4))),
('E-F', ST_MakeLine(ST_MakePoint(-1, -2), ST_MakePoint( 0, 0))),
('G-H', ST_MakeLine(ST_MakePoint( 3, 6), ST_MakePoint( 4, 8))),
('I-J', ST_MakeLine(ST_MakePoint( 4, 8), ST_MakePoint( 5, 10))),
('P-L', ST_MakeLine(ST_MakePoint( 1, 0), ST_MakePoint( 2, 2))),
('X-Y', ST_MakeLine(ST_MakePoint( 2, 2), ST_MakePoint( 0, 4)));
Notes:
I purposely switched your D and C points to demonstrate a need for vector negation
The P-L line is parallel with your example lines (but not collinear)
The X-Y line has nothing to do with the others
the solutions below obviously won't work, when you have linestrings that have more than 2 points and those are not on the same line (so when a single linestring is not straight).
The ST_Union aggregate function can split your collinear linestrings. You'll just need to calculate how many lines are containing those.
However, grouping by collinearity is not that simple. I did not find any out-of-the-box solution for this, but you can calculate it (this will not calculate counts yet):
select string_agg(label, ','), ST_AsText(ST_Multi(ST_Union(line_data)))
from lines
group by (
select case
when ST_SRID(s) <> ST_SRID(e) then row(ST_SRID(s), s, null)
when ST_X(s) = ST_X(e) then row(ST_SRID(s), ST_SetSRID(ST_MakePoint(ST_X(s), 1.0), ST_SRID(s)), null)
when ST_Y(s) = ST_Y(e) then row(ST_SRID(s), ST_SetSRID(ST_MakePoint(1.0, ST_Y(e)), ST_SRID(s)), null)
else (
select row(
ST_SRID(s),
(select case
when ST_Y(rv) < 0
then ST_SetSRID(ST_MakePoint(-ST_X(rv), -ST_Y(rv)), ST_SRID(s))
else rv
end), -- normalized vector (negated when necessary, but same for all parallel lines)
(ST_X(e) * ST_Y(s) - ST_X(s) * ST_Y(e)) / (ST_X(e) - ST_X(s)) -- solution of the linear equaltion, where x=0
)
from coalesce(1.0 / nullif(ST_Distance(s, e), 0), 0) dmi, -- distance's multiplicative inverse
ST_TransScale(e, -ST_X(s), -ST_Y(s), dmi, dmi) rv -- raw vector (translated and scaled)
)
end
from ST_StartPoint(line_data) s,
ST_EndPoint(line_data) e
)
will produce:
X-Y | MULTILINESTRING((2 2,0 4))
P-L | MULTILINESTRING((1 0,2 2))
E-F,A-B,I-J,G-H,D-C | MULTILINESTRING((-3 -6,-2 -4),(-2 -4,-1 -2),(-1 -2,0 0),(0 0,1 2),(2 4,1 2),(3 6,4 8),(4 8,5 10))
To calculate counts, JOIN your original data again, where the splitted lines are contained by (ST_Contains) your original lines:
select ST_AsText(splitted_line), count(line_data)
from (select ST_Multi(ST_Union(line_data)) ml
from lines
group by (
select case
when ST_SRID(s) <> ST_SRID(e) then row(ST_SRID(s), s, null)
when ST_X(s) = ST_X(e) then row(ST_SRID(s), ST_SetSRID(ST_MakePoint(ST_X(s), 1.0), ST_SRID(s)), null)
when ST_Y(s) = ST_Y(e) then row(ST_SRID(s), ST_SetSRID(ST_MakePoint(1.0, ST_Y(e)), ST_SRID(s)), null)
else (
select row(
ST_SRID(s),
(select case
when ST_Y(rv) < 0
then ST_SetSRID(ST_MakePoint(-ST_X(rv), -ST_Y(rv)), ST_SRID(s))
else rv
end), -- normalized vector (negated when necessary, but same for all parallel lines)
(ST_X(e) * ST_Y(s) - ST_X(s) * ST_Y(e)) / (ST_X(e) - ST_X(s)) -- solution of the linear equaltion, where x=0
)
from coalesce(1.0 / nullif(ST_Distance(s, e), 0), 0) dmi, -- distance's multiplicative inverse
ST_TransScale(e, -ST_X(s), -ST_Y(s), dmi, dmi) rv -- raw vector (translated and scaled)
)
end
from ST_StartPoint(line_data) s,
ST_EndPoint(line_data) e)) al,
generate_series(1, ST_NumGeometries(ml)) i,
ST_GeometryN(ml, i) splitted_line
left join lines on ST_Contains(line_data, splitted_line)
group by splitted_line
will return:
LINESTRING(-3 -6,-2 -4) | 1
LINESTRING(-2 -4,-1 -2) | 2
LINESTRING(-1 -2,0 0) | 3
LINESTRING(0 0,1 2) | 2
LINESTRING(2 2,0 4) | 1
LINESTRING(1 0,2 2) | 1
LINESTRING(2 4,1 2) | 1
LINESTRING(3 6,4 8) | 1
LINESTRING(4 8,5 10) | 1

t-sql nearest quarter hour

I'm looking for a way to return a value adjusted to the 'highest' quarter. I cannot round because the value returned cannot be greater than the original value. For example if the value is 53.290 I need to return 53.25 and 51.49 would return 51.25. Can I do this in t-sql?
The answer you've provided, Steve, of dividing by 0.25 has the same effect as this code. I'm not sure, but I feel that multiplication by four is clearer than divison by a fraction.
SELECT (FLOOR(#myValue * 4)) / 4.0
After working on this for a bit and doing some more research I think this is the best solution. Thanks everyone for the responses.
DECLARE #myvalue DECIMAL(8,4);
SET #myvalue = 53.26;
SELECT (Floor(#myvalue/.25)) * .25; -- Returns 53.25
SET #myvalue = 53.24;
SELECT (Floor(#myvalue/.25)) * .25; -- Returns 53.00
DECLARE #Value DECIMAL(10,4) = 53.290;
SELECT ROUND(#Value/25.00, 2) * 25
Result: 53.250000000
Rounded value to TWO Digits
SELECT CONVERT(DECIMAL(18, 2), ROUND(#Value/25.00, 2) * 25)
Result: 53.25
This will get the nearest quarter based on the values present on the right side of the decimal.I have just tried my hands on it. Hope it would be useful. Just try it with different values as well.
DECLARE #Value DECIMAL(10, 2) = 53.49
SELECT CASE
WHEN CAST(RIGHT(#Value, 2) AS FLOAT) > 0
AND CAST(RIGHT(#Value, 2) AS FLOAT) <= 49
THEN FLOOR(#Value) + 0.25
WHEN CAST(RIGHT(#Value, 2) AS FLOAT) > 49
AND CAST(RIGHT(#Value, 2) AS FLOAT) <= 74
THEN FLOOR(#Value) + 0.50
ELSE CEILING(#Value) - 0.25
END

FileMaker Divide by Zero Error

I have a Calculation field in my Results table that will calculate the coursework_percent based on a student's submission of classwork, homework, quiz, project and participation. The code for the Calculation field is this:
Sum ( Coursework_Results_Classwork::mark ) / If ( Sum ( Coursework_CR_Classwork::max_mark ) ≠ 0 ; Sum ( Coursework_CR_Classwork::max_mark ) - Sum ( Coursework_Results_Classwork::reduce_max ) ; 1 ) * Coursework_Weighting::classwork_w
+
Sum ( Coursework_Results_Homework::mark ) / If ( Sum ( Coursework_CR_Homework::max_mark ) ≠ 0 ; Sum ( Coursework_CR_Homework::max_mark ) - Sum ( Coursework_Results_Homework::reduce_max ) ; 1 ) * Coursework_Weighting::homework_w
+
Sum ( Coursework_Results_Quiz::mark ) / If ( Sum ( Coursework_CR_Quiz::max_mark ) ≠ 0 ; Sum ( Coursework_CR_Quiz::max_mark ) - Sum ( Coursework_Results_Quiz::reduce_max ) ; 1 ) * Coursework_Weighting::quiz_w
+
Sum ( Coursework_Results_Project::mark ) / If ( Sum ( Coursework_CR_Project::max_mark ) ≠ 0 ; Sum ( Coursework_CR_Project::max_mark ) - Sum ( Coursework_Results_Project::reduce_max ) ; 1 ) * Coursework_Weighting::project_w
+
Sum ( Coursework_Results_Participation::mark ) / If ( Sum ( Coursework_CR_Participation::max_mark ) ≠ 0 ; Sum ( Coursework_CR_Participation::max_mark ) - Sum ( Coursework_Results_Participation::reduce_max ) ) * Coursework_Weighting::participation_w
The idea behind the code is this:
If a student did not submit a coursework due to valid reason, he should not be penalised. Hence, his maximum mark should be adjusted accordingly. I used reduce_max to reduce his otherwise maximum possible score.
My bug is this:
If a particular category (e.g. homework) has only 1 assignment and the student did not submit with valid reason(hence an empty field), the calculated field will have a division by zero error. I cannot use a zero because zero is used for non-submission without valid reason. I suspect that the bug lies in my If condition testing for ≠ 0. An empty field is not considered a zero.
Can someone help me out? Thanks.
I amended pft's answer to solve my problem. The bug was not really due to empty field. Instead, it occurs when I use reduce_max such that the maximum of the coursework becomes zero. My solution is this:
Let ([
sumOfClasswork = Sum ( Coursework_Results_Classwork::mark );
sumOfHomework = Sum ( Coursework_Results_Homework::mark );
sumOfQuiz = Sum ( Coursework_Results_Quiz::mark );
sumOfProject = Sum ( Coursework_Results_Project::mark );
sumOfParticipation = Sum ( Coursework_Results_Participation::mark );
classworkMax = Sum ( Coursework_CR_Classwork::max_mark );
homeworkMax = Sum ( Coursework_CR_Homework::max_mark );
quizMax = Sum ( Coursework_CR_Quiz::max_mark );
projectMax = Sum ( Coursework_CR_Project::max_mark );
participationMax = Sum ( Coursework_CR_Participation::max_mark );
classworkReductions = Sum ( Coursework_Results_Classwork::reduce_max );
homeworkReductions = Sum ( Coursework_Results_Homework::reduce_max );
quizReductions = Sum ( Coursework_Results_Quiz::reduce_max );
projectReductions = Sum ( Coursework_Results_Project::reduce_max );
participationReductions = Sum ( Coursework_Results_Participation::reduce_max );
// if coursework maximum after reduction is zero, 1 is returned to avoid a division by zero error
classworkRedMax = If ( classworkMax - classworkReductions <> 0 ; classworkMax - classworkReductions ; 1 );
homeworkRedMax = If ( homeworkMax - homeworkReductions <> 0 ; homeworkMax - homeworkReductions ; 1 );
quizRedMax = If ( quizMax - quizReductions <> 0 ; quizMax - quizReductions ; 1 );
projectRedMax = If ( projectMax - projectReductions <> 0 ; projectMax - projectReductions ; 1 );
participationRedMax = If ( participationMax - participationReductions <> 0 ; participationMax - participationReductions ; 1 );
classworkWeight = Coursework_Weighting::classwork_w;
homeworkWeight = Coursework_Weighting::homework_w;
quizWeight = Coursework_Weighting::quiz_w;
projectWeight = Coursework_Weighting::project_w;
participationWeight = Coursework_Weighting::participation_w
];
// finally the computation of coursework ;)
sumOfClasswork / classworkRedMax * classworkWeight
+
sumOfHomework / homeworkRedMax * homeworkWeight
+
sumOfQuiz / quizRedMax * quizWeight
+
sumOfProject / projectRedMax * projectWeight
+
sumOfParticipation / participationRedMax * participationWeight
)
I believe the IsEmpty function should help you out here. If I understand your intention correctly, you could use this code segment:
Let ([
sumOfMarks = Sum ( Coursework_Results_Classwork::mark );
sumOfMaxs = Sum ( Coursework_CR_Classwork::max_mark );
sumOfReductions = Sum ( Coursework_Results_Classwork::reduce_max );
firstMax = Coursework_CR_Classwork::max_mark;
theWeight = Coursework_Weighting::classwork_w
];
Case (
// The case where the student has at least one valid,
// positively-scored assignment
sumOfMaxs > 0 ; sumOfMarks / (sumOfMaxs - sumOfReductions) ;
// The case where there is only one related record for max_mark
// and it is empty
IsEmpty ( firstMax ) ; 1
) * theWeight
)