SQL Server formatting negative values from selected data - sql

I am new to stackoverflow but I do search it often.
I am creating a report from data in which I have to format negative numbers like,
-00000010 (9 characters max)
I am getting this,
000000-10
This is what I am attempting now but I'm having issues. Any help would be greatly appreciated.
SELECT 'H'
+ DG.BLOCK
+ LEFT(DG.ZIP,5)
+ RIGHT('000000000'
+ CAST(CAST(SUM(DG.WP)AS INT) AS VARCHAR(9)),9)
+ RIGHT('000000000' + CAST(CAST(SUM(DG.WE)AS INT) AS VARCHAR(9)),9)
+ RIGHT('000000000' + CAST(CAST(SUM(DG.EP)AS INT) AS VARCHAR(9)),9)
+ RIGHT('000000000' + CAST(CAST(SUM(DG.EE)AS INT) AS VARCHAR(9)),9)
+ RIGHT('000000000' + CAST(CAST(COUNT(DGG.CLAIMCONTROL)AS INT) AS VARCHAR(9)),9)
+ RIGHT('000000000' + CAST(CAST(SUM(DGG.INC) AS INT) AS VARCHAR(9)),9)
+ RIGHT('000000000' + CAST(CAST(SUM(DGG.PAID)AS INT) AS VARCHAR(9)),9)
+ RIGHT('000000000' + CAST(CAST(SUM(DGG.ALAE) AS INT) AS VARCHAR(9)),9)
AS [H Record]
FROM TABLE

If 2012+, you have the option of Format().
Example
Select replace(format(-1,' 000000000'),'- ','-')
Returns
-000000001 If number was negative
000000001 If number was positive
Just a word of caution. Format() has some great functionality, but is not known to be a high performer.

The following code demonstrates formatting the data as either 9 digits plus an optional sign or as a fixed 9 characters including the sign.
-- Sample data.
declare #Samples as Table ( Sample Int );
insert into #Samples ( Sample ) values ( 0 ), ( 1 ), ( -10 ), ( 100 ), ( -1000 );
-- Format the data.
select Sample,
case when Sign( Sample ) = -1 then '-' else '' end +
Right( Replicate( '0', 8 ) + Cast( Abs( Sample ) as VarChar(9) ), 9 ) as FormattedSample9PlusSign,
case when Sign( Sample ) = -1 then
'-' + Right( Replicate( '0', 7 ) + Cast( -Sample as VarChar(8) ), 8 ) else
Right( Replicate( '0', 8 ) + Cast( Sample as VarChar(9) ), 9 ) end as FormattedSample9
from #Samples;
Tip: In SSMS use query results to text (Ctrl-T) for more convenient display.

You can try this, if you do not have v2012+:
DECLARE #mockup TABLE(SomeNumber INT);
DECLARE #padWidth INT=3;
INSERT INTO #mockup VALUES(-1000),(-500),(-1),(0),(1),(500),(1000);
SELECT CASE WHEN m.SomeNumber < 0 THEN '-' ELSE ' ' END
+ REPLACE(STR(ABS(m.SomeNumber),#padWidth),' ','0')
FROM #mockup AS m;
Numbers, which are to big, will be returned as ***. This is better than other approaches cutting the string with RIGHT or LEFT. They might return a bad result...
This is returned
-***
-500
-001
000
001
500
***

In DB2 this works to get a number of 15 digits including the sign:
CASE WHEN MYNUMBER < 0 THEN '-' || LPAD(MYNUMBER , 14, 0)
ELSE LPAD(MYNUMBER , 15, 0)
END

Related

ORDER BY in SQL where column is synthetic string with embedded integer

I have the following table :
CREATE TABLE TEST
(
name VARCHAR(10),
date_of_entry DATE,
flag1 INT,
flag2 INT,
salary FLOAT,
flag3 INT,
id INT
);
with the following rows :
name date_of_entry flag1 flag2 salary flag3 id
--------------------------------------------------------------
AGMA 2018-11-08 0 1 265466940 1 1
AGMA 2018-11-08 0 1 220737125 1 2
AGMA 2018-11-08 0 1 181270493 0 3
AGMA 2018-11-08 0 1 8584205 0 4
I would like to execute the following SQL to order the rows in a specific manner :
SELECT
name
+ '.' + CONVERT(varchar(8), date_of_entry, 112)
+ '.' + CONVERT(varchar(1), flag1)
+ '.' + CONVERT(varchar(1), flag2)
+ '.' + CONVERT(varchar(2555), salary)
+ '.' + CONVERT(varchar(1), flag3)
+ '.' + CONVERT(varchar(1), id) AS SYNTHETIC_ORDER
FROM
TEST
ORDER BY
SYNTHETIC_ORDER DESC
However, the salary column gets sorted incorrectly within the string. So my end result is (when executed within Microsoft SQL Server) :
SYNTHETIC_ORDER
-----------------------------------
AGMA.20181108.0.1.8.58421e+006.0.4
AGMA.20181108.0.1.2.65467e+008.1.1
AGMA.20181108.0.1.2.20737e+008.1.2
AGMA.20181108.0.1.1.8127e+008.0.3
As can be noted, the result is that id 4 comes first, when I want id 1 to come first.
Expected result :
SYNTHETIC_ORDER
-----------------------------------
AGMA.20181108.0.1.2.65467e+008.1.1
AGMA.20181108.0.1.2.20737e+008.1.2
AGMA.20181108.0.1.1.8127e+008.0.3
AGMA.20181108.0.1.8.58421e+006.0.4
Is there a way to ensure that the salary is correctly ordered in this SQL?
Why can't you just order it by the individual columns?
SELECT
date_of_entry, flag1, flag2, salary, flag3
, name
+ '.' + CONVERT(varchar(8), date_of_entry, 112)
+ '.' + CONVERT(varchar(1), flag1)
+ '.' + CONVERT(varchar(1), flag2)
+ '.' + CONVERT(varchar(2555), salary)
+ '.' + CONVERT(varchar(1), flag3)
+ '.' + CONVERT(varchar(1), id) AS SYNTHETIC_ORDER
FROM TEST
ORDER BY date_of_entry DESC, flag1 DESC, flag2 DESC, salary DESC, flag3 DESC
This will get you the MAX.
SELECT SYNTHETIC_ORDER
FROM (
SELECT
ROW_NUMBER() OVER(ORDER BY date_of_entry DESC, flag1 DESC, flag2 DESC, salary DESC, flag3 DESC) AS RowNum
, name
+ '.' + CONVERT(varchar(8), date_of_entry, 112)
+ '.' + CONVERT(varchar(1), flag1)
+ '.' + CONVERT(varchar(1), flag2)
+ '.' + CONVERT(varchar(2555), salary)
+ '.' + CONVERT(varchar(1), flag3)
+ '.' + CONVERT(varchar(1), id) AS SYNTHETIC_ORDER
FROM TEST
) a
WHERE RowNum = 1
Fixed width rep and it uses only functions available in both H2 (not tagged) and SQLS (tagged):
SELECT
CONCAT(
CAST(name as CHAR(10)), --right pad to 10,
YEAR(date_of_entry),
RIGHT(CONCAT('0',MONTH(date_of_entry)),2),
RIGHT(CONCAT('0',DAY(date_of_entry)),2), --yyyymmdd
CAST(flag1 as CHAR(1)), --rpad to 1, doesn't need cast if never null/0 length
CAST(flag2 as CHAR(1)), --maybe doesn't need cast, see above
RIGHT(CONCAT('0000000000', CAST(salary AS INT)),10), --lpad with 0 to 10 wide
CAST(flag3 as CHAR(1)), --maybe doesn't need cast, see above
RIGHT(CONCAT('0000000000', id), 10) --lpad with 0 to 10 wide
) AS SYNTHETIC_ORDER
FROM
TEST
ORDER BY
SYNTHETIC_ORDER DESC
Points of note:
Your CREATE TABLE statement doesn't mention ID, but your query does; included ID
Your query doesn't mention NAME but your example data output does; included NAME
You might not need to pad the ID or salary so much
Come of the casts to chars (e.g. on flag columns) can be dropped (if the flag column is 100% guaranteed to always be 1 char long)
If salary max value in table is larger than an int can hold, consider a cast to something else
By padding the salary with leading zeroes, the sort will work out. Normalising it to between 0 and 1 could also work, if all the values were padded out to the same width but you possibly then get the problem that loss of precision (dividing a 10 digit salary down to eg 0.123456) will cause two different salaries to merge because there aren't enough digits to fully represent. With any division-that-quantizes-to-lower precision you then risk the original values sorting wrongly (e.g. If salaries of 1000000000 and 1000000001 with id of 2 and 1 respectively both normalise to 0.123456 they would end up sorted wrongly. To guard against this you probably need as many digits for the division answer as the salary had in the first place, padded to a fixed width, but if you've gone that far you might as well just pad all the salaries out either to the width of the widest or to some width that will contain them all. Here utilising a cast to an int might be handy, if the int will overflow. You can make a decision to pad to one digit wider than an int will hold and then if someone inserts a large value in future and your query starts failing because of overflow it at least won't silently deliver wrong results because the pad is chopping digits off the left hand edge. In addressing the cast to bit you can choose whether to add some logic that pads out to the LENGTH() of the string form of the SELECT MAX salary
CONCAT is nice cos you can pass most types to it without first casting to varchar, and it doesn't null the whole thing if you concat a null on, unlike regular string concat ops with + or ||
This will get you what you want but maybe not in a way you like fun to the sub-query
SELECT
name
+ '.' + CONVERT(varchar(8), date_of_entry, 112)
+ '.' + CONVERT(varchar(1), flag1)
+ '.' + CONVERT(varchar(1), flag2)
+ '.' + CONVERT(varchar(2555), salary / (SELECT MIN(salary) AS min_sal FROM TEST))
+ '.' + CONVERT(varchar(1), flag3)
+ '.' + CONVERT(varchar(1), id) AS SYNTHETIC_ORDER
FROM TEST
ORDER BY SYNTHETIC_ORDER DESC
Can you try:
SELECT name
+ '.' + CONVERT(varchar(8), date_of_entry, 112)
+ '.' + CONVERT(varchar(1), flag1)
+ '.' + CONVERT(varchar(1), flag2)
+ '.' + CHAR(DIGITS(salary))
+ '.' + CONVERT(varchar(1), flag3)
+ '.' + CONVERT(varchar(1), id) AS SYNTHETIC_ORDER
FROM TEST
ORDER BY SYNTHETIC_ORDER DESC
If you need the "max" synthetic order, simply do:
select top (1) name, date_of_entry, falg1, flag2, salary, flag3, id
from test
order by name desc, date_of_entry desc, flag1 desc, flag2 desc, salary desc, flag3 desc, id desc;
I don't see a reason to stuff these values into a string.
If you want a separate row for each name, then:
select top (1) with ties name, date_of_entry, falg1, flag2, salary, flag3, id
from test
order by row_number() over (partition by name desc order by date_of_entry desc, flag1 desc, flag2 desc, salary desc, flag3 desc, id desc);
Sir,
As soon as you convert it to a huge string(varchar), it follows alphabetical order instead of order of magnitude like you are expecting.
Can't you just use a row_number as your "Synthetic order". In other words, instead of this:
SELECT
name
+ '.' + CONVERT(varchar(8), date_of_entry, 112)
+ '.' + CONVERT(varchar(1), flag1)
+ '.' + CONVERT(varchar(1), flag2)
+ '.' + CONVERT(varchar(2555), salary)
+ '.' + CONVERT(varchar(1), flag3)
+ '.' + CONVERT(varchar(1), id) AS SYNTHETIC_ORDER
FROM
TEST
ORDER BY
SYNTHETIC_ORDER DESC
this:
SELECT
id,
row_number() over (order by date_of_entry,flag1,flag2,salary,flag3,id) as SYNTHETIC_ORDER
FROM
TEST
ORDER BY
SYNTHETIC_ORDER DESC
Good luck!
You can change your query to
SELECT
name
+ '.' + CONVERT(varchar(8), date_of_entry, 112)
+ '.' + CONVERT(varchar(1), flag1)
+ '.' + CONVERT(varchar(1), flag2)
+ '.' + CONVERT(varchar(2555), salary)
+ '.' + CONVERT(varchar(1), flag3)
+ '.' + CONVERT(varchar(1), id) AS SYNTHETIC_ORDER
FROM
TEST
ORDER BY
salary DESC
Try using the following routine.
In your code, you write CONVERT(varchar(2555), salary). This doesn't work because when you convert a float to a string using convert, the sort order of the result is not the same as the sort order of the float. e.g. 3 < 20, but '20' < '3'.
The routine FloatToSortable solves that problem. If you pass a bunch of floats through the routine, and sort on the results, you'll get the same order as if you sorted the floats. e.g. FloatToSortable(3) < FloatToSortable(20).
And so in your code, where you write
CONVERT(varchar(2555), salary)
replace it with
dbo.FloatToSortable(salary).
You say that you can't add a function to your database. That's unfortunate. I've just used functions here to avoid repetition. You can certainly use the same premise to create a single expression that will give the same result, although that expression will be much longer and harder to understand.
-- FloatToSortable takes a FLOAT parameter and returns a string
-- such that the sort order of FLOATs X and Y will match the
-- sort order of strings F(X) and F(Y).
--
-- The internal format of FLOAT is an 8-byte double-precision
-- float, starting with the SIGN where 0=positive and 1=negative,
-- followed by the EXPONENT and then the MANTISSA.
-- If it weren't for the SIGN we could just sort by the binary
-- value. Because of the sign we need to XOR the binary
-- before we can sort on it.
--
-- If the parameter is positive, XOR with 8000000000000000
-- If the parameter is negative, XOR with FFFFFFFFFFFFFFFF
--
-- Then we convert each byte to a Sortable string. We could
-- use hexidecimal, but it's simpler just use letters A..O
--
-- This function is working with salaries, so we don't have
-- to worry about NANs and Infinities, but it should work
-- with all values.
-- NybbleToSortable
-- Given an integer in range 0..15 return a character
-- We just map the number to a letter, 0 -> 'A', 15 -> 'O'
create function NybbleToSortable ( #a tinyint )
returns varchar(16)
as
begin
return char(#a + ascii('A'))
end
go
-- XorToSortable
-- Take the nth byte of #a, XOR it with the nth byte of #b,
-- and convert that byte to a Sortable string.
create function dbo.XorToSortable ( #a varbinary(8),
#b varbinary(8),
#n int )
returns varchar(16)
as
begin
declare #aa tinyint, #bb tinyint, #x tinyint
set #aa = cast ( substring ( #a, #n, 1 ) as tinyint )
set #bb = cast ( substring ( #b, #n, 1 ) as tinyint )
set #x = #aa ^ #bb
return dbo.NybbleToSortable ( #x / 16 )
+ dbo.NybbleToSortable ( #x % 16 )
end
go
create function dbo.FloatToSortable ( #x float )
returns varchar(16)
as
begin
declare #m varbinary(8), #b varbinary(8)
set #b = cast(#x as varbinary(8))
if #x < 0
set #m = 0xFFFFFFFFFFFFFFFF
else
set #m = 0x8000000000000000
return dbo.XorToSortable ( #b, #m, 1 )
+ dbo.XorToSortable ( #b, #m, 2 )
+ dbo.XorToSortable ( #b, #m, 3 )
+ dbo.XorToSortable ( #b, #m, 4 )
+ dbo.XorToSortable ( #b, #m, 5 )
+ dbo.XorToSortable ( #b, #m, 6 )
+ dbo.XorToSortable ( #b, #m, 7 )
+ dbo.XorToSortable ( #b, #m, 8 )
end
go
-- Create some test data
create table dbo.sal ( salary float, salbin as dbo.FloatToSortable(salary)) ;
go
declare #x float
set #x = pi()/9876543
while abs(#x) < 170
begin
insert into sal ( salary ) values ( #x )
set #x=#x * -2.014159265
end
select * from sal order by salbin
-- result is:
-- salary salbin
-- ---------------------- ----------------
-- -51.6508818660658 DPLGCMKPOHCLNIAP
-- -12.7318092715982 DPNGIJFAELIPCGOM
-- -3.1383581745746 DPPGOEKEHICIIKOI
-- -0.773597202236665 EABHDOLBBEIDLJLO
-- -0.190689716730473 EADHJHHKLHHKMEDG
-- -0.0470045237516562 EAFHOPAFOHHBPGCJ
-- -0.0115864939704268 EAHIEFFHBKJCNPMF
-- -0.00285604090440349 EAJIJKHCLHAGILBG
-- -0.000704006722693307 EALIOOFNBDCOOAMG
-- -0.000173535842863177 EANJEBBKHFNPDPAD
-- -4.27761380502506E-05 EAPJJCKPBGJEEFHA
-- -1.0544207791913E-05 EBBJODBPBNKKNIPE
-- -2.59912004745334E-06 EBDKDCGOKEJGGCDL
-- -6.4067639356036E-07 EBFKIAKBLGBGEJKE
-- 3.180862629353E-07 LOJFFIKOCNMOIIKB
-- 1.29042429395639E-06 LOLFKGFEIEBGJGMI
-- 5.23504172442538E-06 LONFPFBFEPNNJAIF
-- 2.12377138161667E-05 LOPGEEPEJEJMLAHP
-- 8.61579547748313E-05 LPBGJFPGGEGGLLMK
-- 0.000349528825712453 LPDGOIBOOABNBNJK
-- 0.00141798166313501 LPFHDLHCDHKFMEBP
-- 0.00575252124882327 LPHHIPPEKKCBMBFH
-- 0.0233370441794017 LPJHOFKKIGCELCJB
-- 0.094674597011311 LPLIDMJICJOMPBIA
-- 0.384079459692908 LPNIJEMCADJMJBKO
-- 1.55814797226306 LPPIOOCMJBHDCNED
-- 6.32115319420792 MABJEINMGCAIIEAO
-- 25.6438916046025 MADJKENGBEIHOPME
-- 104.033102255957 MAFKACBOFIOMLAIO

Convert or Cast vchar to int without character '-' or -

I need select in SQL Table this (00-00066) and get after convert this (0000066)
just like>>>>>
declare #v1 varchar(21) = '00-00066', --- I need ignore this character '-'
#v2 varchar(21) = '000 - 66 ',
#v3 varchar(21) = '00-66' + char(13) + char(10),
#v4 varchar(21) = char(9) + '66000-00'
select cast(#v1 as int) -- error
select cast(#v2 as int) -- error
select cast(#v3 as int) -- error
select cast(#v4 as int) -- error
DigitsOnlyEE was developed specifically for this problem and is does it faster than any function I've seen. You just have to grab the code to create it from the link provided.
Here's a quick example of how it works:
SELECT DigitsOnly FROM dbo.DigitsOnlyEE('000 - 66 ');
Results:
00066
To use against a table:
-- sample data
DECLARE #table TABLE (oldString varchar(100));
INSERT #table VALUES ('00-00066'),('000 - 66 '),
('00-66' + char(13) + char(10)), (char(9) + '66000-00');
-- solution
SELECT oldString, DigitsOnly
FROM #table
CROSS APPLY dbo.DigitsOnlyEE(oldString);
Results:

Converting CHAR string to nth letter in Alphabet string in SQL

I have to build a process that takes a VARCHAR string (for example 'AHT559') and converts it to a INT only string by converting the Alphabetic chars to INTEGERS based on the nth letter in the alphabet. The above would thus result in: 010820559.
I have done this in SAS before, but I'm relatively new to SQL. What would be the best way to do this in SQL?
Here is what I've done in SAS:
DO _i = 1 TO length( account );
IF (rank( char( account, _i ) ) -64) < 0 THEN agreement_hash = CATS( agreement_hash, char( account, _i ) );
ELSE IF (rank( char( account, _i ) ) -64) < 10 THEN agreement_hash = CATS( agreement_hash, 0, rank( char( account, _i ) )-64 );
ELSE agreement_hash = CATS( agreement_hash, rank( char( account, _i ) )-64 );
END;
If the format of the values is always the same as you state in the comments and you only need to process a single value at a time you can do some simple string manipulation to convert the characters to integers using their ASCII values, and subtracting 64 to get the number of the alphabetic character:
SELECT ASCII('A') -- produces 65
SELECT ASCII('A') - 64 -- produces 1
This is a little long winded and could be done in less lines of code, but it's separated for clarity.
DECLARE #val NVARCHAR(10) = 'AHT559'
-- get first, second and third character numeric values
DECLARE #first INT = ASCII(SUBSTRING(#val, 1, 1)) - 64
DECLARE #second INT = ASCII(SUBSTRING(#val, 2, 1)) - 64
DECLARE #third INT = ASCII(SUBSTRING(#val, 3, 1)) - 64
-- join them together adding a '0' if < 10
SELECT RIGHT('0' + CAST(#first AS VARCHAR(2)), 2)
+ RIGHT('0' + CAST(#second AS VARCHAR(2)), 2)
+ RIGHT('0' + CAST(#third AS VARCHAR(2)), 2)
+ RIGHT(#val, 3)
Tested on 4 million rows:
-- temp table creation - takes approx 100 seconds on my machine
CREATE TABLE #temp (val NVARCHAR(6))
DECLARE #rowno INT = 1
SELECT #rowno = 1
WHILE #rowno <= 4000000
BEGIN
INSERT INTO #temp ( val ) VALUES ( 'AHT559' )
SELECT #rowno = #rowno + 1
END
To run this code against the entire temp table takes < 20 seconds on my machine:
SELECT val AS OrignalValue,
RIGHT('0' + CAST( ASCII(SUBSTRING(val, 1, 1)) - 64 AS VARCHAR(2)), 2)
+ RIGHT('0' + CAST( ASCII(SUBSTRING(val, 2, 1)) - 64 AS VARCHAR(2)), 2)
+ RIGHT('0' + CAST( ASCII(SUBSTRING(val, 3, 1)) - 64 AS VARCHAR(2)), 2)
+ RIGHT(val, 3) AS FormattedValue
FROM #temp
Here is a similar script for sqlserver, any character which is not a capital letter is assumed a digit in this syntax:
DECLARE #x varchar(100) = 'AHT559'
DECLARE #p int = len(#x)
WHILE #p > 0
SELECT #x =
CASE WHEN substring(#x, #p, 1) between 'A' and 'Z'
THEN stuff(#x, #p, 1, right(ascii(substring(#x, #p, 1)) - 64 + 100, 2))
ELSE #x END,
#p -= 1
SELECT #x
Result:
010820559
You could use something like the below, possibly as a scalar function to do this conversion.
DECLARE #i INT
DECLARE #Item NVARCHAR(4000) = 'AHT1234'
DECLARE #ItemTable TABLE
(
Item NCHAR(1)
)
SET #i = 1
--Split the input string into separate characters, store in temp table
WHILE (#i <= LEN(#Item))
BEGIN
INSERT INTO #ItemTable(Item)
VALUES(SUBSTRING(#Item, #i, 1))
SET #i = #i + 1
END
DECLARE #AlphaTable TABLE (
Letter NCHAR(1),
Position NVARCHAR(2)
)
-- Populate this with the whole alphabet obviously. Could be a permanent rather than temp table.
INSERT INTO #AlphaTable
( Letter, Position )
VALUES ( N'A', '01'),
(N'H', '08'),
(N'T', '20')
DECLARE #Output NVARCHAR(50)
-- Convert the output and concatenate it back to a single output.
SELECT #Output = COALESCE(#output, '') + Converted
FROM (
SELECT CASE WHEN ISNUMERIC(Item) = 1
THEN CONVERT(NVARCHAR(1), Item)
ELSE (SELECT Position FROM #AlphaTable WHERE Letter = CONVERT(NCHAR(1), Item))
END AS Converted
FROM #ItemTable
) AS T1
SELECT #Output
GO
Try this.
DECLARE #STR VARCHAR(MAX)= 'AHT559',
#SP INT,
#SP_STR VARCHAR(50),
#OUTPUT VARCHAR(MAX)=''
DECLARE #TEMP_STR VARCHAR(50)
SET #TEMP_STR = #STR
WHILE Patindex('%[A-Z]%', #TEMP_STR) <> 0
BEGIN
SELECT #SP = Patindex('%[A-Z]%', #TEMP_STR)
SELECT #SP_STR = Upper(LEFT(#TEMP_STR, #SP))
SELECT #SP_STR = ( Ascii(#SP_STR) - 65 ) + 1
SELECT #TEMP_STR = Stuff(#TEMP_STR, 1, #SP, '')
SET #OUTPUT += RIGHT('0' + #SP_STR, 2)
END
SELECT #OUTPUT + Substring(#STR, Patindex('%[0-9]%', #STR), Len(#STR))
How about using a CTE to create every combination of the first 3 letters and using that to match to:
SQL Fiddle
MS SQL Server 2008 Schema Setup:
CREATE TABLE Accounts
(
Account VARCHAR(6)
)
INSERT INTO Accounts
VALUES ('AHT559'), ('BXC556'),
('CST345')
Query 1:
;WITH AlphaToNum
AS
(
SELECT *
FROM (VALUES
('A', '01'), ('B', '02'), ('C', '03'), ('D', '04'),
('E', '05'), ('F', '06'), ('G', '07'), ('H', '08'),
('I', '09'), ('J', '10'), ('K', '11'), ('L', '12'),
('M', '13'), ('N', '14'), ('O', '15'), ('P', '16'),
('Q', '17'), ('R', '18'), ('S', '19'), ('T', '20'),
('U', '21'), ('V', '22'), ('W', '23'), ('X', '24'),
('Y', '25'), ('Z', '26')
) X(alpha, num)
),
MappingTable
As
(
SELECT A1.alpha + A2.alpha + A3.alpha as match, A1.num + A2.num + A3.num as val
FROM AlphaToNum A1
CROSS APPLY AlphaToNum A2
CROSS APPLY AlphaToNum A3
)
SELECT A.Account, M.val + SUBSTRING(A.Account,4, 3) As ConvertedAccount
FROM MappingTable M
INNER JOIN Accounts A
ON LEFT(A.Account,3) = M.match
Results:
| Account | ConvertedAccount |
|---------|------------------|
| AHT559 | 010820559 |
| BXC556 | 022403556 |
| CST345 | 031920345 |
This is probably best done using a CLR UDF, but a full answer is too long for this format.
Basically you need to create a UDF (User defined function) that takes a string (nvarchar...) as an input and returns a string as an output. You can do that with C# quite easily, and you need to wrap it with the CLR integration requirements.
You can see here for relevant information.
The code could look something like:
[Microsoft.SqlServer.Server.SqlFunction(
IsDeterministic=true,
IsPrecise=true,
SystemDataAccess=SystemDataAccessKind.None)]
public static SqlString ToNthAlpha(SqlString value)
{
if(value.IsNull)
return value;
char []chars = value.Value.ToCharArray();
StringBuilder res = new StringBuilder();
for(int i = 0; i < chars.Length; i++)
{
if(chars[i] >= 'A' && chars[i] <= 'Z')
res.AppendFormat("{0:00}", chars[i] - 'A');
res.Append(chars[i]);
}
return new SqlString(res.ToString());
}

Get the value of a column replacing the comma separator

How can I get each value of a column that has a comma separator in her value ?
Example:
ID ColumnUnified
1 12,34,56,78
2 80,99,70,56
What I want is a query to get the number without comma. If possible, in collumns.
12 34 56 78
This will work for any number of values http://beyondrelational.com/modules/2/blogs/70/posts/10844/splitting-delimited-data-to-columns-set-based-approach.aspx
The solution Madhivanan's link refers to is very creative, but I had a slight problem with it on SQL Server 2012 related to the name of one of the columns (Start). I've modified the code in his answer to use StartPos instead of Start for the column name.
I was not familiar with the system procedure spt_values, but I found a very informative description of the procedure here on SO for those who are interested in exactly how this solution works.
Finally, here's the (slightly) revised code from Madhivana's answer:
CREATE TABLE #test(id int, data varchar(100))
INSERT INTO #test VALUES (1,'This,is,a,test,string')
INSERT INTO #test VALUES (2,'See,if,it,can,be,split,into,many,columns')
DECLARE #pivot varchar(8000)
DECLARE #select varchar(8000)
SELECT #pivot = COALESCE(#pivot + ',', '') + '[col'
+ CAST(number + 1 AS VARCHAR(10)) + ']'
FROM master..spt_values
WHERE type = 'p'
AND number <= ( SELECT MAX(LEN(data) - LEN(REPLACE(data, ',', '')))
FROM #test
)
SELECT #select = '
select p.*
from (
select
id,substring(data, StartPos+2, endPos-StartPos-2) as token,
''col''+cast(row_number() over(partition by id order by StartPos) as varchar(10)) as n
from (
select
id, data, n as StartPos, charindex('','',data,n+2) endPos
from (select number as n from master..spt_values where type=''p'') num
cross join
(
select
id, '','' + data +'','' as data
from
#test
) m
where n < len(data)-1
and substring(data,n+1,1) = '','') as data
) pvt
Pivot ( max(token)for n in (' + #pivot + '))p'
EXEC(#select)
DROP TABLE #test

How do I find the count of concatenated columns of int and string data types in SQL CE?

How to merge different data type of column on SQL?
I have a table:
+-----------+-------------+
+ Column + Data Type +
+-----------+-------------+
+ Day + Int +
+ Time + Varchar +
+ Quota + Int +
+-----------+-------------+
I want to merge all of the column on that table.
I tried this but it doesn't work:
SELECT Day + ' - ' + Time + ' : ' + Quota AS [Description], COUNT(*) AS [Total]
FROM table
GROUP BY Day + ' - ' + Time + ' : ' + Quota
The error message: Data Conversion Failed. [ OLE DB status value (if known) = 2 ]
When I tried to merge only the same data type, that is: Day and Quota, it's work. How can I do that?
Columns of data type int should be converted or type cast to string data type like VARCHAR to perform string concatenation.
Click this link to view the demo in SQL Fiddle.
Script: Tested in SQL Server 2012.
CREATE TABLE dbo.MyTable
(
[Day] int NOT NULL
, [Time] varchar NOT NULL
, [Quota] int NOT NULL
);
INSERT INTO dbo.MyTable ([Day], [Time], [Quota]) VALUES
(1, '2', 101),
(1, '1', 101),
(2, '3', 101),
(2, '3', 101),
(3, '4', 263);
SELECT Description
, COUNT(Description) AS DescriptionCount
FROM
(
SELECT CAST([Day] AS VARCHAR)
+ ' - ' + Time
+ ' : ' + CAST([Quota] AS VARCHAR) AS [Description]
FROM dbo.MyTable
) T1
GROUP BY Description;
Output:
DESCRIPTION DESCRIPTIONCOUNT
----------- ----------------
1 - 1 : 101 1
1 - 2 : 101 1
2 - 3 : 101 2
3 - 4 : 263 1
UPDATE
SQL Compact Edition version of the query:
SELECT LTRIM(RTRIM(STR(Day))) + ' - ' +
LTRIM(RTRIM(Time)) + ' : ' +
LTRIM(RTRIM(STR(Quota))) AS Description
, COUNT(*) AS DescriptionCount
FROM MyTable
GROUP BY LTRIM(RTRIM(STR(Day))) + ' - ' +
LTRIM(RTRIM(Time)) + ' : ' +
LTRIM(RTRIM(STR(Quota)))
Output:
This is a mashup of my answer and Siva's, hopefully it will solve the asker's problem:
SQL Fiddle link
CREATE TABLE MyTable
(
iDay int NOT NULL
, sTime varchar(50) NOT NULL
, iQuota int NOT NULL
);
INSERT INTO MyTable (iDay, sTime, iQuota) VALUES
(1, '2', 101),
(1, '1', 101),
(2, '3', 101),
(2, '3', 101),
(3, '4', 263);
SELECT CAST(iDay AS VARCHAR) + ' - ' + sTime + ' : ' +
CAST(iQuota AS VARCHAR) AS Description
FROM MyTable
GROUP BY CAST(iDay AS VARCHAR) + ' - ' + sTime + ' : ' +
CAST(iQuota AS VARCHAR)
(Assuming MySQL):
You are trying to perform addition on strings and integers. + is not a concatenation operator in mysql. The error you get is telling you that the string could not be converted to a numeric type.
Try this:
SELECT concat(Day, ' - ', Time, ' : ', Quota) AS `[Description]`, COUNT(*) AS `[Total]`
FROM table
GROUP BY `[Description]`
When you do the GROUP BY just write the field names. So...
SELECT Day + ' - ' + Time + ' : ' + Quota AS [Description], COUNT(*) AS [Total]
FROM table
GROUP BY Day, Time, Quota
Does that work?