parsing an integer field in sql - sql

I want to split every digit in an integer field in an sql table . example of that would be:
financialNb = 7869
i need the 7 as first digit, 8 as second, 6 as third and 9 as fourth. this is needed because I want every digit to be used as 1 data field in a crystal report?

First you have to convert or cast numbers into text before you can manipulate them in this way. The functions you are looking for are CAST() and SUBSTRING(). To get the numbers to start from the right, you can use REVERSE().
Try this example:
SELECT 7869 AS field1
INTO #tmp
SELECT SUBSTRING(REVERSE(CAST(field1 AS VARCHAR(255))),8,1) AS [Column8]
,SUBSTRING(REVERSE(CAST(field1 AS VARCHAR(255))),7,1) AS [Column7]
,SUBSTRING(REVERSE(CAST(field1 AS VARCHAR(255))),6,1) AS [Column6]
,SUBSTRING(REVERSE(CAST(field1 AS VARCHAR(255))),5,1) AS [Column5]
,SUBSTRING(REVERSE(CAST(field1 AS VARCHAR(255))),4,1) AS [Column4]
,SUBSTRING(REVERSE(CAST(field1 AS VARCHAR(255))),3,1) AS [Column3]
,SUBSTRING(REVERSE(CAST(field1 AS VARCHAR(255))),2,1) AS [Column2]
,SUBSTRING(REVERSE(CAST(field1 AS VARCHAR(255))),1,1) AS [Column1]
FROM #tmp
DROP TABLE #tmp

Presumably your question is about parsing 7869. Using the substring function for this:
select substring(cast(<col> as char(4)), 1, 1) as FirstChar,
substring(cast(<col> as char(4)), 2, 1) as SecondChar,
substring(cast(<col> as char(4)), 3, 1) as ThirdChar,
substring(cast(<col> as char(4)), 4, 1) as FourthChar
from YourTable
I might be interpreting the ordering of the digits incorrectly, and this assumes the strings are always 4 digits and that you want characters. An alternative way is just to look at this as numbers:
select <col%10 as FirstNum,
(col/10) %10 as SecondNum,
(col/100)%10 as ThirdNum,
(col/1000)%10 as FourthNum

You can use the modulo operator (%) to get the digits of an integer, without using the slower string manipulation functions, like this
select
value % 100000000 / 10000000,
value % 10000000 / 1000000,
value % 1000000 / 100000,
value % 100000 / 10000,
value % 10000 / 1000,
value % 1000 / 100,
value % 100 / 10,
value % 10
from testData
Here is a SQL Fiddle to play with.
If the field is always 4 numbers long, you can get away with:
select
value / 1000 as Thousands,
value % 1000 / 100 as Hundreds,
value % 100 / 10 as Tens,
value % 10 as Units
from testData
If, however, you need to use it on arbitrary numbers, you could create a user-defined table valued function, that will return the digits in a table, like this:
create function dbo.getDigits(#Input int)
returns #Digits table
(
Digit int,
Position int
)
as begin
declare #pos int
declare #digit int
set #pos = 0
if #input = 0
begin
-- zero is just a single zero digit at position 1
insert into #digits values (0,1)
return
end
while #input<>0 begin
set #pos=#pos+1
set #digit = #input % 10
set #input = #input / 10
insert into #digits values (#digit, #pos)
end
return
end
and use it like this:
SELECT td.ID, td.Value, d.Digit, d.Position
FROM testData td
CROSS APPLY dbo.getDigits(td.Value) AS d
order by td.ID, d.Position Desc
(Here's another SQL Fiddle, based on the previous one)

Related

Select numbers with more than 4 decimal places

I have a SQL table with one float column populated with values like these:
1.4313
3.35
2.55467
6.22456
3.325
I need to select rows containing only values with more than 4 decimals. In this case, the select must return:
2.55467
6.22456
Ideas? Thanks!
This is what I have tried so far
select *
from table
where CAST(LATITUDE AS DECIMAL(10,5)) - LATITUDE = 0
DECLARE #tbl TABLE (val float)
INSERT INTO #tbl SELECT 1234.567
INSERT INTO #tbl SELECT 1234.5678
INSERT INTO #tbl SELECT -1234.5678
INSERT INTO #tbl SELECT 1234.56789
SELECT *
from #tbl
where (((val*10000) - CONVERT(INT,(val*10000))) <> 0)
Why cant we make it simple by this query:-
SELECT * FROM table WHERE val LIKE '%.____%'
This selects what we want
Another solution also:
SELECT * from table
where (round(value,2) - round(value,4) <> 0)
Given answers did not work for me with MaxDb, but this did:
where FLOOR(value * 10000) != value * 10000
Source
Reduce/Increase 0`s for less/more precision.
This works on Postgres 11:
SELECT * FROM mytable WHERE mycolumn != ROUND(mycolumn::numeric,2)
http://sqlfiddle.com/#!3/abadc/3/0
Seems like something like this should work...
all it does is convert the number to an integer to drop off decimals after multiplying it * 10 to power of decimals you need then it compares that int version of the number to the base number after it too was multiplied by 10 to the power of # of decimals.
If the numbers don't match, then you have decimals beyond 4. If they do match, then it was 4 or fewer.
Select *
from foo
where cast(myNum*power(10,4) as int) <> myNum*power(10,4)
Please try something like:
select * from table
where RIGHT(CAST(value as DECIMAL(10,5)), value), 1) != 0
SELECT *
FROM table WHERE
(abs(val)*100000)%10 <> 0
It's an older question but it checks out.
select val
from table
where ((val * 100) % 1) > 0
Change 100 to your precision.
You can multiply it with 10000 and subtract it from the original number replacing . with ''.
Fiddle
select * from tablename
where replace(numcolumn,'.','') - numcolumn * 10000 > 0
Below is the Code that will check the precision for 4 decimal places:
Replace MyNum, with column you are checking for precision
Replace MyTbl, with the table you are using
Replace 4, with whatever precision you are checking for
Sql:
SELECT MyNum
, LEN(CAST (MyNum AS CHAR))
, -------1. length of decimal number, after conversion to CHAR
CHARINDEX('.', CAST (MyNum AS CHAR))
, ---2.length of numbers after the '.'
LEN(CAST (MyNum AS CHAR)) - CHARINDEX('.', CAST (MyNum AS CHAR)) -----subtracting 1-2, to get the length of numbers after decimal point '.'
FROM MyTbl
WHERE LEN(CAST(MyNum AS CHAR)) - CHARINDEX('.', CAST(MyNum AS CHAR)) > 4; --checking if there are more than 4 numbers after the decimal point '.'
Cast the number as text
Split the text using '.' as separator
Use the 2nd index and apply a length
Filter
--i.e. with postgreSQL.
--1)
select data_numeric, length(arr[2]) as str_length
from (
select data_numeric, regexp_split_to_array(data_numeric::text, '\.') as arr from TABLE
) temp;
--2)
with t1 as (
select data_numeric, regexp_split_to_array(data_numeric::text, '\.') as arr from TABLE
), t2 as (
select data_numeric, arr[2] as decimals, length(arr[2]) as length from t1
)
select * from t2;
Select numbers with more than 2 decimal places:
I had an example, where ((abs(val)*100) and CONVERT(INT,(abs(val)*100)) for value "2.32" in float type column returned two different values.
(abs(2.32)*100) = 232
CONVERT(INT,(abs(2.32)*100)) = 231
That caused wrong select query answers in case for comparing to 0.
I suppose that MSSQL CONVERT() function round numbers in such way that for some float number cases, posted solution would not work.
Here is how I did it for more than 2 decimal places:
DECLARE #tbl TABLE (val float)
INSERT INTO #tbl SELECT 2.32
INSERT INTO #tbl SELECT 1234.54
INSERT INTO #tbl SELECT 1234.545
INSERT INTO #tbl SELECT 1234.5456
INSERT INTO #tbl SELECT 1234.54567
select * from #tbl where abs(val-round((val),2)) > 0.001
You can use the scale function, since postgresql 9.6.
select LATITUDE FROM TABLE where scale(LATITUDE) > 4;
If your data type is float you will get SQL Error [42883]: ERROR: function scale(double precision) does not exist. Below would fix it.
select LATITUDE FROM TABLE where scale(cast(LATITUDE as numeric)) > 4;
This is the simplest solution, Use WITH (NOLOCK) if it's necessary in your case otherwise you can remove it. This will return the records having at least 4 decimal points in ColumnName table.
SELECT * FROM TableName WITH (NOLOCK) WHERE ColumnName LIKE '%.____'

SELECT with CASE fail when aggregate function is used

I have the following t-sql query.
DECLARE #Test TABLE(
Points INT
,PointsOf INT
)
INSERT INTO #Test
VALUES (3,12),(2,12),(3,12),(11,12),(12,12),(5,12),(0,12)
DECLARE #Decimal TINYINT = 2
SELECT
CASE #Decimal
WHEN 0 THEN CAST(CAST(SUM(Points) AS DECIMAL(18,0)) / NULLIF(SUM(PointsOf), 0) * 100 AS DECIMAL(18,0))
WHEN 1 THEN CAST(CAST(SUM(Points) AS DECIMAL(18,1)) / NULLIF(SUM(PointsOf), 0) * 100 AS DECIMAL(18,1))
WHEN 2 THEN CAST(CAST(SUM(Points) AS DECIMAL(18,2)) / NULLIF(SUM(PointsOf), 0) * 100 AS DECIMAL(18,2))
END AS Score
FROM #Test
I have an variable #Decimals. When the variable is 0 I need my query to return score in XX format, when its 1 in XX.X format and when its 2 in the XX.XX format.
What happen here is that the CASE enter multiple THEN clauses. When the query above is executed i get 44.86 as result which is correct, but when i change the #Decimals valiable to 0 i get a result 44.00 which is incorect. Its suppose to return just 44 without decimals. The same thing happen when i have #Decimals at 1, it returns 44.90 when it has to be 44.9.
Does any one know why this happen?
As jarlh quite rightly points out the case statement is converting this to the smallest possible data type to hold all the result types. (See the MSDN documentation, specifically the Return Types section)
From my point of view, this is merely a display issue. You want to display 44.9 and not 44.90 (note the trailing zero) when the #decimal variable is set to one.
One way to do this is to add an additional cast to varchar. It's not pretty and not something I would recommend doing but if you insist on doing formatting and UI type things in SQL Server then what can you do?
DECLARE #Test TABLE(
Points INT
,PointsOf INT
)
INSERT INTO #Test
VALUES (3,12),(2,12),(3,12),(11,12),(12,12),(5,12),(0,12)
DECLARE #Decimal TINYINT = 1
SELECT
CASE #Decimal
WHEN 0 THEN cast(Cast(CAST(SUM(Points) AS DECIMAL(18,0)) / NULLIF(SUM(PointsOf), 0) * 100 as decimal(18,0)) as varchar(10))
WHEN 1 THEN cast(Cast(CAST(SUM(Points) AS DECIMAL(18,1)) / NULLIF(SUM(PointsOf), 0) * 100 as decimal(18,1))as varchar(10))
WHEN 2 THEN cast(Cast(CAST(SUM(Points) AS DECIMAL(18,2)) / NULLIF(SUM(PointsOf), 0) * 100 as decimal(18,2))as varchar(10))
END AS Score
FROM #Test
I don't know how MS Server works, but according to the ANSI SQL spec, a column always has one specific data type, which in this case is decimal(20,2), i.e. the smallest data type that can store all of the case's different result types.

SQL code to generate next sequence in a alphanumeric string

I have some string values already populated in a nvarchar column. the format of the strings are like this:
For example: 16B, 23G, 128F, 128M etc...
I need to find out the maximum value from these, then generate the next one from code. The logic for picking up the maximum item is like the following:
Pick up the string with the largest number.
If multiple largest number, then pick up the string the largest alphabet among those.
For example, the largest string from the above series is 128M.
Now I need to generate the next sequence. the next string will have
The same number as the largest one, but alphabet incremented by 1. I.E. 128N
If the alphabet reaches to Z, then the number gets incremented by 1, and alphabet is A.
for example, the next String of 128Z is 129A.
Can anyone let me know what kind of SQL can get me the desired string.
If you can change the table definition(*), keeping the basic values entirely numeric and just formatting into these strings would be easier:
create table T (
CoreValue int not null,
DisplayValue as CONVERT(varchar(10),(CoreValue / 26)+1) + CHAR(ASCII('A') + (CoreValue-1) % 26)
)
go
insert into T (CoreValue)
select ROW_NUMBER() OVER (ORDER BY so1.object_id)
from sys.objects so1,sys.objects so2
go
select * from T
Results:
CoreValue DisplayValue
----------- ------------
1 1A
2 1B
3 1C
4 1D
5 1E
6 1F
....
22 1V
23 1W
24 1X
25 1Y
26 2Z
27 2A
28 2B
29 2C
....
9593 369Y
9594 370Z
9595 370A
9596 370B
9597 370C
9598 370D
9599 370E
9600 370F
9601 370G
9602 370H
9603 370I
9604 370J
So inserting a new value is as simple as taking the MAX from the column and adding 1 (assuming serializable isolation or similar, to deal with multiple users)
(*) Even if you can't change your table definition - I'd still generate this table. You can then join it to the original table and can use it to perform a simple MAX against an int column, then add one and look up the next alphanumeric value to be used. Just populate it with as many values as you ever expect to use.
Assuming:
CREATE TABLE MyTable
([Value] varchar(4))
;
INSERT INTO MyTable
([Value])
VALUES
('16B'),
('23G'),
('128F'),
('128M')
;
You can do:
select top 1
case when SequenceChar = 'Z' then
cast((SequenceNum + 1) as varchar) + 'A'
else
cast(SequenceNum as varchar) + char(ascii(SequenceChar) + 1)
end as NextSequence
from (
select Value,
cast(substring(Value, 1, CharIndex - 1) as int) as SequenceNum,
substring(Value, CharIndex, len(Value)) as SequenceChar
from (
select Value, patindex('%[A-Z]%', Value) as CharIndex
from MyTable
) a
) b
order by SequenceNum desc, SequenceChar desc
SQL Fiddle Example
Assuming your column always follows the format you described (number+1 char suffix), you can do
WITH cte1 AS(
SELECT LEFT(your_column,LEN(your_column)-1) as num,
RIGHT(your_column,1) as suffix
FROM your_table),
cte2 AS (SELECT MAX(num) as max_num FROM cte1)
SELECT
CASE c.max_suffix
WHEN 'Z' THEN 'A'
ELSE NCHAR(UNICODE(c.max_suffix)+1)
END as next_suffix,
CASE c.max_suffix
WHEN 'Z' THEN a.max_num+1
ELSE a.max_num
END as next_num
FROM cte2 a
CROSS APPLY (SELECT MAX(suffix) as max_suffix FROM cte1 b WHERE b.num=a.max_num)c
;
I'm pretty sure there are other ways to do the same; also, my approach doesn't seem optimal, but I think it returns what you need...
No doubt it would be much better if you can redesign the table as Damien_The_Unbeliever recommends.
To generate alphanumeric String sequence in below format.
A B C.....Y Z AA AB......AZ BA BB.....BZ...go on.
CREATE OR REPLACE FUNCTION to_az(in_num number)
RETURN VARCHAR2
IS
num PLS_INTEGER := TRUNC (in_num) - 1;
return_txt VARCHAR2 (1) := CHR (65 + MOD (num, 26));
BEGIN
IF num <= 25
THEN
RETURN return_txt;
ELSE
RETURN to_az (FLOOR (num / 26))
|| return_txt;
END IF;
END to_az;
Ms-sql function to generate an alpha-numeric next sequence id like 'P0001','P0002' and so on.
ALTER FUNCTION NextProductID()
returns varchar(20)
BEGIN
DECLARE #NEXTNUMBER INT;
DECLARE #NEXTPRODUCTID VARCHAR(20);
SELECT #NEXTNUMBER=MAX( CONVERT(INT, SUBSTRING(PRODUCT_CODE,2,LEN(PRODUCT_CODE))))+1 FROM Product;
--PRINT #NEXTNUMBER;
SET #NEXTPRODUCTID=CONVERT(VARCHAR,#NEXTNUMBER)
SELECT #NEXTPRODUCTID='P'+REPLICATE('0',6-LEN(#NEXTPRODUCTID)) + #NEXTPRODUCTID;
return #NEXTPRODUCTID;
END
Here product is the table name and product_code is the column

Generate random int value from 3 to 6

Is it possible in Microsoft SQL Server generate random int value from Min to Max (3-9 example, 15-99 e.t.c)
I know, I can generate from 0 to Max, but how to increase Min border?
This query generate random value from 1 to 6. Need to change it from 3 to 6.
SELECT table_name, 1.0 + floor(6 * RAND(convert(varbinary, newid()))) magic_number
FROM information_schema.tables
Added 5 sec later:
SELECT table_name, 3.0 + floor(4 * RAND(convert(varbinary, newid()))) magic_number
FROM information_schema.tables
A helpful editor added the 'Select' before each statement but the point of this item is that it can generate unique keys for each row in a return, not just one item (For that I would us the Rand() function).
For example:
Select top 100 Rand(),* from tblExample
Would return the same random value for all 100 rows.
While:
Select top 100 ABS(CHECKSUM(NEWID()) % 10),* from tblexample
Would return a different random value between 0 and 9 on each row in the return.
So while the select makes it easier to copy and paste, you can copy the logic into a select statement if that is what is required.
This generates a random number between 0-9
SELECT ABS(CHECKSUM(NEWID()) % 10)
1 through 6
SELECT ABS(CHECKSUM(NEWID()) % 6) + 1
3 through 6
SELECT ABS(CHECKSUM(NEWID()) % 4) + 3
Dynamic (Based on Eilert Hjelmeseths Comment - thanks to jiraiya for providing the visual presentation)
SELECT ABS(CHECKSUM(NEWID()) % (#max - #min + 1)) + #min
Updated based on comments:
NEWID generates random string (for each row in return)
CHECKSUM takes value of string and creates number
modulus (%) divides by that number and returns the remainder (meaning max value is one less than the number you use)
ABS changes negative results to positive
then add one to the result to eliminate 0 results (to simulate a dice roll)
I see you have added an answer to your question in SQL Server 2008 you can also do
SELECT 3 + CRYPT_GEN_RANDOM(1) % 4 /*Random number between 3 and 6*/
FROM ...
A couple of disadvantages of this method are
This is slower than the NEWID() method
Even though it is evaluated once per row the query optimiser does not realise this which can lead to odd results.
but just thought I'd add it as another option.
You can do this:
DECLARE #maxval TINYINT, #minval TINYINT
select #maxval=24,#minval=5
SELECT CAST(((#maxval + 1) - #minval) *
RAND(CHECKSUM(NEWID())) + #minval AS TINYINT)
And that was taken directly from this link, I don't really know how to give proper credit for this answer.
Here is the simple and single line of code
For this use the SQL Inbuild RAND() function.
Here is the formula to generate random number between two number (RETURN INT Range)
Here a is your First Number (Min) and b is the Second Number (Max) in Range
SELECT FLOOR(RAND()*(b-a)+a)
Note: You can use CAST or CONVERT function as well to get INT range number.
( CAST(RAND()*(25-10)+10 AS INT) )
Example:
SELECT FLOOR(RAND()*(25-10)+10);
Here is the formula to generate random number between two number (RETURN DECIMAL Range)
SELECT RAND()*(b-a)+a;
Example:
SELECT RAND()*(25-10)+10;
More details check this: https://www.techonthenet.com/sql_server/functions/rand.php
Simply:
DECLARE #MIN INT=3; --We define minimum value, it can be generated.
DECLARE #MAX INT=6; --We define maximum value, it can be generated.
SELECT #MIN+FLOOR((#MAX-#MIN+1)*RAND(CONVERT(VARBINARY,NEWID()))); --And then this T-SQL snippet generates an integer between minimum and maximum integer values.
You can change and edit this code for your needs.
Nice and simple, from Pinal Dave's site:
http://blog.sqlauthority.com/2007/04/29/sql-server-random-number-generator-script-sql-query/
DECLARE #Random INT;
DECLARE #Upper INT;
DECLARE #Lower INT
SET #Lower = 3 ---- The lowest random number
SET #Upper = 7 ---- One more than the highest random number
SELECT #Random = ROUND(((#Upper - #Lower -1) * RAND() + #Lower), 0)
SELECT #Random
(I did make a slight change to the #Upper- to include the upper number, added 1.)
In general:
select rand()*(#upper-#lower)+#lower;
For your question:
select rand()*(6-3)+3;
<=>
select rand()*3+3;
SELECT ROUND((6 - 3 * RAND()), 0)
Lamak's answer as a function:
-- Create RANDBETWEEN function
-- Usage: SELECT dbo.RANDBETWEEN(0,9,RAND(CHECKSUM(NEWID())))
CREATE FUNCTION dbo.RANDBETWEEN(#minval TINYINT, #maxval TINYINT, #random NUMERIC(18,10))
RETURNS TINYINT
AS
BEGIN
RETURN (SELECT CAST(((#maxval + 1) - #minval) * #random + #minval AS TINYINT))
END
GO
DECLARE #min INT = 3;
DECLARE #max INT = 6;
SELECT #min + ROUND(RAND() * (#max - #min), 0);
Step by step
DECLARE #min INT = 3;
DECLARE #max INT = 6;
DECLARE #rand DECIMAL(19,4) = RAND();
DECLARE #difference INT = #max - #min;
DECLARE #chunk INT = ROUND(#rand * #difference, 0);
DECLARE #result INT = #min + #chunk;
SELECT #result;
Note that a user-defined function thus not allow the use of RAND(). A workaround for this (source: http://blog.sqlauthority.com/2012/11/20/sql-server-using-rand-in-user-defined-functions-udf/) is to create a view first.
CREATE VIEW [dbo].[vw_RandomSeed]
AS
SELECT RAND() AS seed
and then create the random function
CREATE FUNCTION udf_RandomNumberBetween
(
#min INT,
#max INT
)
RETURNS INT
AS
BEGIN
RETURN #min + ROUND((SELECT TOP 1 seed FROM vw_RandomSeed) * (#max - #min), 0);
END

SQL, parse string like "12M" to 12 * 30 as an int

In a stored procedure in SQL Server 2008, I need to parse strings like "12M" and return 12 * 30 days as an int. So, I am basically parsing and calculating the number of days the string represents.
I am not sure how this can be done.
I am thinking to do a while loop over each character in the string. Any suggestion?
Edit (not author): It appears the goal is to convert "xM" to "x Months in days" (30 days/month), "yY" to "y Years in days" (365days/year), and "zD" to "z Days" (no transformation).
Use:
DECLARE val INT
SET val = CASE UPPER(RIGHT(column, 1))
WHEN 'Y' THEN
CAST(SUBSTRING(column, 1, LEN(column)-1) AS INT) * 365
WHEN 'M' THEN
CAST(SUBSTRING(column, 1, LEN(column)-1) AS INT) * 30
WHEN 'D' THEN
CAST(SUBSTRING(column, 1, LEN(column)-1) AS INT) * 1
END
For testing:
WITH sample AS (
SELECT '12M' AS [column]
UNION ALL
SELECT '100M'
UNION ALL
SELECT '10000M'
UNION ALL
SELECT '1D'
UNION ALL
SELECT '34D'
UNION ALL
SELECT '2343M' )
SELECT s.[column],
CASE UPPER(RIGHT(s.[column], 1))
WHEN 'Y' THEN
CAST(SUBSTRING(s.[column], 1, LEN(s.[column])-1) AS INT) * 365
WHEN 'M' THEN
CAST(SUBSTRING(s.[column], 1, LEN(s.[column])-1) AS INT) * 30
WHEN 'D' THEN
CAST(SUBSTRING(s.[column], 1, LEN(s.[column])-1) AS INT) * 1
END
FROM sample s
Returns:
col result
-----------------
12M 360
100M 3000
10000M 300000
1D 1
34D 34
2343M 70290
Addendum
These values should be stored separately, as two columns:
value INT
value_type CHAR(1) (D, M, Y)
Yes, do NOT do it in SQL. Put in a .NET based stored procedure for that stuff - you will love being able to properly debug it easily, test it and have access ot the date manupulation classes.