I have an SQL Server 2008 database with a table that has a column Data that contains values like this:
Hello\How\Are\You"
Each string in that column has 4 blocks (ie. 3 slash bars)
Question:
How can I extract, using TSQL, the 3rd value (in this case Are) from that column ?
Note that I just care about the 3rd value.
You can use parsename and
replace.
declare #S varchar(30) = 'Hello\How\Are\You'
select parsename(replace(#S, '\', '.'), 2)
if your data is of a more tricky nature, this is one way of doing it:
declare #t1 table(a nvarchar(500))
insert #t1 values('hello/how/are/you')
select left(t2.b, charindex('/', t2.b + '/') - 1)
from #t1 t1
cross apply (select stuff(a,1, charindex('/',a, charindex('/',a) + 1), '') b) t2
Related
lets say i have a "varchar" variable which contains "ascii" code separating each other by a ',' and i want to convert it to character and insert it into a column. Is there any way that i can do this? I am new in mysql so i was wondering if someone can help.
Example: lets say we are inside a trigger or procedure.
declare test varchar(10);
set test = "73,116";
now i want to convert it into "it" and store it in a column of a table which is varchar as well.
Help me with this please.
Iteratively parsing a string is not an easy task for a set-based language such as SQL.
One option uses a recursive query (available in MySQL 8.0):
set #test = '73,116';
with recursive cte as (
select
1 n,
0 + substring(#test, 1, locate(',', #test) - 1) part,
concat(substring(#test, locate(',', #test) + 1), ',') rest
union all
select
n + 1,
substring(rest, 1, locate(',', rest) - 1),
substring(rest, locate(',', rest) + 1)
from cte
where locate(',', rest) > 0
)
select group_concat(char(part using utf8) order by n separator '') res
from cte;
The recursive query extracts each csv part sequentially, while keeping track of the position. Then, the outer query converts each ASCII code the corresponding character, and re-aggregates the results into a string.
Demo on DB Fiddle:
| res |
| --- |
| It |
I know that there are several ways to find which row's column contains a string, like using [column name] regexp ' ' or [column name] like ' '
while currently what I need some help is I have a table with several columns, all of there are varchar or text and I am not sure which column contains a certain string. Just say that I want to search a "xxx from a table. Several different columns could contain this string or not. Is there a way that I could find which column contains this string?
I have a thinking and the solution could be
select * from [table name] where [column1] regexp 'xxx' or
[column2] regexp 'xxx' or ...... [column39] regexp 'xxx' or .....
[colum60] regexp 'xxx' or ... or [column 80] regexp 'xxx';
I do not want the query like this. Is there another effective way?
To give a better example, say that we are searching for a table that belongs to a blog.
We have title, URL, content, key words, tag, comment and so on. Now we just say, if any blog article is related to "database-normalization", this word may appear in the title, URL or content or anywhere, and I do not want to write it one by one like
where title regexp 'database-normalization' or content regexp 'database-normalization' or url regexp 'database-normalization'......
as when there are hundreds columns, I need to write a hundred, or in this case is there an effective way instead of write hundred or statement? Like using if-else or collections or some others to build the query.
If you want a pure dynamic way, you can try this. I've tried it long back on sql-server and hope it may help you.
#TMP_TABLE -- a temporary table
- PK, IDENTITY
- TABLE_NAME
- COLUMN_NAME
- IS_EXIST
INSERT INTO #TMP_TABLE (TABLE_NAME,COLUMN_NAME)
SELECT C.TABLE_NAME, COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS C
WHERE C.TABLE_NAME = <your-table> AND C.DATA_TYPE = 'varchar'; -- you can modify it to handle multiple table at once.
-- boundaries
SET #MINID = (SELECT ISNULL(MIN(<PK>),0) FROM #TMP_TABLE );
SET #MAXID = (SELECT ISNULL(MAX(<PK>),0) FROM #TMP_TABLE );
WHILE ((#MINID<=#MAXID) AND (#MINID<>0))
BEGIN
SELECT #TABLE_NAME = TABLE_NAME,#COLUMN_NAME = COLUMN_NAME
FROM #TMP_TABLE
WHERE <PK> = #MINID;
SET #sqlString = ' UPDATE #TMP_TABLE
SET IS_EXIST = 1
WHERE EXIST (SELECT 1 FROM '+ #TABLE_NAME+' WHERE '+ #COLUMN_NAME +' = ''demo.webstater.com'') AND <PK> = '+ #MINID;
EXEC(#sql) ;
SET #MINID = (SELECT MIN(<PK>) FROM #TMP_TABLE WHERE <PK> > #MINID );
END
SELECT * FROM #TMP_TABLE WHERE IS_EXIST = 1 ; -- will give you matched results.
If you know the columns in advance, what you proposed is probably the most effective way (if a little verbose).
Otherwise, you could get the column names from INFORMATION_SCHEMA.COLUMNS and construct dynamic SQL based on that.
His question is not to query specific columns with like clause. He has been asking to apply same pattern across columns dynamically.
Example: Table having 3 columns - FirstName, LastName, Address and pattern matching is "starts with A" then resulting query should be:
Select * From Customer where FirstName like 'A%" or LastName like 'A%" or Address like 'A%'
If you want to build query in business layer, this could easily be done with reflection along with EF.
If you are motivated to do in database then you can achieve by building query dynamically and then execute through sp_executesql.
Try this (Just pass tablename and the string to be find)-:
create proc usp_findString
#tablename varchar(500),
#string varchar(max)
as
Begin
Declare #sql2 varchar(max),#sql nvarchar(max)
SELECT #sql2=
STUFF((SELECT ', case when '+QUOTENAME(NAME)+'='''+#string+''' then 1 else 0 end as '+NAME
FROM (select a.name from sys.columns a join sys.tables b on a.[object_id]=b.[object_id] where b.name=#tablename) T1
--WHERE T1.ID=T2.ID
FOR XML PATH('')),1,1,'')
--print #string
set #sql='select '+#sql2+' from '+#tablename
print #sql
EXEC sp_executesql #sql
End
SQL Server 2014
One way is to use CASE to check the substring existence with LOCATE in mysql and return the column but all you have to check in every column of the table as below:
CREATE TABLE test(col1 VARCHAR(1000), col2 VARCHAR(1000), col3 VARCHAR(1000))
INSERT INTO test VALUES
('while currently what I need some help is I have a table with 10 columns',
'contains a certain string. Just say that I want to search a table',
'contains a certain string demo.webstater.com')
SELECT (CASE WHEN LOCATE('demo.webstater.com', col1, 1) > 0 THEN 'col1'
WHEN LOCATE('demo.webstater.com', col2, 1) > 0 THEN 'col2'
WHEN LOCATE('demo.webstater.com', col3, 1) > 0 THEN 'col3'
END) whichColumn
FROM test
OUTPUT:
whichColumn
col3
There are many ways in which you can do your analysis. You can use "LIKE A%%" if it starts from A in SQL, "REGEX" LibrarY for multiple checks.
I have a column with few different ID's
abc_1234
abc_2345
bcd_3456/
cde_4567/
And I want a new column that takes off the / if it exists
abc_1234
abc_2345
bcd_3456
cde_4567
I know I'll be using a combination of IF/THEN, LEFT, and LEN, but I don't know the syntax. Help is appreciated! Thanks!
(In case your are using SQL Server RDBMS)
You can try the following combination of right and left:
case when right(col, 1) = '/' then left(col, len(col)-1) else col end
SQLFiddle
(In case your are using MySQL RDBMS)
trim(trailing '/' from col);
SQLFiddle
If your using SQL Server try this
SELECT REPLACE(col,'/','')
Replace (Transact-SQL)
You would usually simply trim the string:
Oracle: select rtrim(col, '/') from ...
MySQL and PostgreSQL: select trim(trailing '/' from col) from ...
but not all DBMS offer this. SQL Server for instance doesn't.
For SQLServer
declare #tbl table
(
c1 nvarchar(9)
)
insert into #tbl values ('abc_1234')
insert into #tbl values ('abc_2345')
insert into #tbl values ('abc_3456/')
insert into #tbl values ('abc_4567/')
select IIF(RIGHT(c1, 1) = '/', LEFT(C1, LEN(C1)-1), C1)
from #tbl
Result
abc_1234
abc_2345
abc_3456
abc_4567
How to split a string in SQL Server.
Example:
Input string: stack over flow
Result:
stack
over
flow
if you can't use table value parameters, see: "Arrays and Lists in SQL Server 2008 Using Table-Valued Parameters" by Erland Sommarskog , then there are many ways to split string in SQL Server. This article covers the PROs and CONs of just about every method:
"Arrays and Lists in SQL Server 2005 and Beyond, When Table Value Parameters Do Not Cut it" by Erland Sommarskog
You need to create a split function. This is how a split function can be used:
SELECT
*
FROM YourTable y
INNER JOIN dbo.yourSplitFunction(#Parameter) s ON y.ID=s.Value
I prefer the number table approach to split a string in TSQL but there are numerous ways to split strings in SQL Server, see the previous link, which explains the PROs and CONs of each.
For the Numbers Table method to work, you need to do this one time table setup, which will create a table Numbers that contains rows from 1 to 10,000:
SELECT TOP 10000 IDENTITY(int,1,1) AS Number
INTO Numbers
FROM sys.objects s1
CROSS JOIN sys.objects s2
ALTER TABLE Numbers ADD CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number)
Once the Numbers table is set up, create this split function:
CREATE FUNCTION [dbo].[FN_ListToTable]
(
#SplitOn char(1) --REQUIRED, the character to split the #List string on
,#List varchar(8000)--REQUIRED, the list to split apart
)
RETURNS TABLE
AS
RETURN
(
----------------
--SINGLE QUERY-- --this will not return empty rows
----------------
SELECT
ListValue
FROM (SELECT
LTRIM(RTRIM(SUBSTRING(List2, number+1, CHARINDEX(#SplitOn, List2, number+1)-number - 1))) AS ListValue
FROM (
SELECT #SplitOn + #List + #SplitOn AS List2
) AS dt
INNER JOIN Numbers n ON n.Number < LEN(dt.List2)
WHERE SUBSTRING(List2, number, 1) = #SplitOn
) dt2
WHERE ListValue IS NOT NULL AND ListValue!=''
);
GO
You can now easily split a CSV string into a table and join on it:
select * from dbo.FN_ListToTable(' ','stack over flow')
OUTPUT:
ListValue
-------------------
stack
over
flow
(3 row(s) affected)
A common set-based solution to this kind of problem is to use a numbers table.
The following solution uses a simple recursive CTE to generate the numbers table on the fly - if you need to work with longer strings, this should be replaced with a static numbers table.
DECLARE #vch_string varchar(max)
DECLARE #chr_delim char(1)
SET #chr_delim = ' '
SET #vch_string = 'stack over flow'
;WITH nums_cte
AS
(
SELECT 1 AS n
UNION ALL
SELECT n+1 FROM nums_cte
WHERE n < len(#vch_string)
)
SELECT n - LEN(REPLACE(LEFT(s,n),#chr_delim,'')) + 1 AS pos
,SUBSTRING(s,n,CHARINDEX(#chr_delim, s + #chr_delim,n) -n) as ELEMENT
FROM (SELECT #vch_string as s) AS D
JOIN nums_cte
ON n <= LEN(s)
AND SUBSTRING(#chr_delim + s,n,1) = #chr_delim
OPTION (MAXRECURSION 0);
I know this question was for SQL Server 2008 but things evolve so starting with SQL Server 2016 you can do this
DECLARE #string varchar(100) = 'Richard, Mike, Mark'
SELECT value FROM string_split(#string, ',')
CREATE FUNCTION [dbo].[Split]
(
#List varchar(max),
#SplitOn nvarchar(5)
)
RETURNS #RtnValue table
(
Id int identity(1,1),
Value nvarchar(max)
)
AS
BEGIN
While (Charindex(#SplitOn,#List)>0)
Begin
Insert Into #RtnValue (value)
Select
Value = ltrim(rtrim(Substring(#List,1,Charindex(#SplitOn,#List)-1)))
Set #List = Substring(#List,Charindex(#SplitOn,#List)+len(#SplitOn),len(#List))
End
Insert Into #RtnValue (Value)
Select Value = ltrim(rtrim(#List))
Return
END
Create Above Function And Execute Belowe Query To Get Your Result.
Select * From Dbo.Split('Stack Over Flow',' ')
Suggestion : use delimiter for get split value. it's better. (for ex. 'Stack,Over,Flow')
Hard. Really hard - Strin Manipulation and SQL... BAD combination. C# / .NET for a stored procedure is a way, could return a table defined type (table) with one item per row.
How to split a string in SQL Server.
Example:
Input string: stack over flow
Result:
stack
over
flow
if you can't use table value parameters, see: "Arrays and Lists in SQL Server 2008 Using Table-Valued Parameters" by Erland Sommarskog , then there are many ways to split string in SQL Server. This article covers the PROs and CONs of just about every method:
"Arrays and Lists in SQL Server 2005 and Beyond, When Table Value Parameters Do Not Cut it" by Erland Sommarskog
You need to create a split function. This is how a split function can be used:
SELECT
*
FROM YourTable y
INNER JOIN dbo.yourSplitFunction(#Parameter) s ON y.ID=s.Value
I prefer the number table approach to split a string in TSQL but there are numerous ways to split strings in SQL Server, see the previous link, which explains the PROs and CONs of each.
For the Numbers Table method to work, you need to do this one time table setup, which will create a table Numbers that contains rows from 1 to 10,000:
SELECT TOP 10000 IDENTITY(int,1,1) AS Number
INTO Numbers
FROM sys.objects s1
CROSS JOIN sys.objects s2
ALTER TABLE Numbers ADD CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number)
Once the Numbers table is set up, create this split function:
CREATE FUNCTION [dbo].[FN_ListToTable]
(
#SplitOn char(1) --REQUIRED, the character to split the #List string on
,#List varchar(8000)--REQUIRED, the list to split apart
)
RETURNS TABLE
AS
RETURN
(
----------------
--SINGLE QUERY-- --this will not return empty rows
----------------
SELECT
ListValue
FROM (SELECT
LTRIM(RTRIM(SUBSTRING(List2, number+1, CHARINDEX(#SplitOn, List2, number+1)-number - 1))) AS ListValue
FROM (
SELECT #SplitOn + #List + #SplitOn AS List2
) AS dt
INNER JOIN Numbers n ON n.Number < LEN(dt.List2)
WHERE SUBSTRING(List2, number, 1) = #SplitOn
) dt2
WHERE ListValue IS NOT NULL AND ListValue!=''
);
GO
You can now easily split a CSV string into a table and join on it:
select * from dbo.FN_ListToTable(' ','stack over flow')
OUTPUT:
ListValue
-------------------
stack
over
flow
(3 row(s) affected)
A common set-based solution to this kind of problem is to use a numbers table.
The following solution uses a simple recursive CTE to generate the numbers table on the fly - if you need to work with longer strings, this should be replaced with a static numbers table.
DECLARE #vch_string varchar(max)
DECLARE #chr_delim char(1)
SET #chr_delim = ' '
SET #vch_string = 'stack over flow'
;WITH nums_cte
AS
(
SELECT 1 AS n
UNION ALL
SELECT n+1 FROM nums_cte
WHERE n < len(#vch_string)
)
SELECT n - LEN(REPLACE(LEFT(s,n),#chr_delim,'')) + 1 AS pos
,SUBSTRING(s,n,CHARINDEX(#chr_delim, s + #chr_delim,n) -n) as ELEMENT
FROM (SELECT #vch_string as s) AS D
JOIN nums_cte
ON n <= LEN(s)
AND SUBSTRING(#chr_delim + s,n,1) = #chr_delim
OPTION (MAXRECURSION 0);
I know this question was for SQL Server 2008 but things evolve so starting with SQL Server 2016 you can do this
DECLARE #string varchar(100) = 'Richard, Mike, Mark'
SELECT value FROM string_split(#string, ',')
CREATE FUNCTION [dbo].[Split]
(
#List varchar(max),
#SplitOn nvarchar(5)
)
RETURNS #RtnValue table
(
Id int identity(1,1),
Value nvarchar(max)
)
AS
BEGIN
While (Charindex(#SplitOn,#List)>0)
Begin
Insert Into #RtnValue (value)
Select
Value = ltrim(rtrim(Substring(#List,1,Charindex(#SplitOn,#List)-1)))
Set #List = Substring(#List,Charindex(#SplitOn,#List)+len(#SplitOn),len(#List))
End
Insert Into #RtnValue (Value)
Select Value = ltrim(rtrim(#List))
Return
END
Create Above Function And Execute Belowe Query To Get Your Result.
Select * From Dbo.Split('Stack Over Flow',' ')
Suggestion : use delimiter for get split value. it's better. (for ex. 'Stack,Over,Flow')
Hard. Really hard - Strin Manipulation and SQL... BAD combination. C# / .NET for a stored procedure is a way, could return a table defined type (table) with one item per row.