Sorting and ordering varchars in sql - sql

I have this SQL table that looks like this
As you can see the Original Principal Balance is not ordered correctly. These variables are stored as a VARCHAR. How would I sort and order them correctly?
Here is my sql code:
WITH Original_Principal_Bal
AS (
SELECT [New Loan Number]
,[Current Amortizing UPB]
,[Current Def UPB]
,sum([Current Amortizing UPB] + [Current Def UPB]) OVER (PARTITION BY Deal) AS [Total UPB]
,BPO
,[Current Rate]
,[Current Maturity]
,[Next Due Date]
,[First Payment Date]
,CASE
WHEN [Original Loan Amount] BETWEEN 0.01
AND 100000
THEN '$0.01 to $100,000'
WHEN [Original Loan Amount] BETWEEN 100000.01
AND 200000
THEN '$100,000.01 to $200,000'
WHEN [Original Loan Amount] BETWEEN 200000.01
AND 300000
THEN '$200,000.01 to $300,000'
WHEN [Original Loan Amount] BETWEEN 300000.01
AND 400000
THEN '$300,000.01 to $400,000'
WHEN [Original Loan Amount] BETWEEN 400000.01
AND 500000
THEN '$400,000.01 to $500,000'
WHEN [Original Loan Amount] BETWEEN 500000.01
AND 600000
THEN '$500,000.01 to $600,000'
WHEN [Original Loan Amount] BETWEEN 600000.01
AND 700000
THEN '$600,000.01 to $700,000'
WHEN [Original Loan Amount] BETWEEN 700000.01
AND 800000
THEN '$700,000.01 to $800,000'
WHEN [Original Loan Amount] BETWEEN 800000.01
AND 900000
THEN '$800,000.01 to $900,000'
WHEN [Original Loan Amount] BETWEEN 900000.01
AND 1000000
THEN '$900,000.01 to $1,000,000'
WHEN [Original Loan Amount] BETWEEN 1100000.01
AND 1200000
THEN '$1,100,000.01 to $1,200,000'
WHEN [Original Loan Amount] BETWEEN 1300000.01
AND 1400000
THEN '$1,300,000.01 to $1,400,000'
WHEN [Original Loan Amount] BETWEEN 1600000.01
AND 1700000
THEN '$1,600,000.01 to $1,700,000'
WHEN [Original Loan Amount] BETWEEN 1900000.01
AND 2000000
THEN '$1,900,000.01 to $2,000,000'
WHEN [Original Loan Amount] > 2000000.01
THEN '$2,000,000 or greater'
END AS [Original Principal Balance]
FROM Portfolio_Analytics..Securitization_Tape
)
SELECT [Original Principal Balance]
,COUNT([New Loan Number]) AS [Number of Mortgage Loans]
,ROUND(sum([Current Amortizing UPB] + [Current Def UPB]), 0) AS [Aggregate Unpaid Principal Balance as of Cut-off Date ($)]
,ROUND(avg([Current Amortizing UPB] + [Current Def UPB]), 0) AS [Average Unpaid Principal Balance ($)]
,ROUND(sum(([Current Amortizing UPB] + [Current Def UPB]) / [Total UPB]) * 100, 2) AS [Percetage of Aggregate Principal Balance as of Cut-off Date(%)]
,sum(BPO) AS [Aggregate Updated Value($)]
,ROUND(sum([Current Rate] * ([Current Amortizing UPB] + [Current Def UPB])) / sum([Current Amortizing UPB] + [Current Def UPB]), 2) AS [Weighted Average Mortgage Interest Rate(%)]
,ROUND(sum(([Current Amortizing UPB] + [Current Def UPB]) / bpo * ([Current Amortizing UPB] + [Current Def UPB])) / sum([Current Amortizing UPB] + [Current Def UPB]) * 100, 2) AS [Weighted Average Updated Loan-to-Value Ratio(%)]
,ROUND(sum((DATEDIFF(month, [Next Due Date], [Current Maturity]) + 1) * ([Current Amortizing UPB] + [Current Def UPB])) / (sum([Current Amortizing UPB] + [Current Def UPB])), 0) AS [Weighted Average Remaining Term to Maturity(Months)]
,ROUND(sum((DATEDIFF(month, [First Payment Date], [Next Due Date]) + 1) * ([Current Amortizing UPB] + [Current Def UPB])) / (sum([Current Amortizing UPB] + [Current Def UPB])), 0) AS [Weighted Average Remaining Term to Maturity(Months)]
FROM Original_Principal_Bal
GROUP BY [Original Principal Balance]
Perhaps I need to store the numbers differenly when I create the table, I am not sure how else to match what I want it to look like though.

You can do:
ORDER BY MIN([Original Loan Amount])
You will also need to include [Original Loan Amount] in the CTE.

As trincot commented, "rendering" doesn't belong in the database layer. I don't necesarily agree or disagree with this statement. While it may be a "best practice" there are always practical reasons to go against the norm.
Seeing as your bucket labels are manually entered strings, I think the best option would be to simply add a ranking column to your select statement. Something like this:
WITH Original_Principal_Bal
AS (
SELECT [New Loan Number]
,[Current Amortizing UPB]
,[Current Def UPB]
,sum([Current Amortizing UPB] + [Current Def UPB]) OVER (PARTITION BY Deal) AS [Total UPB]
,BPO
,[Current Rate]
,[Current Maturity]
,[Next Due Date]
,[First Payment Date]
,CASE
WHEN [Original Loan Amount] BETWEEN 0.01
AND 100000
THEN '$0.01 to $100,000'
WHEN [Original Loan Amount] BETWEEN 100000.01
AND 200000
THEN '$100,000.01 to $200,000'
WHEN [Original Loan Amount] BETWEEN 200000.01
AND 300000
THEN '$200,000.01 to $300,000'
WHEN [Original Loan Amount] BETWEEN 300000.01
AND 400000
THEN '$300,000.01 to $400,000'
WHEN [Original Loan Amount] BETWEEN 400000.01
AND 500000
THEN '$400,000.01 to $500,000'
WHEN [Original Loan Amount] BETWEEN 500000.01
AND 600000
THEN '$500,000.01 to $600,000'
WHEN [Original Loan Amount] BETWEEN 600000.01
AND 700000
THEN '$600,000.01 to $700,000'
WHEN [Original Loan Amount] BETWEEN 700000.01
AND 800000
THEN '$700,000.01 to $800,000'
WHEN [Original Loan Amount] BETWEEN 800000.01
AND 900000
THEN '$800,000.01 to $900,000'
WHEN [Original Loan Amount] BETWEEN 900000.01
AND 1000000
THEN '$900,000.01 to $1,000,000'
WHEN [Original Loan Amount] BETWEEN 1100000.01
AND 1200000
THEN '$1,100,000.01 to $1,200,000'
WHEN [Original Loan Amount] BETWEEN 1300000.01
AND 1400000
THEN '$1,300,000.01 to $1,400,000'
WHEN [Original Loan Amount] BETWEEN 1600000.01
AND 1700000
THEN '$1,600,000.01 to $1,700,000'
WHEN [Original Loan Amount] BETWEEN 1900000.01
AND 2000000
THEN '$1,900,000.01 to $2,000,000'
WHEN [Original Loan Amount] > 2000000.01
THEN '$2,000,000 or greater'
END AS [Original Principal Balance]
,CASE
WHEN [Original Loan Amount] BETWEEN 0.01
AND 100000
THEN 0
WHEN [Original Loan Amount] BETWEEN 100000.01
AND 200000
THEN 1
WHEN [Original Loan Amount] BETWEEN 200000.01
AND 300000
THEN 2
WHEN [Original Loan Amount] BETWEEN 300000.01
AND 400000
THEN 4
WHEN [Original Loan Amount] BETWEEN 400000.01
AND 500000
THEN 5
WHEN [Original Loan Amount] BETWEEN 500000.01
AND 600000
THEN 6
WHEN [Original Loan Amount] BETWEEN 600000.01
AND 700000
THEN 7
WHEN [Original Loan Amount] BETWEEN 700000.01
AND 800000
THEN 8
WHEN [Original Loan Amount] BETWEEN 800000.01
AND 900000
THEN 9
WHEN [Original Loan Amount] BETWEEN 900000.01
AND 1000000
THEN 10
WHEN [Original Loan Amount] BETWEEN 1100000.01
AND 1200000
THEN 11
WHEN [Original Loan Amount] BETWEEN 1300000.01
AND 1400000
THEN 12
WHEN [Original Loan Amount] BETWEEN 1600000.01
AND 1700000
THEN 13
WHEN [Original Loan Amount] BETWEEN 1900000.01
AND 2000000
THEN 14
WHEN [Original Loan Amount] > 2000000.01
THEN 15
END AS [Balance Rank]
FROM Portfolio_Analytics..Securitization_Tape
)
SELECT [Original Principal Balance]
,COUNT([New Loan Number]) AS [Number of Mortgage Loans]
,ROUND(sum([Current Amortizing UPB] + [Current Def UPB]), 0) AS [Aggregate Unpaid Principal Balance as of Cut-off Date ($)]
,ROUND(avg([Current Amortizing UPB] + [Current Def UPB]), 0) AS [Average Unpaid Principal Balance ($)]
,ROUND(sum(([Current Amortizing UPB] + [Current Def UPB]) / [Total UPB]) * 100, 2) AS [Percetage of Aggregate Principal Balance as of Cut-off Date(%)]
,sum(BPO) AS [Aggregate Updated Value($)]
,ROUND(sum([Current Rate] * ([Current Amortizing UPB] + [Current Def UPB])) / sum([Current Amortizing UPB] + [Current Def UPB]), 2) AS [Weighted Average Mortgage Interest Rate(%)]
,ROUND(sum(([Current Amortizing UPB] + [Current Def UPB]) / bpo * ([Current Amortizing UPB] + [Current Def UPB])) / sum([Current Amortizing UPB] + [Current Def UPB]) * 100, 2) AS [Weighted Average Updated Loan-to-Value Ratio(%)]
,ROUND(sum((DATEDIFF(month, [Next Due Date], [Current Maturity]) + 1) * ([Current Amortizing UPB] + [Current Def UPB])) / (sum([Current Amortizing UPB] + [Current Def UPB])), 0) AS [Weighted Average Remaining Term to Maturity(Months)]
,ROUND(sum((DATEDIFF(month, [First Payment Date], [Next Due Date]) + 1) * ([Current Amortizing UPB] + [Current Def UPB])) / (sum([Current Amortizing UPB] + [Current Def UPB])), 0) AS [Weighted Average Remaining Term to Maturity(Months)]
FROM Original_Principal_Bal
GROUP BY [Original Principal Balance]

try:
order by cast(substring(left( [Original Principal Balance],charindex(' ',[Original Principal Balance])-1),2,1000) as decimal(18,4))
This will convert the first number to a number and sort on that.

There's one very cool thing about the MONEY data type... It doesn't complain about commas or currency symbols and it's a numeric data type, so it sorts like and other number... Here are few examples...
DECLARE #string VARCHAR(50) = '$700,000.01 to $800,000';
SELECT CONVERT(MONEY, SUBSTRING(#string, 1, CHARINDEX(' ', #string, 2)));
SELECT CONVERT(MONEY, LEFT(#string, PATINDEX('%[^0-9.,$]%', #string)));
SELECT CONVERT(MONEY, LEFT(#string, CHARINDEX(' ', #string)));

Related

Does rounding work if you call it on an operation in parentheses in SQL Server?

Basically I can't get the rounding method to work. I want to round the price to 2 decimal places; I have tried to follow examples out there but it's just ignoring it. So I must be missing something.
This is what I have so far
SELECT TOP 100
CONVERT(varchar, pord.dt_created, 1) AS [Date Created],
pord_line.supplier AS [Supplier],
pord_line.ref_no AS [Purchase Order],
pord_line.qty_received AS [Quantity Received],
pord_line.qty- pord_line.qty_received AS [Quantity Oustanding],
pord.currency AS [Currency],
-- pord_line.price* pord_line.qty_received AS [Amount Received in Euros],
-- pord_line.price * (pord_line.qty-pord_line.qty_received) AS [Amount Outstanding In euros]
ROUND((pord_line.price * pord_line.qty_received), 2 )AS [Amount Received in Euros],
ROUND(((pord_line.price * (pord_line.qty-pord_line.qty_received))), 2) AS [Amount Outstanding In euros]
FROM
pord_line (nolock)
JOIN
pord ON pord_line.ref_no = pord.ref_no
WHERE
pord.dt_created BETWEEN '2015-10-08' AND '2015-11-08' ;
It is the following code which isn't performing as I intended
ROUND((pord_line.price * pord_line.qty_received), 2 )AS [Amount Received in Euros],
ROUND(((pord_line.price * (pord_line.qty-pord_line.qty_received))), 2) AS [Amount Outstanding In euros]
Generally speaking, the ROUND function does not change the datatype; if input the expression is DECIMAL(18, 10) the result is also DECIMAL(18, 10):
SELECT ROUND(1.0153456789, 2) -- 1.0200000000
If you want exactly 2 digits then cast the result to DECIMAL(n, 2):
SELECT CAST(1.0153456789 AS DECIMAL(18, 2)) -- 1.02
And your query would become:
CAST(pord_line.price * pord_line.qty_received AS DECIMAL(18, 2)) AS [Amount Received in Euros],
CAST(pord_line.price * (pord_line.qty - pord_line.qty_received) AS DECIMAL(18, 2)) AS [Amount Outstanding In euros],
(Please don't use images to display data on stack overflow for code or data)
As for your question: round does not format. what you can do is either use format or try cast :
Cast(ROUND( (pord_line.price * pord_line.qty_received), 2 ) as Decimal(10,2)) AS [Amount Received in Euros],
Rounding works but you receiving result as for example decimal(15,10) - 10 precision. try casting your result to decimal (18,2)
CAST( ROUND( (pord_line.price * pord_line.qty_received), 2 ) as decimal(18,2)) AS [Amount Received in Euros]
CAST( ROUND( ((pord_line.price * (pord_line.qty-pord_line.qty_received))), 2) as decimal(18,2) AS [Amount Outstanding In euros]

SQL Server / SSMS - Error Converting nvarchar to float

I have checked Temp_Calc_1 against Temp_Calc_2 and they both have the same column datatypes (float, nvarchar(255)) and are in the same order when reading down the table. When I try to run this on the empty Temp_Calc_2, I get an error:
Error converting nvarchar to float
on the line INSERT INTO dbo.Temp_Calc_2
USE MfgMetrics
INSERT INTO dbo.Temp_Calc_2
SELECT
zps.[Plant], [Work Center],
[Scheduled start Date], [SCHEDULE START TIME],
[SCHEDULED FINISHED DATE], [SCHEDULED FINISHED TIME],
[MATERIAL NUMBER], [MATERIAL DESCRIPTON],
[ORDER NUMBER], [ORDER TYPE],
[PLANNED QUANTITY], [PLANNED QTY - PROD UN],
[DELIVERED QTY], [DELIVERED QTY - PROD UN],
[RemainingQty(BUn)], [REMAINING QTY - Prod Un],
[COMMITED QTY], [COMMITED QTY - PROD UN], [UOM],
[STORAGE LOCATION], [COMMENTS],
[RATE QUANTITY], [RATE QUANTITY - PROD UN], [RATE HOUR],
[OPERATING EFFIECIENCY], [UNIT],
[STD UNITS / HR], [STD UNITS / HR - PROD UN], [UOM2],
[ORDER STATUS], [ACTUAL START DATE], [ACTUAL START TIME],
[MRP CONTROLLER], [CREATED ON], [TIME CREATED],
[CHANGED ON], [TIME CHANGE],
[ORDER TEXT LINES], [ORDER TEXT 2nd LINE], [FileDate],
scf.[OQ_RxnTime_(mins)], scf.[RunDurThreshold_(hrs)],
CASE
WHEN (zps.[MATERIAL NUMBER] LIKE '%.%')
THEN zps.[MATERIAL NUMBER]
WHEN ISNUMERIC(zps.[MATERIAL NUMBER]) = 1
THEN CAST(CAST(zps.[MATERIAL NUMBER] AS INT) AS NVARCHAR(255))
ELSE zps.[MATERIAL NUMBER]
END As [Material],
(CAST(CAST(zps.[ORDER NUMBER] AS INT) AS NVARCHAR(255)) + '_' + CAST(CAST(zps.[PLANNED QUANTITY] AS INT) AS NVARCHAR(255))) AS Order_Quantity,
scf.[OQ_RxnTime_(mins)] * zps.[STD UNITS / HR] / 60 AS OQWindow
FROM
Temp_Calc_1 zps
INNER JOIN
SAWorkCenters swc ON zps.Plant = swc.Plant
AND zps.[Work Center] = swc.WCGroup
INNER JOIN
SchedAttCalcFactors scf ON zps.Plant = scf.[Plant Code]
ORDER BY
zps.Plant, zps.[Work Center], zps.[ORDER NUMBER], zps.FileDate
GO

Create a value adding two columns together

I am attempting to define and new column called End Year that is the calculation of another column plus a number representing number of years. For some reason my script does not recognize the new column called Allocation Year using the excerpt below SQL statement in SQL Server 2008. Note that Contract Year is an existing column that is identified in the non-redacted full script:
,[Allocation Type] =
CASE
WHEN left([contract type] ,1)'1' = THEN 'O&M'
ELSE 'N/A'
END
,[Allocation Year] =
CASE
WHEN [Contract Year]='XXXX' THEN '0'
ELSE CAST ([Contract Year] AS INT)
END
,[End Year] =
CASE
WHEN [Allocation Type]='O&M' THEN [Allocation Year] + 6
END
Columns are named with the AS keyword (which can also be elided in many cases), not with the = operator.
SQL Server does not let you reference other columns or aliases in a SELECT clause - you have to use a subquery:
Like so:
SELECT
*,
CASE WHEN [Allocation Type] = 'O&M'
THEN [Allocation Year Temp] + 6
ELSE NULL
END AS [Allocation Year]
FROM
(
SELECT
CASE WHEN LEFT( [Contract type], 1 ) = '1'
THEN 'O&M'
ELSE 'N/A'
END AS [Allocation Type],
CASE WHEN [Contract Year] = 'XXXX'
THEN '0'
ELSE CAST( [Contract Year] AS int )
END AS [Allocation Year Temp],
*
FROM
....
)
If you want to put everything in one select query, then try this one. Hope this helps. Thanks.
,[End Year] =
CASE
WHEN left([contract type] ,1)='1' THEN
(CASE
WHEN [Contract Year]='XXXX' THEN 6
ELSE (CAST ([Contract Year] AS INT) + 6 )
END)
END
You cannot reference computed columns in the SELECT statement. You'll have to repeat the calculation (or use subqueries like Dai suggested):
,[Allocation Type] =
CASE
WHEN left([contract type] ,1)='1' THEN 'O&M'
ELSE 'N/A'
END
,[Allocation Year] =
CASE
WHEN [Contract Year]='XXXX' THEN 0
ELSE CAST ([Contract Year] AS INT)
END
,[End Year] =
CASE
WHEN left([contract type] ,1)='1' THEN (CASE
WHEN [Contract Year]='XXXX' THEN 0
ELSE CAST ([Contract Year] AS INT)
END) + 6
END

Find Gap in Dates SQL

I'm using Microsoft SQL Server and have 2 tables, AbsenceHistory and FITNoteHistory.
AbsenceHistory:
[Employee Number], [Absence Number], [Start Date], [End Date]
FITNoteHistory:
[Absence Number], [FIT Note Number], [Start Date], [End Date]
I need to identify where there is a gap in the FIT Note History, that doesn't cover the entire Absence Period between DateAdd(d,7,AbsenceHistory.[Start Date]) and AbsenceHistory.[End Date], where AbsenceHistory.[End Date] is not null and DATEDIFF(d,AbsenceHistory.[Start Date],AbsenceHistory.[End Date]) >= 7.
The output needs to give me the actual date gaps for each absence.
E.g. Absence Number, Date of Gap
Can anyone help?
Example Data:
AbsenceHistory:
[Employee Number], [Absence Number], [Start Date], [End Date]
18615, 70, '01-Jan-2018', '31-Jan-2018'
FITNoteHistory:
[Absence Number], [FIT Note Number], [Start Date], [End Date]
70, 1, '08-Jan-2018', '15-Jan-2018'
70, 15, '18-Jan-2018', '24-Jan-2018'
70, 31, '26-Jan-2018', '01-Feb-2018'
My expected output would be:
[Employee Number], [Absence Number], [Gap Date]
18615, 70, '16-Jan-2018'
18615, 70, '17-Jan-2018'
18615, 70, '25-Jan-2018'
This should get you started
declare #t table ([Absence Number] int, [FIT Note Number] int, [Start Date] date, [End Date] date)
insert into #t values
(70, 1, '08-Jan-2018', '15-Jan-2018')
, (70, 15, '18-Jan-2018', '24-Jan-2018')
, (70, 31, '26-Jan-2018', '01-Feb-2018');
select t.[Absence Number], t.[FIT Note Number], t.[Start Date], t.[End Date]
from #t t
order by t.[Start Date], t.[End Date], t.[FIT Note Number];
declare #minDate date = (select min([End Date]) from #t);
declare #maxDate date = (select max([Start Date]) from #t);
with cteDate as
(
select #minDate as dDate
union all
select cast(dateadd(dd, 1, dDate) as date)
from cteDate
where dDate < #maxDate
)
, cteNext as
( select t.[Absence Number], t.[FIT Note Number], t.[Start Date], t.[End Date]
, lead(t.[Start Date], 1) OVER (ORDER BY t.[Start Date]) AS nextStart
from #t t
)
select n.[Absence Number], n.[FIT Note Number], n.[Start Date], n.[End Date]
, d.dDate
from cteNext n
join cteDate d
on d.dDate > n.[End Date]
and d.dDate < n.nextStart
order by d.dDate;

sql query date formatting (Hours:Minutes)

How to display values as Hours:Minutes format (eg 77, Result: 1:17)
info :
select
SUM([Travel Time] + [Total Productive Time])
from
vAction_Reg
where
incident_id = '10064068'
Result: 77.00
Need to get result in below format
(1:17)
Changed Query:
SELECT( SELECT ISNULL(SUM(action.service_time), 0) AS Expr1
FROM dbo.act_reg AS action
INNER JOIN dbo.act_type ON action.act_type_id = dbo.act_type.act_type_id
WHERE (dbo.act_type.act_type_n IN ('Travel Time')) AND (action.incident_id = inc.incident_id)) AS [Travel Time],
( SELECT ISNULL(SUM(action.service_time), 0) AS Expr1
FROM dbo.act_reg AS action
INNER JOIN dbo.act_type AS act_type_6 ON action.act_type_id = act_type_6.act_type_id
WHERE (act_type_6.act_type_n NOT IN ('Travel Time', 'Site Departure')) AND (action.incident_id = inc.incident_id)) AS [Total Productive Time],
( SELECT cast(total / 60 as varchar(8)) + ':' + cast(total % 60 as varchar(2))
FROM
( SELECT cast(sum([Travel Time] + [Total Productive Time]) as int) as total) T) AS [Total Service Time]
FROM dbo.incident AS inc
INNER JOIN dbo.assyst_usr ON inc.ass_usr_id = dbo.assyst_usr.assyst_usr_id
INNER JOIN dbo.serv_dept ON dbo.assyst_usr.serv_dept_id = dbo.serv_dept.serv_dept_id
WHERE (inc.incident_ref ='64483')
You can divide for hours, mod for remaining minutes:
select
cast(fld / 60 as varchar(8)) + ':' + cast(fld % 60 as varchar(2))
Update for ?money? - assumes no fractional parts;
select
cast(total / 60 as varchar(8)) + ':' + cast(total % 60 as varchar(2))
from (
select
cast(sum([Travel Time] + [Total Productive Time]) as int) as total
from
vAction_Reg
where
incident_id = '10064068'
) T
Create a function as below:
CREATE FUNCTION [dbo].[NumberToTime]
(
#Num int
)
RETURNS nvarchar(20)
AS
BEGIN
DECLARE #TimeDuration AS nvarchar(20)
SET #TimeDuration = (SELECT CAST((#Num / 60) AS VARCHAR(8)) + ':' +
CAST((#Num % 60) AS VARCHAR(2)))
RETURN #TimeDuration
END
To use the function:
select
dbo.NumberToTime(SUM([Travel Time] + [Total Productive Time])) from
vAction_Reg where
incident_id = '10064068'
It will return: 1:17