Cap field value in SELECT - sql

I am trying to cap particular fields so that they don't exceed a particular value.
For example, something like this would suffice, where I can specify that a field should not exceed 8:
SELECT (
cap(t.MondayHrs,8)
+ cap(t.TuesdayHrs,8)
+ cap(t.WednesdayHrs,8)
+ cap(t.ThursdayHrs,8)
+ cap(t.FridayHrs,8)
) as TotalHours
If MondayHrs = 7, then it should be added to TotalHours as 7.
If MondayHrs = 10, then it should be added to TotalHours as 8 (my capped value)
Is there anything built into T-SQL that could facilitate this?

create function Cap (#Value float,#maxValue float) Returns float
as
begin
Declare #Result float
if #Value > #maxValue select #Result=#Maxvalue else select #Result=#Value
Return #Result
end;
usage
Select dbo.Cap(1,10),dbo.Cap(11,10)

Try...
select least(t.MondayHrs,8)
+ least(t.TuesdayHrs,8)
+ least(t.WednesdayHrs,8)
+ least(t.ThursdayHrs,8)
+ least(t.FridayHrs,8)
) as TotalHours

An alternate idea would be to use CASE. For example:
SELECT (
(CASE WHEN t.MondayHrs > 8 THEN 8 ELSE t.MondayHrs END)
+ (CASE WHEN t.TuesdayHrs > 8 THEN 8 ELSE t.TuesdayHrs END)
) as TotalHours

You can write a function for that like MySQL GREATEST.
Then you could do something like
select greatest(some_col, 8) +
greatest(other_col, 8) +
...
from your_table

Related

SQL Numerator from 0-Z

Is there a simple an elegant way to create a numerator in SQL the do the following:
Three digit number
Each digit can can have a number from 0-Z : Min 000, Max ZZZ
for example let's take : 129, After increasing by 1, the new number will be 12A
When number is 15Z, after increasing by one it will turn into 160 and so on.
I can only use SQL, no code behind !!.
I only need an enumerator and not a conversion between bases like other have suggested.
Can someone help me write a UDF.
I got an excellent and elegant answer which I marked below.
Who ever marked my question as negative please reconsider because this is a real problem and not a theoratic one and it saved me a lot of workarounds.
Thanks in advance.
Produce cartesian and take the first that is greater then input value:
declare #v varchar(10) = '129'
;with cte as(select * from (values('0'),('1'),('2'),('3'),('4'),('5'),('6'),('7'),('8'),('9')
,('A'),('B'),('C'),('D'),('E'),('F'),('G'),('H'),('I'),('J'),('K')
,('L'),('M'),('N'),('O'),('P'),('Q'),('R'),('S'),('T'),('U'),('V')
,('W'),('X'),('Y'),('Z')) t(n))
select top 1 c1.n + c2.n + c3.n as n
from cte c1
cross join cte c2
cross join cte c3
where c1.n + c2.n + c3.n > #v
order by n
You should store the values numeric and then convert them when needed to your format.
Here is a script to add one to the strange format. The script is not checking for overflow nor invalid data in the original value:
DECLARE #original char(3) = '129'
DECLARE #val int =
CASE WHEN RIGHT(#original, 1) > '9' THEN ASCII(RIGHT(#original, 1)) - 55
ELSE RIGHT(#original, 1) END
+CASE WHEN SUBSTRING(#original, 2,1) > '9'
THEN ASCII(SUBSTRING(#original, 2,1)) - 55 ELSE SUBSTRING(#original, 2,1) END * 36
+CASE WHEN LEFT(#original, 1) > '9'
THEN ASCII(LEFT(#original, 1)) - 55 ELSE LEFT(#original, 1) END * 36 * 36
+1 -- increase 1
SELECT
CHAR(CASE WHEN #val / 36 / 36 > 9 THEN #val / 36 / 36 + 55
ELSE #val / 36 / 36+48 END)+
CHAR(CASE WHEN (#val / 36) % 36 > 9 THEN (#val / 36) % 36 + 55
ELSE (#val / 36) % 36+48 END)+
CHAR(CASE WHEN #val % 36 % 36 > 9 THEN #val % 36 % 36 + 55
ELSE #val % 36 % 36+48 END)
Result:
12A
I suggest you create this table in your DB with this query
;WITH Ch AS
( select i,C=CASE WHEN i<=10 THEN Char(47+i) ELSE CHAR(54+i) END FROM (SELECT TOP 36 row_number() over(order by object_id) i from sys.objects) t )
SELECT row_number() over(order by Ch.c,Ch2.c,Ch3.c) i,
Ch.c + Ch2.c + Ch3.c C INTO B36 FROM Ch CROSS JOIN Ch Ch2 CROSS JOIN Ch Ch3
So you can do indexes in the i & C columns
Then you do queries from this table for conversions with index seeking fastest speed and optimized as follows:
SELECT C FROM B36 WHERE i=3728
Reverse encoding with
SELECT i FROM B36 WHERE C='AG7'
Function that converts bigint to Base36
CREATE FUNCTION dbo.fnBase36
(
#Val BIGINT
)
RETURNS VARCHAR(13)
AS
BEGIN
DECLARE #Result VARCHAR(13) = ''
IF (#Val <= 0)
BEGIN
RETURN '0'
END
WHILE (#Val > 0)
BEGIN
SELECT #Result = CHAR(#Val % 36 + CASE WHEN #Val % 36 < 10 THEN 48 ELSE 55 END) + #Result,
#Val = FLOOR(#Val/36)
END
RETURN #Result
END
GO
select dbo.fnBase36(321)
--returns 8X
I can give you a converter like this:
char(48 + (i / (36 * 36)) + CASE WHEN (i / (36 * 36)) > 9 THEN 7 ELSE 0 END) +
char(48 + (i / 36) % 36 + CASE WHEN (i / 36) % 36 > 9 THEN 7 ELSE 0 END) +
char(48 + i % 36 + CASE WHEN i % 36 > 9 THEN 7 ELSE 0 END)
that can convert an int value to what you want.
[SQL Fiddle Demo]
Edit
I also can give you inversion of above converter like this:
(E.g.: #t varchar(max) = '12Z')
(ASCII(LEFT(#t, 1)) - 48 - CASE WHEN ASCII(LEFT(#t, 1)) > ASCII('9') THEN 7 ELSE 0 END) * 36 * 36 +
(ASCII(SUBSTRING(#t,2,1)) - 48 - CASE WHEN ASCII(SUBSTRING(#t,2,1)) > ASCII('9') THEN 7 ELSE 0 END) * 36 +
(ASCII(RIGHT(#t, 1)) - 48 - CASE WHEN ASCII(RIGHT(#t, 1)) > ASCII('9') THEN 7 ELSE 0 END)

TSQL round number and check in which group it is

I have a quite simple task:
I must check in wchich group my float is.
Here are my groups:
0-30 display "(0-30)"
30-40 display "(0-30)"
40-50 display "(0-30)"
50-60 display "(0-30)"
etc
I have created a simple script:
DECLARE #num FLOAT
SET #num = 42.5;
SELECT CASE WHEN #num<=30 THEN '(0-30)'
ELSE '('+convert(VARCHAR,convert(INT,round((#num/10),0))*10)+'-'+convert(VARCHAR,convert(INT,round(((#num+10)/10),0))*10)+')'
END
I think it is a little lame, so if anyone could help me out with creating a better solution :)
Thanks for any advice :)
Use:
DECLARE #num FLOAT
SET #num = 311.2;
SELECT
CASE
WHEN #num <= 30
THEN '(0-30)'
ELSE '(' + cast(cast(#num AS INT) / 10 * 10 AS VARCHAR) + '-' + cast(cast(#num AS INT) / 10 * 10 + 10 AS VARCHAR) + ')' END
Or you can use: (to get rid of the CASE statement and get a more readable look, IMO)
declare #num float = 156
select
'(' + convert(varchar, lowLimit) + ' - ' + convert(varchar, highLimit) + ')'
from
(
select
0 as lowLimit,
30 as highLimit
where
#num <= 30
union all
select
floor(#num/10)*10,
ceiling(#num/10)*10
where
#num > 30
) limits

Sql concatenate problem?

In my table i have a column called Dep_user_code which is nothing but employeeid...Everytime I need to increment the employeeid when i insert a new value..but it has both alphabet and number..i need to increment the number alone.. for example if my employeeid is 'NECUSER0001' means next time when i insert a new employeeid it has to be 'NECUSER0002' dynamically i have to generate like this..everytime when i insert a value it has to increment..
I have tried like taking the string part and number part like this but dont know how to implement this...Any suggestion?
select SUBSTRING(Dep_user_code,1,7) from NEC_Customer_User_Map
select SUBSTRING(Dep_user_code,8,4) from NEC_Customer_User_Map
you should also keep an identity key. use SELECT IDENT_CURRENT('NEC_Customer_User_Map') to find out last inserted ID.
If the value is always text then numbers, you split apart the value using Patindex:
Select Substring( Dep_user_code, 1, PatIndex( '%[0-9]%', Dep_user_code) - 1 ) As TextPortion
, Substring( Dep_user_code, PatIndex( '%[0-9]%', Dep_user_code)
, Len(Dep_user_code) ) As NumberPortion
However, whether you can use an identity in combination with a prefix depends on whether you can allow gaps. If you cannot allow gaps, then you need to query for the next id value that you can use which can be done in a variety of ways depending on the needs.
I've had to support databases with setups like this before and while I'm generally not a fan of this style, I'm assuming you have some reason for not storing the NECUSER in one column and the incrementing identity integer in another column with the PK set to both. If not, I'd suggest going that route and letting SQL do the work for you.
Otherwise, using the result of the following query should yield the results you want. I've added comments to try and answer any questions the query might raise.
SELECT SUBSTRING(Dep_user_code, 1, 7) +
RIGHT(
REPLICATE('0', 3) + --Ensure we have padding 0s
IsNull(MAX(CAST(SUBSTRING(Dep_user_code, 8, 4) AS INT), -1) + 1 --Work with ints, find MAX or set NULL to -1 so +1 will = 0
, 4) --Only want 4 character total from RIGHT function
FROM NEC_Customer_User_Map
WITH last AS (
SELECT MAX(Dep_user_code) AS Code
FROM NEC_Customer_User_Map
WHERE LEFT(Dep_user_code, 7) = 'NECUSER'
)
SELECT LEFT(Dep_user_code, 7) + RIGHT(CAST(STUFF(Code, 1, 7, '1') AS int) + 1, 4)
FROM last
The RIGHT part does the following:
replaces 'NECUSER' with '1' thus getting something like '10002';
casts the result as int;
increments by 1;
(implicitly) casts the value to varchar and gets the last 4 chars.
Maybe STUFF(Code, 1, 7, '1') should better be replaced with '1' + RIGHT(Code, 4), not sure.
EDIT: As it happens, the implicit conversion could also be employed in case of converting the string to the integer too:
... + RIGHT(STUFF(Code, 1, 7, '1') + 1, 4) ...
or
... + RIGHT('1' + RIGHT(Code, 4) + 1, 4) ...
declare #max varchar(20)
declare #number varchar(20)
select #max = max(cast(substring(dep_user_name , 8, 4) as int)) from NEC_Customer_User_Map (nolock)
select #max = isnull(#max, 0) + 1
select #max = (case when len(#max) = 1 then '000' + #max
when len(#max) = 2 then '00' + #max
when len(#max) = 3 then '0' + #max
else #max
end)
Select #number = (Substring( dep_user_name, 1, PatIndex( '%[0-9]%', dep_user_name) - 1 ) + #max) from NEC_Customer_User_Map
insert into NEC_Customer_User_Map(Dep_User_Name) values (#number )
You can consider to have both parts of Dep_user_code as separate fileds in your db in order to take advantage of several tsql features like IDENTITY and IDENT_CURRENT()

How to display the number “12” in the format of “0000012”

How to display the number “12” in the format of “0000012” Using SQL
if you just want the number 12 then
SELECT '0000012'
else if it is a number you need to display with 7 digits:
SELECT RIGHT('0000000'+CONVERT(nvarchar,FieldValue),7)
More info on the question would help.
How about something like
DECLARE #Val INT
DECLARE #Length INT
SELECT #Val = 12,
#Length = 7
SELECT REPLICATE('0',#Length - LEN(CAST(#Val AS VARCHAR(MAX)))) + CAST(#Val AS VARCHAR(MAX))
REPLICATE (Transact-SQL)
Repeats a string value a specified
number of times.
The shortest answer that probably also works best is just
SELECT RIGHT(10000000+ #Val, #Length)
e.g.
SELECT RIGHT(10000000+ NumColumn, 7)
I haven't got a server to test with, but you should be able to use the following in your SQL:
'RN ' + RIGHT(CAST(auto_id AS VarChar) + '000000', 6)
eg:
Code:
SELECT 'RN ' + RIGHT(CAST(auto_id AS VarChar) + '000000', 6)
FROM tablename
This is not efficient but will work for your case -
DECLARE #num int
DECLARE #totalChar int
SET #num = 12
SET #totalChar = 10
SELECT right('0000000000' + CONVERT(varchar, #num), #totalChar)
Output -
000000012
This should easily port to other SQLs:
SELECT
REVERSE(CAST(REVERSE(CAST(CAST(12 AS INTEGER) AS VARCHAR(7))) + '000000' AS CHAR(7)))
SELECT REPLACE(STR(12, 7), ' ', '0')

SQL Server - sum comma separated value from a column

There is a column in database which contains comma separated values like: 0.00,12.45,14.33 and so on.
I need to sum this inside a stored procedure. One way which I can think of is to split and convert it into a table using a function and then sum it.
Any other ideas?
Using Sql Server 2005+ CTE you can create a recursive select, something like
DECLARE #Table TABLE(
ID INT,
Vals VARCHAR(100)
)
INSERT INTO #Table SELECT 1, '0.00,12.45,14.33'
INSERT INTO #Table SELECT 2, '1,2,3,4'
;WITH ValList AS(
SELECT ID,
CAST(LEFT(Vals,PATINDEX('%,%', Vals) - 1) AS FLOAT) Val,
RIGHT(Vals,LEN(Vals) - PATINDEX('%,%', Vals)) Remainder
FROM #Table
UNION ALL
SELECT ID,
CAST(LEFT(Remainder,CASE WHEN PATINDEX('%,%', Remainder) = 0 THEN LEN(Remainder) ELSE PATINDEX('%,%', Remainder) - 1 END) AS FLOAT) Val,
RIGHT(Remainder,CASE WHEN PATINDEX('%,%', Remainder) = 0 THEN 0 ELSE LEN(Remainder) - PATINDEX('%,%', Remainder) END) Remainder
FROM ValList
WHERE LEN(Remainder) > 0
)
SELECT ID,
SUM(Val)
FROM ValList
GROUP BY ID
OUTPUT
ID Total
----------- ----------------------
1 26.78
2 10
within a function you could try something like this, totally unsure if it will work tho!
CREATE FUNCTION ufn_sum_csv(#string varchar(100))
RETURNS #result int
AS BEGIN
EXEC 'SELECT #result = ' + REPLACE(#string,',','+')
RETURN
Can't try it out on this comp.