sql query complex - sql

I have table where in a table called test which have 4 fields.one field named as listing, I have 1,2,3,4,5,6 multiple values separated by comma, I need to check whether in that table and in that particular field an id say 4 is there or not.. by a sql query.

You database design is wrong, that's why you have problems querying the data. You should have the values in a separate table, so that teach value is in it's own field. Then it would be easy to find the records:
select t.testId
from test t
inner join listing l on l.testId = t.testId
where l.id = 4
Now you have to use some ugly string comparison to find the records:
select testId
from test
where ','+listing+',' like '%,4,%'

You can try
SELECT *
FROM YourTable
WHERE REPLACE(Col, ' ', '') LIKE '4,%' --Starts with
OR REPLACE(Col, ' ', '') LIKE '%,4' --Ends with
OR REPLACE(Col, ' ', '') LIKE '%,4,%' --Contains
OR REPLACE(Col, ' ', '') = '4' --Equals
Just as a matter of interest, have a look at this
DECLARE #delimiter NVARCHAR(5),
#Val INT
SELECT #Val = 40
SELECT #delimiter = ','
DECLARE #YourTable TABLE(
ID INT,
Vals VARCHAR(50)
)
INSERT INTO #YourTable (ID,Vals) SELECT 1, '1,2,3,4,5,6,7,8'
DECLARE #TempTable TABLE(
ID INT,
Vals XML
)
INSERT INTO #TempTable
SELECT ID,
CAST('<d>' + REPLACE(Vals, #delimiter, '</d><d>') + '</d>' AS XML)
FROM #YourTable
SELECT *
FROM #TempTable tt
WHERE EXISTS(
SELECT T.split.value('.', 'nvarchar(max)') AS data
FROM tt.Vals.nodes('/d') T(split)
WHERE T.split.value('.', 'nvarchar(max)') = #Val
)

The common approach is to parse the list into a table variable or table-valued function, then either join against the table, or use an EXISTS sub-query.
There are lots of examples on how to do this:
http://www.bing.com/search?setmkt=en-US&q=SQL+parse+list+into+table

You could use an instring function in the where clause and in the select clause:
Oracle:
select substr(column, instr(column, '1', 1), 1)
where instr(column, '1', 1) > 0
works if you want a single value. Alternatively you can use a combination of case or decode statements to create a single column for each possible value:
select
decode(instr(column, '1', 1), 0, substr(column, instr(column, '1', 1), 1), null) c1,
decode(instr(column, '2', 1), 0, substr(column, instr(column, '2', 1), 1), null) c2,
decode(instr(column, '3', 1), 0, substr(column, instr(column, '3', 1), 1), null) c3
The beauty of this approach for such a poorly normalised set of data is you can save this as a view and then run SQL on that, so if you save the above you could use:
select c1, c2 from view where c1 is not null or c2 is not null
NB. In other dbms you might have to use different syntax, possibly the case rather decode statement

If you need to find 4 and only 4 (ie not 14 or 24 or 40 etc) you should use
SELECT * FROM foo WHERE col LIKE '%, 4,%'
or
SELECT * FROM foo WHERE col LIKE '%,4,%'
if there are no spaces between the commas and numbers

How about this?
Select * From Foo Where Col like '%4%'

Related

Split string and display below other column data using SQL Server [duplicate]

I have a table that looks like this:
ProductId, Color
"1", "red, blue, green"
"2", null
"3", "purple, green"
And I want to expand it to this:
ProductId, Color
1, red
1, blue
1, green
2, null
3, purple
3, green
Whats the easiest way to accomplish this? Is it possible without a loop in a proc?
Take a look at this function. I've done similar tricks to split and transpose data in Oracle. Loop over the data inserting the decoded values into a temp table. The convent thing is that MS will let you do this on the fly, while Oracle requires an explicit temp table.
MS SQL Split Function
Better Split Function
Edit by author:
This worked great. Final code looked like this (after creating the split function):
select pv.productid, colortable.items as color
from product p
cross apply split(p.color, ',') as colortable
based on your tables:
create table test_table
(
ProductId int
,Color varchar(100)
)
insert into test_table values (1, 'red, blue, green')
insert into test_table values (2, null)
insert into test_table values (3, 'purple, green')
create a new table like this:
CREATE TABLE Numbers
(
Number int not null primary key
)
that has rows containing values 1 to 8000 or so.
this will return what you want:
EDIT
here is a much better query, slightly modified from the great answer from #Christopher Klein:
I added the "LTRIM()" so the spaces in the color list, would be handled properly: "red, blue, green". His solution requires no spaces "red,blue,green". Also, I prefer to use my own Number table and not use master.dbo.spt_values, this allows the removal of one derived table too.
SELECT
ProductId, LEFT(PartialColor, CHARINDEX(',', PartialColor + ',')-1) as SplitColor
FROM (SELECT
t.ProductId, LTRIM(SUBSTRING(t.Color, n.Number, 200)) AS PartialColor
FROM test_table t
LEFT OUTER JOIN Numbers n ON n.Number<=LEN(t.Color) AND SUBSTRING(',' + t.Color, n.Number, 1) = ','
) t
EDIT END
SELECT
ProductId, Color --,number
FROM (SELECT
ProductId
,CASE
WHEN LEN(List2)>0 THEN LTRIM(RTRIM(SUBSTRING(List2, number+1, CHARINDEX(',', List2, number+1)-number - 1)))
ELSE NULL
END AS Color
,Number
FROM (
SELECT ProductId,',' + Color + ',' AS List2
FROM test_table
) AS dt
LEFT OUTER JOIN Numbers n ON (n.Number < LEN(dt.List2)) OR (n.Number=1 AND dt.List2 IS NULL)
WHERE SUBSTRING(List2, number, 1) = ',' OR List2 IS NULL
) dt2
ORDER BY ProductId, Number, Color
here is my result set:
ProductId Color
----------- --------------
1 red
1 blue
1 green
2 NULL
3 purple
3 green
(6 row(s) affected)
which is the same order you want...
You can try this out, doesnt require any additional functions:
declare #t table (col1 varchar(10), col2 varchar(200))
insert #t
select '1', 'red,blue,green'
union all select '2', NULL
union all select '3', 'green,purple'
select col1, left(d, charindex(',', d + ',')-1) as e from (
select *, substring(col2, number, 200) as d from #t col1 left join
(select distinct number from master.dbo.spt_values where number between 1 and 200) col2
on substring(',' + col2, number, 1) = ',') t
I arrived this question 10 years after the post.
SQL server 2016 added STRING_SPLIT function.
By using that, this can be written as below.
declare #product table
(
ProductId int,
Color varchar(max)
);
insert into #product values (1, 'red, blue, green');
insert into #product values (2, null);
insert into #product values (3, 'purple, green');
select
p.ProductId as ProductId,
ltrim(split_table.value) as Color
from #product p
outer apply string_split(p.Color, ',') as split_table;
Fix your database if at all possible. Comma delimited lists in database cells indicate a flawed schema 99% of the time or more.
I would create a CLR table-defined function for this:
http://msdn.microsoft.com/en-us/library/ms254508(VS.80).aspx
The reason for this is that CLR code is going to be much better at parsing apart the strings (computational work) and can pass that information back as a set, which is what SQL Server is really good at (set management).
The CLR function would return a series of records based on the parsed values (and the input id value).
You would then use a CROSS APPLY on each element in your table.
Just convert your columns into xml and query it. Here's an example.
select
a.value('.', 'varchar(42)') c
from (select cast('<r><a>' + replace(#CSV, ',', '</a><a>') + '</a></r>' as xml) x) t1
cross apply x.nodes('//r/a') t2(a)
Why not use dynamic SQL for this purpose, something like this(adapt to your needs):
DECLARE #dynSQL VARCHAR(max)
SET #dynSQL = 'insert into DestinationTable(field) values'
select #dynSQL = #dynSQL + '('+ REPLACE(Color,',',''',''') + '),' from Table
SET #dynSql = LEFT(#dynSql,LEN(#dynSql) -1) -- delete the last comma
exec #dynSql
One advantage is that you can use it on any SQL Server version

Concatenating various characters

I have a series of ID numbers like this
ABC/12345/2012
DEF/67891/2013
GHI/23456/2014
KLM/78911/2014
I need to change them so they look like this
12-12345
13-67891
14-23456
14-78911
14-6634
The below works to a degree but I have a few that only have 4 numbers in, they should be proceeded by a zero.
SELECT RIGHT(ID, 2)+'-'+RIGHT(SUBSTRING(ID, CHARINDEX('/', ID, 1)-1, LEN(ID)-7), 5)
12-12345
13-67891
14-23456
14-78911
14-/6634
So I need 14-/6634 to look like 14-06634
Assuming your column name is ID, and length of each substrings between the '/' characters is not variable (ABC = 3, 12345 = 5, 2012 = 4):
SELECT RIGHT(ID, 2)+'-'+RIGHT(SUBSTRING(ID, CHARINDEX('/', ID, 1)-1, LEN(ID)-7), 5)
Based on your main post edit:
SELECT RIGHT(ID, 2)+'-'+REPLACE(RIGHT(SUBSTRING(ID, CHARINDEX('/', ID, 1)-1, LEN(ID)-7), 5), '/', '0')
Try this
declare #tmp varchar(50) = 'ABC/12345/2012'
select SUBSTRING(#tmp, len(#tmp) - 1, 2) + '-' + SUBSTRING(#tmp,CHARINDEX('/',#tmp)+1,LEN(#tmp))
it gives you
12-12345/2012
and now you have to remove /2012
If your data is fixed format, then you can use PARSENAME
Sample execution with sample data:
DECLARE #TestTable TABLE (TestData VARCHAR (50));
INSERT INTO #TestTable (TestData)
SELECT 'ABC/12345/2012' UNION
SELECT 'DEF/67891/2013' UNION
SELECT 'GHI/23456/2014' UNION
SELECT 'KLM/78911/2014'
SELECT RIGHT(PARSENAME(REPLACE(TestData, '/', '.'), 1), 2) + '-' +
PARSENAME(REPLACE(TestData, '/', '.'), 2) AS TestData
FROM #TestTable
Result:
TestData
--------
12-12345
13-67891
14-23456
14-78911

Semicolon seperated value to other column in sql server

i have a table with a column have value seperated by semi colon.
the concern is value in the column are not fixed. it starts from 1 and end upto 80 semicolon sepaeration.
i am trying to put each individual value to seperate column
SQL SERVER 2008 code
DECLARE #Table TABLE(
Val VARCHAR(50)
)
INSERT INTO #Table (Val) SELECT '2Xcalcium; kidney' union all SELECT '3XMagnessium; liver' union all SELECT '2-ECG;3XSODIUM;DIALYSIS'
SELECT *,
CAST(LEFT(Val,CHARINDEX(';',Val)-1) AS VARCHAR) FirstValue,
CAST(RIGHT(Val,LEN(Val) - CHARINDEX(';',Val)) AS VARCHAR) SecondValue
FROM #Table
I tried the above code but this is limited to 2 semicolon only. please share your expertise.
Try it like this:
DECLARE #Table TABLE(
Val VARCHAR(50)
)
INSERT INTO #Table (Val) SELECT '2Xcalcium; kidney' union all SELECT '3XMagnessium; liver' union all SELECT '2-ECG;3XSODIUM;DIALYSIS';
;WITH Splitted AS
(
SELECT *
,CAST('<x>' + REPLACE(Val,';','</x><x>') + '</x>' AS XML) ValuesAsXML
FROM #Table
)
SELECT *
,ValuesAsXML.value('x[1]','varchar(max)') AS FirstCol
,ValuesAsXML.value('x[2]','varchar(max)') AS SecondCol
,ValuesAsXML.value('x[3]','varchar(max)') AS ThirdCol
,ValuesAsXML.value('x[4]','varchar(max)') AS FourthCol
,ValuesAsXML.value('x[5]','varchar(max)') AS FifthCol
FROM Splitted
The result
Val FirstCol SecondCol ThirdCol FourthCol FifthCol
2Xcalcium; kidney 2Xcalcium kidney NULL NULL NULL
3XMagnessium; liver 3XMagnessium liver NULL NULL NULL
2-ECG;3XSODIUM;DIALYSIS 2-ECG 3XSODIUM DIALYSIS NULL NULL
Most of the link provided extract the element into rows.
If you prefer to use your existing logic and extract the individual element into separate column, you can use multiple cascaded CROSS APPLY.
SELECT t.Val,
v1.V as V1,
v2.V as V2,
v3.V as V3
FROM #Table t
cross apply
(
select V = LEFT(t.Val, CHARINDEX(';', t.Val + ';') - 1),
Val = STUFF(t.Val, 1, CHARINDEX(';', t.Val + ';'), '')
) v1
cross apply
(
select V = LEFT(v1.Val, CHARINDEX(';', v1.Val + ';') - 1),
Val = STUFF(v1.Val, 1, CHARINDEX(';', v1.Val + ';'), '')
) v2
cross apply
(
select V = LEFT(v2.Val, CHARINDEX(';', v2.Val + ';') - 1),
Val = STUFF(v2.Val, 1, CHARINDEX(';', v2.Val + ';'), '')
) v3
From your question ,it seems that you have data in below format..This can be done easily with numbers table..
declare #string varchar(max)
set #string='s,t,a,c,k'
select substring(','+#string+',',n+1,charindex(',',','+#string+',',n+1)-n-1)
from
numbers
where n<=len(#string)
and substring(','+#string+',',n,1)=','
Output:
s
t
a
c
k
Few more Gems:
https://dba.stackexchange.com/questions/11506/why-are-numbers-tables-invaluable
http://sqlperformance.com/2012/07/t-sql-queries/split-strings

Selecting between quotes (") in SQL Server 2012

I have a table holding IDs in one column and a string in the second column like below.
COLUMN01 COLUMN02
----------------------------------------------------------------------------------
1 abc"11444,12,13"efg"14,15"hij"16,17,18,19"opqr
2 ahsdhg"21,22,23"ghshds"24,25"fgh"26,27,28,28"shgshsg
3 xvd"3142,32,33"hty"34,35"okli"36,37,38,39"adfd
Now I want to have the following result
COLUMN01 COLUMN02
-----------------------------------------------------------
1 11444,12,13,14,15,16,17,18,19
2 21,22,23,24,25,26,27,28,28
3 3142,32,33,34,35,36,37,38,39
How can I do that?
Thanks so much
Here is one way (maybe not the best, but it seems to work). I am NOT a SQL guru...
First, create this SQL Function. It came from: Extract numbers from a text in SQL Server
create function [dbo].[GetNumbersFromText](#String varchar(2000))
returns table as return
(
with C as
(
select cast(substring(S.Value, S1.Pos, S2.L) as int) as Number,
stuff(s.Value, 1, S1.Pos + S2.L, '') as Value
from (select #String+' ') as S(Value)
cross apply (select patindex('%[0-9]%', S.Value)) as S1(Pos)
cross apply (select patindex('%[^0-9]%', stuff(S.Value, 1, S1.Pos, ''))) as S2(L)
union all
select cast(substring(S.Value, S1.Pos, S2.L) as int),
stuff(S.Value, 1, S1.Pos + S2.L, '')
from C as S
cross apply (select patindex('%[0-9]%', S.Value)) as S1(Pos)
cross apply (select patindex('%[^0-9]%', stuff(S.Value, 1, S1.Pos, ''))) as S2(L)
where patindex('%[0-9]%', S.Value) > 0
)
select Number
from C
)
Then, you can do something like this to get the results you were asking for. Note that I broke the query up into 3 parts for clarity. And, obviously, you don't need to declare the table variable and insert data into it.
DECLARE #tbl
TABLE (
COLUMN01 int,
COLUMN02 varchar(max)
)
INSERT INTO #tbl VALUES (1, 'abc"11444,12,13"efg"14,15"hij"16,17,18,19"opqr')
INSERT INTO #tbl VALUES (2, 'ahsdhg"21,22,23"ghshds"24,25"fgh"26,27,28,28"shgshsg')
INSERT INTO #tbl VALUES (3, 'xvd"3142,32,33"hty"34,35"okli"36,37,38,39"adfd')
SELECT COLUMN01, SUBSTRING(COLUMN02, 2, LEN(COLUMN02) - 1) as COLUMN02 FROM
(
SELECT COLUMN01, REPLACE(COLUMN02, ' ', '') as COLUMN02 FROM
(
SELECT COLUMN01, (select ',' + number as 'data()' from dbo.GetNumbersFromText(Column02) for xml path('')) as COLUMN02 FROM #tbl
) t
) tt
GO
output:
COLUMN01 COLUMN02
1 11444,12,13,14,15,16,17,18,19
2 21,22,23,24,25,26,27,28,28
3 3142,32,33,34,35,36,37,38,39
I know you want to do it using SQL. But ones I had nearly the same problem and getting this data to a string using a php or another language, than parsing is a way to do it. For example, you can use this kind of code after receiving the data into a string.
function clean($string) {
$string = str_replace(' ', '-', $string); // Replaces all spaces with hyphens.
$string = preg_replace('/[^A-Za-z0-9\-]/', '', $string); // Removes special chars.
return preg_replace('/-+/', '-', $string); // Replaces multiple hyphens with single one.
}
For more information you might want to look at this post that I retrieved the function: Remove all special characters from a string
As I said this is an easy way to do it, I hope this could help.

Replace multiple characters in SQL

I have a problem where I want to replace characters
I am using replace function but that is not giving desired output.
Values of column table_value needs to replaced with their fill names like
E - Email
P - Phone
M - Meeting
I am using this query
select table_value,
replace(replace(replace(table_value, 'M', 'MEETING'), 'E', 'EMAIL'), 'P', 'PHONE') required_value
from foobar
so second required_value row should be EMAIL,PHONE,MEETING and so on.
What should I do so that required value is correct?
The below will work (even it's not a smart solution).
select
table_value,
replace(replace(replace(replace(table_value, 'M', 'MXXTING'), 'E', 'XMAIL'), 'P', 'PHONX'), 'X', 'E') required_value
from foobar
You can do it using CTE to split the table values into E, P and M, then replace and put back together.
I assumed each record has a unique identifer Id but please replace that with whatever you have.
;WITH cte
AS
(
SELECT Id, SUBSTRING(table_value, 1, 1) AS SingleValue, 1 AS ValueIndex
FROM replacetable
UNION ALL
SELECT replacetable.Id, SUBSTRING(replacetable.table_value, cte.ValueIndex + 1, 1) AS SingleValue, cte.ValueIndex + 1 AS ValueIndex
FROM cte
INNER JOIN replacetable ON cte.ValueIndex < LEN(replacetable.table_value)
)
SELECT DISTINCT Id,
STUFF((SELECT DISTINCT ','+ CASE SingleValue
WHEN 'E' THEN 'EMAIL'
WHEN 'P' THEN 'PHONE'
WHEN 'M' THEN 'MEETING'
END
FROM cte c
WHERE c.Id = cte.Id
AND SingleValue <> ','
FOR XML PATH ('')),1,1,'')
FROM cte
Sorry , for mess code, maybe this is not best way to solve this, but what I've tried:
SET QUOTED_IDENTIFIER ON
SET ANSI_NULLS ON
GO
CREATE Function [dbo].[fn_CSVToTable]
(
#CSVList Varchar(max)
)
RETURNS #Table TABLE (ColumnData VARCHAR(100))
AS
BEGIN
DECLARE #S varchar(max),
#Split char(1),
#X xml
SELECT #Split = ','
SELECT #X = CONVERT(xml,' <root> <s>' + REPLACE(#CSVList,#Split,'</s> <s>') + '</s> </root> ')
INSERT INTO #Table
SELECT CASE RTRIM(LTRIM(T.c.value('.','varchar(20)'))) WHEN 'M' THEN 'Meeting'
WHEN 'P' THEN 'Phone'
WHEN 'E' THEN 'Email'
End
FROM #X.nodes('/root/s') T(c)
RETURN
END
GO
Then When I run this:
Select Main.table_value,
Left(Main.ColumnData,Len(Main.ColumnData)-1) As ColumnData
From
(
Select distinct tt2.table_value,
(
Select tt1.ColumnData+ ',' AS [text()]
From (
SELECT
*
FROM dbo.TestTable tt
CROSS APPLY dbo.fn_CSVToTable(tt.table_value)
) tt1
Where tt1.table_value = tt2.TABLE_value
ORDER BY tt1.table_value
For XML PATH ('')
) ColumnData
From dbo.TestTable tt2
) [Main]
I get this:
table_value ColumnData
-------------------------------------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
E,P Email,Phone
E,P,M Email,Phone,Meeting
P,E Phone,Email
P,M Phone,Meeting
(4 row(s) affected)
You could also use a DECODE or CASE to translate the values.