I have a date which look like this "Corporate Services\Corporate Affairs & Communications(DP19)"
I want to the result to be like this:
Column A
Column B
Corporate Service
Corporate Affairs & Communications (DP19)
I already tried using substring but no luck,
I am using Microsoft SQL
DECLARE #AData VARCHAR(1000) = 'Corporate Services\Corporate Affairs & Communications(DP19)';
SELECT
LEFT(#AData, CHARINDEX('\', #AData) - 1) AS [Column A],
SUBSTRING(#AData, CHARINDEX('\', #AData) + 1, LEN(#AData)) AS [Column B];
I kind of cheated with using LEN(#AData) for the Length parameter of the SUBSTRING() function used in Column B, but SUBSTRING() doesn't care, so no harm no foul:
length
...If the sum of start and length is greater than the number of characters in expression, the whole value expression beginning at start is returned.
you can use PARSENAME
declare #t table(ch varchar(max))
insert into #t values
(N'Corporate Services\Corporate Affairs & Communications(DP19)');
SELECT
PARSENAME(REPLACE(ch,'\','.'),2) AS N'Column A' ,
PARSENAME(REPLACE(ch,'\','.'),1) AS N'Column B'
FROM #t
Related
I am working towards migrating an access Right() function onto SQL Server.
This is the Access Right() function:
Barcode: "ID" & (Right(String(8,"0") & [Job Number],8) & Right(String(6,"0") &
[PART LIBARY HEADER_1]![PartID],6) & Right(String(6,"0") & [Part Assembly Link
Table]![AssemblyLinkID],6))
What the above expression does, is that it dynamically creates a field to house a barcode that is always 22 characters in length. Now the values of these barcodes are determined by other columns.
I am using the Right() method to gather the Rightmost: 8 chars of Job Number, 6 chars of PartID and 6 chars of AssemblyLinkID.
Now the problem I face is that whilst I can move these values onto T-SQL, I having difficulty setting the default value to be = 0.
For example the following input:
Job Number: 123456
PartID: 9876
AssemblyLinkID: 127
Would need to return… ID00123456009876000127
The String() function in access is handling the null values for me by appending with 0 but this method is not something available in SQL Server.
String(8,"0")
This is my SQL Code so far:
Concat('ID', RIGHT(STR(0, 8) & [Job Number],8)) AS Barcode,
STR() is not the function I am looking to use as it is not giving the same results as MS Access's String().
This does the job you want:
declare #t table ([Job NUmber] int, PartID int, AssemblyLinkID int)
insert into #t ([Job NUmber], PartID, AssemblyLinkID) values(123456,9876,127)
select
'ID' +
RIGHT('00000000' + CONVERT(varchar(8),[Job Number]),8) +
RIGHT('000000' + CONVERT(varchar(6),PartID),6) +
RIGHT('000000' + CONVERT(varchar(6),AssemblyLinkID),6)
from #t
You can use REPLICATE as an exact analogue for your STRING calls but since the string literals are shorter than the method call, I prefer them.
Not sure quite what your point was around nulls, but if any of these columns may be NULL, then you may way to use COALESCE as well:
declare #t table ([Job NUmber] int, PartID int, AssemblyLinkID int)
insert into #t ([Job NUmber], PartID, AssemblyLinkID) values(123456,9876,127)
select
'ID' +
RIGHT('00000000' + COALESCE(CONVERT(varchar(8),[Job Number]),''),8) +
RIGHT('000000' + COALESCE(CONVERT(varchar(6),PartID),''),6) +
RIGHT('000000' + COALESCE(CONVERT(varchar(6),AssemblyLinkID),''),6)
from #t
(Here we'll get all zeros for a NULL value. If you want something different, substitute that into the empty string towards the end of each expression)
Right(String(8,"0") & [Job Number],8) =
RIGHT(REPLICATE('0', 8) + ISNULL([Job Number], ''), 8)
I have a column with the name of a person in the following format: "LAST NAME, FIRST NAME"
Only Upper Cases Allowed
Space after comma optional
I would like to use a regular expression like: [A-Z]+,[ ]?[A-Z]+ but I do not know how to do this in T-SQL. In Oracle, I would use REGEXP_LIKE, is there something similar for SQL Server 2016?
I need something like the following:
UPDATE table
SET is_correct_format = 'YES'
WHERE REGEXP_LIKE(table.name,'[A-Z]+,[ ]?[A-Z]+');
First, case sensitivity depends on the collation of the DB, though with LIKE you can specify case comparisons. With that... here is some Boolean logic to take care of the cases you stated. Though, you may need to add additional clauses if you discover some bogus input.
declare #table table (Person varchar(64), is_correct_format varchar(3) default 'NO')
insert into #table (Person)
values
('LowerCase, Here'),
('CORRECTLY, FORMATTED'),
('CORRECTLY,FORMATTEDTWO'),
('ONLY FIRST UPPER, LowerLast'),
('WEGOT, FormaNUMB3RStted'),
('NoComma Formatted'),
('CORRECTLY, TWOCOMMA, A'),
(',COMMA FIRST'),
('COMMA LAST,'),
('SPACE BEFORE COMMA , GOOD'),
(' SPACE AT BEGINNING, GOOD')
update #table
set is_correct_format = 'YES'
where
Person not like '%[^A-Z, ]%' --check for non characters, excluding comma and spaces
and len(replace(Person,' ','')) = len(replace(replace(Person,' ',''),',','')) + 1 --make sure there is only one comma
and charindex(',',Person) <> 1 --make sure the comma isn't at the beginning
and charindex(',',Person) <> len(Person) --make sure the comma isn't at the end
and substring(Person,charindex(',',Person) - 1,1) <> ' ' --make sure there isn't a space before comma
and left(Person,1) <> ' ' --check preceeding spaces
and UPPER(Person) = Person collate Latin1_General_CS_AS --check collation for CI default (only upper cases)
select * from #table
The tsql equivalent could look like this. I'm not vouching for the efficiency of this solution.
declare #table as table(name varchar(20), is_Correct_format varchar(5))
insert into #table(name) Values
('Smith, Jon')
,('se7en, six')
,('Billy bob')
UPDATE #table
SET is_correct_format = 'YES'
WHERE
replace(name, ', ', ',x')
like (replicate('[a-z]', charindex(',', name) - 1)
+ ','
+ replicate('[a-z]', len(name) - charindex(',', name)) )
select * from #table
The optional space is hard to solve, so since it's next to a legal character I'm just replacing with another legal character when it's there.
TSQL does not provide the kind of 'repeating pattern' of * or + in regex, so you have to count the characters and construct the pattern that many times in your search pattern.
I split the string at the comma, counted the alphas before and after, and built a search pattern to match.
Clunky, but doable.
I'm trying to dynamically select tables from my database based on the table name, which in turn is based on the date of creation.
For example, the tables might be called 'tableA20110305', or 'tableB20110305', indicating that the tables were created on 05 March 2011.
I'm trying to write a query that will select all tables named thus, created before a certain cutoff date (1 year ago), and concatenate them into DROP TABLE command statements in a table variable. The select statement looks like this.
DECLARE #cutoffDate datetime = CONVERT(DATETIME, DATEADD(YEAR,-1,GETDATE()), 112)
SELECT 'DROP TABLE "' + TABLE_NAME + '"' AS 'Command'
FROM INFORMATION_SCHEMA.TABLES
WHERE (TABLE_NAME LIKE 'tableA%' OR TABLE_NAME LIKE 'tableB%')
AND (CONVERT(DATETIME, SUBSTRING(TABLE_NAME, 7, 8), 112) < #cutoffDate)
ORDER BY Command DESC
However, when I execute this SQL, I'm seeing the following error:
Msg 241, Level 16, State 1, Line 14
Conversion failed when converting date and/or time from character string.
BUT... if I execute the following SQL statement, I see no error and get date returned as expected:
SELECT CONVERT(DATETIME, SUBSTRING('tableA20110305', 7, 8), 112)
I don't understand why these queries are not returning the same result or where this error is coming from.
I'd very much appreciate any insights..
This explains this behavior very well. Taken from 70-461: Querying Microsoft SQL Server 2012:
WHERE propertytype = 'INT' AND CAST(propertyval AS INT) > 10
Suppose
that the table being queried holds different property values. The
propertytype column represents the type of the property (an INT, a
DATE, and so on), and the propertyval column holds the value in a
character string. When propertytype is 'INT', the value in propertyval
is convertible to INT; otherwise, not necessarily.
Some assume that
unless precedence rules dictate otherwise, predicates will be
evaluated from left to right, and that short circuiting will take
place when possible. In other words, if the first predicate
propertytype = 'INT' evaluates to false, SQL Server won’t evaluate the
second predicate CAST(propertyval AS INT) > 10 because the result is
already known. Based on this assumption, the expectation is that the
query should never fail trying to convert something that isn’t
convertible.
The reality, though, is different. SQL Server does
internally support a short-circuit concept; however, due to the
all-at-once concept in the language, it is not necessarily going to
evaluate the expressions in left-to-right order. It could decide,
based on cost-related reasons, to start with the second expression,
and then if the second expression evaluates to true, to evaluate the
first expression as well. This means that if there are rows in the
table where propertytype is different than 'INT', and in those rows
propertyval isn’t convertible to INT, the query can fail due to a
conversion error.
And in your case engine decides first to do filter by dates part and it fails.
And there can be several workaround:
Use TRY_CAST instead(supported from SQL Server 2012)
First select all tables which are like 'tableA%' OR TABLE_NAME LIKE 'tableB%' into some temp table and then do another filter (CONVERT(DATETIME, SUBSTRING(TABLE_NAME, 7, 8), 112) < #cutoffDate)
Well , as mentioned in the comments you probably have other tables in your database that does not follow the same format as tableA<DateFormat> , so you need to try to filter only them .
You can use ISDATE combined with CASE EXPRESSION to make sure the SUBSTRING is indeed in a date format:
DECLARE #cutoffDate datetime = CONVERT(DATETIME, DATEADD(YEAR,-1,GETDATE()), 112)
SELECT 'DROP TABLE "' + TABLE_NAME + '"' AS 'Command'
FROM INFORMATION_SCHEMA.TABLES
WHERE (TABLE_NAME LIKE 'tableA%' OR TABLE_NAME LIKE 'tableB%')
AND CASE WHEN ISDATE(SUBSTRING(TABLE_NAME, 7, 8)) = 1
THEN (CONVERT(DATETIME, SUBSTRING(TABLE_NAME, 7, 8), 112)
ELSE getdate()
END < #cutoffDate
ORDER BY Command DESC
DECLARE #cutoffDate Varchar(8); --<-- use varchar here not datetime since you YYYYMMDD
SET #cutoffDate = CONVERT(Varchar(8), DATEADD(YEAR,-1,GETDATE()), 112)
SELECT 'DROP TABLE '+ QUOTENAME(TABLE_SCHEMA) +'.' + QUOTENAME(TABLE_NAME) AS [Command]
From (
Select TABLE_SCHEMA , TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE (TABLE_NAME LIKE 'tableA%' OR TABLE_NAME LIKE 'tableB%')
AND ISDATE(SUBSTRING(TABLE_NAME, 7, 8)) = 1
) A
Where (CONVERT(DATETIME, SUBSTRING(TABLE_NAME, 7, 8)) < #cutoffDate)
ORDER BY Command DESC
Adding ISDATE(SUBSTRING(TABLE_NAME, 7, 8)) = 1 to your where clause will only bring back the results which has a proper date value in its name hence converting it to date/datetime should work.
The problem is that some table matches the condition but does not have the prescribed format. In SQL Server 2012+, you can use try_convert():
SELECT 'DROP TABLE "' + TABLE_NAME + '"' AS 'Command'
FROM INFORMATION_SCHEMA.TABLES
WHERE (TABLE_NAME LIKE 'tableA%' OR TABLE_NAME LIKE 'tableB%') AND
(TRY_CONVERT(DATETIME, SUBSTRING(TABLE_NAME, 7, 8), 112) < #cutoffDate)
ORDER BY Command DESC;
In earlier versions, you might as well use string comparisons:
SELECT 'DROP TABLE "' + TABLE_NAME + '"' AS 'Command'
FROM INFORMATION_SCHEMA.TABLES
WHERE (TABLE_NAME LIKE 'tableA%' OR TABLE_NAME LIKE 'tableB%') AND
(SUBSTRING(TABLE_NAME, 7, 8), 112) < CONVERT(VARCHAR(8), #cutoffDate, 112))
ORDER BY Command DESC;
This converts the cutoff date to a string in the format of YYYYMMDD, which is fine for this comparison. However, you do need to be careful about the values that do not match the specific format -- this might accidentally delete a table that you don't intend to delete.
I'm looking for an elegant way to convert a field of type varchar, with variable data in it, to a data type which can be used for mathematical operations sample data from the field
(excluding quotes)
''
'abc'
'23'
'23.2'
The method should work for all, and for the first & second values should return 0, and not throw an SQL Server error..
Try this:
SELECT CASE WHEN IsNumeric(YourColumn) = 0 THEN
0
ELSE
CAST(YourColumn AS decimal(18, 2))
END
You have to adjust the destination data type, I have chosen decimal(18, 2) for demonstration.
I know this is a long-dead thread, but I recently stumbled upon it from a Google search and had a thought. It is less elegant than a CASE statement, but it is an alternative.
SELECT
COALESCE(CAST(NULLIF(ISNUMERIC(LEFT(MyColumn, PATINDEX('% %', MyColumn + ' ') - 1)), 1) AS MONEY), LEFT(MyColumn, PATINDEX('% %', MyColumn + ' ') - 1))
FROM
myTable
or you could do:
Select COALESCE(CAST(NULLIF(ISNUMERIC(MyColumn), 1) AS MONEY), MyColumn)
FROM
myTable
The top version would see "2 5" as just 2, the bottom one would see it as a text field.
SELECT CASE IsNumeric(mycol) WHEN 1 THEN CAST(mycol AS FLOAT) ELSE 0 END
FROM mytable
If you'd like to convert it, you should use UPDATE instead of SELECT
UPDATE Table
SET Col1 = CAST(Col1 As Decimal(18,2))
COALESCE is a great option for this: Find more information here. It evaluates the arguments in order and returns the current value of the first expression that initially does not evaluate to NULL.
ISNUMERIC returns 0 or 1 depending on if the value being evaluated could be considered one of the SQL 'number' or 'numeric' types. e.g. int, bigint, money..
NULLIF essentially finds the value you specify and if it matches it replaces it with a NULL value.
CAST Simply changes a data type to another in this example to MONEY
As you can see, if you break the below down using this information its quite an elegant solution I think?
COALESCE(CAST(NULLIF(ISNUMERIC(COL1), 1) AS MONEY), COL1)
If I have the following nvarchar variable - BTA200, how can I extract just the BTA from it?
Also, if I have varying lengths such as BTA50, BTA030, how can I extract just the numeric part?
I would recommend a combination of PatIndex and Left. Carefully constructed, you can write a query that always works, no matter what your data looks like.
Ex:
Declare #Temp Table(Data VarChar(20))
Insert Into #Temp Values('BTA200')
Insert Into #Temp Values('BTA50')
Insert Into #Temp Values('BTA030')
Insert Into #Temp Values('BTA')
Insert Into #Temp Values('123')
Insert Into #Temp Values('X999')
Select Data, Left(Data, PatIndex('%[0-9]%', Data + '1') - 1)
From #Temp
PatIndex will look for the first character that falls in the range of 0-9, and return it's character position, which you can use with the LEFT function to extract the correct data. Note that PatIndex is actually using Data + '1'. This protects us from data where there are no numbers found. If there are no numbers, PatIndex would return 0. In this case, the LEFT function would error because we are using Left(Data, PatIndex - 1). When PatIndex returns 0, we would end up with Left(Data, -1) which returns an error.
There are still ways this can fail. For a full explanation, I encourage you to read:
Extracting numbers with SQL Server
That article shows how to get numbers out of a string. In your case, you want to get alpha characters instead. However, the process is similar enough that you can probably learn something useful out of it.
substring(field, 1,3) will work on your examples.
select substring(field, 1,3) from table
Also, if the alphabetic part is of variable length, you can do this to extract the alphabetic part:
select substring(field, 1, PATINDEX('%[1234567890]%', field) -1)
from table
where PATINDEX('%[1234567890]%', field) > 0
LEFT ('BTA200', 3) will work for the examples you have given, as in :
SELECT LEFT(MyField, 3)
FROM MyTable
To extract the numeric part, you can use this code
SELECT RIGHT(MyField, LEN(MyField) - 3)
FROM MyTable
WHERE MyField LIKE 'BTA%'
--Only have this test if your data does not always start with BTA.
declare #data as varchar(50)
set #data='ciao335'
--get text
Select Left(#Data, PatIndex('%[0-9]%', #Data + '1') - 1) ---->>ciao
--get numeric
Select right(#Data, len(#data) - (PatIndex('%[0-9]%', #Data )-1) ) ---->>335