SQL add column based on if statement - sql

I have a table with a 4 character (col1) that I would like to:
split in half
use an if statement to recode the left 2 characters as specific integers
concat left onto rght (separated by '.') to generate the fin column below.
col1
left
rght
fin
xx01
xx
01
01.1
xy01
xy
01
01.2
xz01
xz
01
01.3
Bit of a SQL noob so appreciate any advice. Thanks!

As col1 length is 4 digit fixed so here half means 2 digit. You can do ORDER BY operation either full value of col1 or LEFT(col1, 2).
-- SQL SERVER
SELECT col1, LEFT(col1, 2) "left"
, RIGHT(col1, 2) "right"
, RIGHT(col1, 2) + '.' + CAST((ROW_NUMBER() OVER (ORDER BY col1)) AS VARCHAR(2)) fin
FROM test
Please check from url https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=d76d9c504472f93ed3e4c53b88b94f57
Using CASE statement
SELECT col1, LEFT(col1, 2) "left"
, RIGHT(col1, 2) "right"
-- , RIGHT(col1, 2) + '.' + CAST((ROW_NUMBER() OVER (ORDER BY col1)) AS VARCHAR(2)) fin
, RIGHT(col1, 2) + '.' + CASE LEFT(col1, 2) WHEN 'xx' THEN '1' WHEN 'xy' THEN '2' ELSE '3' END fin
FROM test
Please check from url https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=a1f10444fd50c9c1c541c49d8f23344f

Related

Pad Zero before first hypen and remove spaces and add BA and IN

I have data as below
98-45.3A-22
104-44.0A-23
00983-29.1-22
01757-42.5A-22
04968-37.3A2-23
Output Looking for output as below in SQL Server
00098-BA45.3A-IN-22
00104-BA44.0A-IN-23
00983-BA29.1-IN-22
01757-BA42.5A-IN-22
04968-BA37.3A2-IN-23
I splitted parts to cope with tricky data templates. This should work even with non-dash-2-digit tail:
WITH Src AS
(
SELECT * FROM (VALUES
('98-45.3A-22'),
('104-44.0A-23'),
('00983-29.1-22'),
('01757-42.5A-22'),
('04968-37.3A2-23')
) T(X)
), Parts AS
(
SELECT *,
RIGHT('00000'+SUBSTRING(X, 1, CHARINDEX('-',X, 1)-1),5) Front,
'BA'+SUBSTRING(X, CHARINDEX('-',X, 1)+1, 2) BA,
SUBSTRING(X, PATINDEX('%.%',X), LEN(X)-CHARINDEX('-', REVERSE(X), 1)-PATINDEX('%.%',X)+1) P,
SUBSTRING(X, LEN(X)-CHARINDEX('-', REVERSE(X), 1)+1, LEN(X)) En
FROM Src
)
SELECT Front+'-'+BA+P+'-IN'+En
FROM Parts
It returns:
00098-BA45.3A-IN-22
00104-BA44.0A-IN-23
00983-BA29.1-IN-22
01757-BA42.5A-IN-22
04968-BA37.3A2-IN-23
Try this,
DECLARE #String VARCHAR(100) = '98-45.3A-22'
SELECT ISNULL(REPLICATE('0',6 - CHARINDEX('-',#String)),'') -- Add leading Zeros
+ STUFF(
STUFF(#String,CHARINDEX('-',#String),1,'-BA'), -- Add 'BA'
CHARINDEX('-',#String,CHARINDEX('-',#String)+1)+2, -- 2 additional for the character 'BA'
1,'-IN') -- Add 'IN'
What if I have more than 6 digit number before first hyphen and want to remove the leading zeros to make it 6 digits.
DECLARE #String VARCHAR(100) = '0000098-45.3A-22'
SELECT CASE WHEN CHARINDEX('-',#String) <= 6
THEN ISNULL(REPLICATE('0',6 - CHARINDEX('-',#String)),'') -- Add leading Zeros
+ STUFF(
STUFF( #String,CHARINDEX('-',#String),1,'-BA'), -- Add 'BA'
CHARINDEX('-',#String,CHARINDEX('-',#String)+1)+2, -- 2 additional for the character 'BA'
1,'-IN') -- Add 'IN'
ELSE STUFF(
STUFF(
STUFF(#String,CHARINDEX('-',#String),1,'-BA'), -- Add 'BA'
CHARINDEX('-',#String,CHARINDEX('-',#String)+1)+2, -- 2 additional for the character 'BA'
1,'-IN'), -- Add 'IN'
1, CHARINDEX('-',#String) - 6, '' -- remove extra leading Zeros
)
END
Making assumptions that the format is consistent (e.g. always ends with "-" + 2 characters....)
DECLARE #Data TABLE (Col1 VARCHAR(100))
INSERT #Data ( Col1 )
SELECT Col1
FROM (
VALUES ('98-45.3A-22'), ('104-44.0A-23'),
('00983-29.1-22'), ('01757-42.5A-22'),
('04968-37.3A2-23')
) x (Col1)
SELECT RIGHT('0000' + LEFT(Col1, CHARINDEX('-', Col1) - 1), 5)
+ '-BA' + SUBSTRING(Col1, CHARINDEX('-', Col1) + 1, CHARINDEX('.', Col1) - CHARINDEX('-', Col1))
+ SUBSTRING(Col1, CHARINDEX('.', Col1) + 1, LEN(Col1) - CHARINDEX('.', Col1) - 3)
+ '-IN-' + RIGHT(Col1, 2)
FROM #Data
It's not ideal IMO to do this string manipulation all the time in SQL. You could shift it out to your presentation layer, or store the pre-formatted value in the db to save the cost of this every time.
Use REPLICATE AND CHARINDEX:
Replicate: will repeat given character till reach required count specify in function
CharIndex: Finds the first occurrence of any character
Declare #Data AS VARCHAR(50)='98-45.3A-22'
SELECT REPLICATE('0',6-CHARINDEX('-',#Data)) + #Data
SELECT
SUBSTRING
(
(REPLICATE('0',6-CHARINDEX('-',#Data)) +#Data)
,0
,6
)
+'-'+'BA'+ CAST('<x>' + REPLACE(#Data,'-','</x><x>') + '</x>' AS XML).value('/x[2]','varchar(max)')
+'-'+ 'IN'+ '-' + CAST('<x>' + REPLACE(#Data,'-','</x><x>') + '</x>' AS XML).value('/x[3]','varchar(max)')
In another way by using PARSENAME() you can use this query:
WITH t AS (
SELECT
PARSENAME(REPLACE(REPLACE(s, '.', '###'), '-', '.'), 3) AS p1,
REPLACE(PARSENAME(REPLACE(REPLACE(s, '.', '###'), '-', '.'), 2), '###', '.') AS p2,
PARSENAME(REPLACE(REPLACE(s, '.', '###'), '-', '.'), 1) AS p3
FROM yourTable)
SELECT RIGHT('00000' + p1, 5) + '-BA' + p2 + '-IN-' + p3
FROM t;

Parsing expression only with sql \ finding an order of expression's values

Is there a way to find an order of words/letters inside an expression found in the database?
To be more clear here is an example:
From table X i'm getting the Names: "a" and "b".
In other table there is the expression: "b + a",
The result I need is b,1 | a,2
Is there any way to do it using only SQL query?
P.S. I didn't find any reference to this subject...
Beautiful question! Take a look at this solution wchich breaks expression into list of identifiers:
DECLARE #val varchar(MAX) = 'b * (c + a) / (b - c)';
WITH Split AS
(
SELECT 1 RowNumber, LEFT(#val, PATINDEX('%[^a-z]%', #val)-1) Val, STUFF(#val, 1, PATINDEX('%[^a-z]%', #val), '')+'$' Rest
UNION ALL
SELECT RowNumber+1 Rownumber, LEFT(Rest, PATINDEX('%[^a-z]%', Rest)-1) Val, STUFF(Rest, 1, PATINDEX('%[^a-z]%', Rest), '') Rest
FROM Split
WHERE PATINDEX('%[^a-z]%', Rest)<>0
)
SELECT Val, ROW_NUMBER() OVER (ORDER BY MIN(RowNumber)) RowNumber FROM Split
WHERE LEN(Val)<>0
GROUP BY Val
It yields following results (only first occurences):
b 1
c 2
a 3
If executed with DECLARE #val varchar(MAX) = 'as * (c + a) / (bike - car)' returns:
as 1
c 2
a 3
bike 4
car 5
(From an similar question)
You can do it with CHARINDEX() that searches for a substring within a larger string, and returns the position of the match, or 0 if no match is found.
CHARINDEX(' a ',' ' + REPLACE(REPLACE(#mainString,'+',' '),'.',' ') + ' ')
Add more recursive REPLACE() calls for any other punctuation that may occur
For your question here is an example:
INSERT INTO t1 ([name], [index])
SELECT name, CHARINDEX(' ' + name + ' ',' ' + REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE('b * (c + a) / (b - c)','+',' '),'-',' '),'*',' '),'(',' '),')',' '),'/',' ') + ' ')
FROM t2
The result will be:
a, 10
b, 1
c, 6

SQL Sorting numeric and string

Hi I have interesting problem, I have about 1500 records within a table. the format of column I need to sort against is
String Number.number.(optional number) (optional string)
In reality this could look like this:
AB 2.10.19
AB 2.10.2
AB 2.10.20 (I)
ACA 1.1
ACA 1.9 (a) V
I need a way to sort these so that instead of
AB 2.10.19
AB 2.10.2
AB 2.10.20 (I)
I get this
AB 2.10.2
AB 2.10.19
AB 2.10.20 (I)
Because of the lack of standard formatting I'm at a loss as to how I can sort this via SQL.
I'm at the point of just manually identifying a new int column to denote the sorting value, unless anyone has any suggestion?
I'm using SQL Server 2008 R2
You would need to sort on the first text token, then on the second text token (which is not a number, its a string comprising some numbers) then optionally on any remaining text.
To make the 2nd token sort correctly (like a version number I presume) you can use a hierarchyid:
with t(f) as (
select 'AB 2.10.19' union all
select 'AB 2.10.2' union all
select 'AB 2.10.20 (I)' union all
select 'AB 2.10.20 (a) Z' union all
select 'AB 2.10.21 (a)' union all
select 'ACA 1.1' union all
select 'ACA 1.9 (a) V' union all
select 'AB 4.1'
)
select * from t
order by
left(f, charindex(' ', f) - 1),
cast('/' + replace(substring(f, charindex(' ', f) + 1, patindex('%[0-9] %', f + ' ') - charindex(' ', f)) , '.', '/') + '/' as hierarchyid),
substring(f, patindex('%[0-9] %', f + ' ') + 1, len(f))
f
----------------
AB 2.10.2
AB 2.10.19
AB 2.10.20 (a) Z
AB 2.10.20 (I)
AB 2.10.21 (a)
AB 4.1
ACA 1.1
ACA 1.9 (a) V
add text for the same length
SELECT column
FROM table
ORDER BY left(column + replicate('*', 100500), 100500)
--get the start and end position of numeric in the string
with numformat as
(select val,patindex('%[0-9]%',val) strtnum,len(val)-patindex('%[0-9]%',reverse(val))+1 endnum
from t
where patindex('%[0-9]%',val) > 0) --where condition added to exclude records with no numeric part in them
--get the substring based on the previously calculated start and end positions
,substrng_to_sort_on as
(select val, substring(val,strtnum,endnum-strtnum+1) as sub from numformat)
--Final query to sort based on the 1st,2nd and the optional 3rd numbers in the string
select val
from substrng_to_sort_on
order by
cast(substring(sub,1,charindex('.',sub)-1) as numeric), --1st number in the string
cast(substring(sub,charindex('.',sub)+1,charindex('.',reverse(sub))) as numeric), --second number in the string
cast(reverse(substring(reverse(sub),1,charindex('.',reverse(sub))-1)) as numeric) --third number in the string
Sample demo
Try this:
SELECT column
FROM table
ORDER BY CASE WHEN SUBSTRING(column,LEN(column)-1,1) = '.'
THEN 0
ELSE 1
END, column
This will put any strings that have a . in the second to last position first in the ordering.
Edit:
On second thought, this won't work with the leading 'AB', 'ACA' etc. Try this instead:
SELECT column
FROM table
ORDER BY SUBSTRING(column,1,2), --This will deal with leading letters up to 2 chars
CASE WHEN SUBSTRING(column,LEN(column)-1,1) = '.'
THEN 0
ELSE 1
END,
Column
Edit2:
To also compensate for the second numeric set, use this:
SELECT column
FROM table
ORDER BY substring(column,1,2),
CASE WHEN substring(column,charindex('.',column) + 2,1) = '.' and substring(column,len(column)-1,1) = '.' THEN 0
WHEN substring(column,charindex('.',column) + 2,1) = '.' and substring(column,len(column)-1,1) <> '.' THEN 1
WHEN substring(column,charindex('.',column) + 2,1) <> '.' and substring(column,len(column)-1,1) = '.' THEN 2
ELSE 3 END, column
Basically, this is a manual way to force hierarchical ordering by accounting for each condition.

Extract last number out of existing one

I have to extract the next number out of given numbers. My table contains numbers like below. The main product is always with .1 at the end and could or not contains his subproducts e.g:
07.0001.1 (main product)
07.0001.2 (his sub)
07.0001.3 (his sub)
etc..
01.1453.1
01.1453.2
03.3456.1
03.3456.2
03.3456.3
03.5436.1
03.5436.2
03.5436.3
03.5436.4
12.7839.1
12.7839.2
12.3232.1
12.4444.1
12.4444.2
13.7676.1
i want to pass first to digits of a number to the query and based on that get all which starts with that and then get the highest number out of next four and return this number + 1.
So if we would take above example inputs if i say 12 then it should find this product: 12.7839.x and return 12.7839 + 1 so 12.7840
Another example if i say 03 then should find 03.5436 so 03.5436 + 1 so should return 03.5437
Hope you know what i mean.
I am not so familiar with SQL but this is how far i am:
select * from tbArtikel where Nummer LIKE '12.%'
This is another alternate for achieving the desired results. Providing the option to pass number to be queried. Consider following SQL statements
CREATE TABLE tblDummyExample
(
Number VARCHAR(64)
)
INSERT INTO tblDummyExample
VALUES ('07.0001.1')
, ('07.0001.2')
, ('07.0001.3')
, ('01.1453.1')
, ('01.1453.2')
, ('03.3456.1')
, ('03.3456.2')
, ('03.3456.3')
, ('03.5436.1')
, ('03.5436.2')
, ('03.5436.3')
, ('03.5436.4')
, ('12.7839.1')
, ('12.7839.2')
, ('12.3232.1')
, ('12.4444.1')
, ('12.4444.2')
, ('13.7676.1')
DECLARE #startWith VARCHAR(2) = '12' -- provide any number as input
SELECT #startWith + '.'+ CAST((MAX(CAST(SUBSTRING(ex.Number, (CHARINDEX('.', ex.Number, 1) + 1), (CHARINDEX('.', ex.Number, (CHARINDEX('.', ex.Number, 1) + 1)) - (CHARINDEX('.', ex.Number, 1) + 1))) AS INT)) + 1) AS VARCHAR(16))
FROM tblDummyExample ex
WHERE ex.Number LIKE #startWith+'%'
I'm sure, this solution is not restricted to any specific SQL Server version.
Try this, extract the first two parts, convert the 2nd to a numeric value, add one and convert back to a string again:
select
parsename(max(nummer), 3) + '.' -- 03
+ ltrim(max(cast(parsename(nummer, 2) as int) +1)) -- 5436 -> 5437
+ '.1'
from tbArtikel
where Nummer LIKE '03.%'
Try like this,
DECLARE #table TABLE (col VARCHAR(10))
INSERT INTO #table
VALUES ('01.1453.1')
,('01.1453.2')
,('03.3456.1')
,('03.3456.2')
,('03.3456.3')
,('03.5436.1')
,('03.5436.2')
,('03.5436.3')
,('03.5436.4')
,('12.7839.1')
,('12.7839.2')
,('12.3232.1')
,('12.4444.1')
,('12.4444.2')
,('13.7676.1')
SELECT TOP 1 left(col, charindex('.', col, 1) - 1) + '.' + convert(VARCHAR(10), convert(INT, substring(col, charindex('.', col, 1) + 1, charindex('.', col, charindex('.', col, 1) + 1) - (charindex('.', col, 1) + 1))) + 1)
FROM #table
WHERE col LIKE '03.%'
ORDER BY 1 DESC

Convert varchar to 3 (sometimes 4) chars in T-SQL

I select data from a database. The values are (field name is ADR_KOMP_VL) :
4 , 61A, 100, 12, 58, 123C, 6 A, 5
I need to convert these values to 3 digits (except when there is a letter then it is 4)
So the converted values should be:
004, 061A, 100, 012, 058, 123C, 006A, 005
The rules are:
Always 3 digits
No spaces
If the original value is less than three digits, put 0's in front of it.(The length is 3)
If the original value contains a letter, put 0's in front of it (but the length is 4)
For the "no space" part I have this:
select REPLACE(ADR_KOMP_VL, ' ','')
The solution I have so far is:
SELECT RIGHT('000' + CONVERT(VARCHAR(4),REPLACE(ADR_KOMP_VL, ' ','')), 3)
But this only gives me the right length, when there is no letter in the value. My problem is how to handle the values with a letter in them??
This only check if the last character is letter. Additional logic will be required if that's not the case
SELECT REPLICATE('0', CASE WHEN ISNUMERIC(RIGHT(ADR_KOMP_VL, 1)) = 0 THEN 4
ELSE 3
END - LEN(REPLACE(ADR_KOMP_VL, ' ', '')))
+ REPLACE(ADR_KOMP_VL, ' ', '')
FROM TX
EDIT - actually this might work better, checks for whole ADR_KOMP_VL if it's numeric:
SELECT REPLICATE('0', CASE WHEN ISNUMERIC(REPLACE(ADR_KOMP_VL, ' ', '')) = 0 THEN 4
ELSE 3
END - LEN(REPLACE(ADR_KOMP_VL, ' ', '')))
+ REPLACE(ADR_KOMP_VL, ' ', '')
FROM TX
SQLFiddle DEMO
You can use a case statement:
SELECT (case when ADR_KOMP_VL like '%[A-Z]%'
then RIGHT('0000' + CONVERT(VARCHAR(4),REPLACE(ADR_KOMP_VL, ' ','')), 4)
else RIGHT('000' + CONVERT(VARCHAR(4),REPLACE(ADR_KOMP_VL, ' ','')), 3)
end)