I'm having a column in a table which contains data as shown below
Oranges
Apple
Oranges, Apple
Mango
I need to find the occurences and the name of the fruit and return the output in two columns
Oranges - Oranges(2)
Apple - Apple(2)
Mango - Mango(1)
Is it possible to combine LIKE and COUNT function to get the desired results.
There is a STRING_SPLIT support from sql-server 2016 if your version lower than 2016,you can try to write a split function to split your column by ,
CREATE FUNCTION fn_split ( #stringToSplit VARCHAR(MAX) )
RETURNS
#returnList TABLE ([Name] [nvarchar] (500))
AS
BEGIN
DECLARE #name NVARCHAR(255)
DECLARE #pos INT
WHILE CHARINDEX(',', #stringToSplit) > 0
BEGIN
SELECT #pos = CHARINDEX(',', #stringToSplit)
SELECT #name = SUBSTRING(#stringToSplit, 1, #pos-1)
INSERT INTO #returnList
SELECT #name
SELECT #stringToSplit = SUBSTRING(#stringToSplit, #pos+1, LEN(#stringToSplit)-#pos)
END
INSERT INTO #returnList
SELECT #stringToSplit
RETURN
END
then use CROSS APPLY to get count by the name.
SELECT Name,count(*)
FROM T t1 CROSS APPLY fn_split(t1.col) v
group by Name
sqlfiddle
you have to create a function same as below.
CREATE FUNCTION [dbo].[split](
#delimited NVARCHAR(MAX),
#delimiter NVARCHAR(100)
) RETURNS #t TABLE (id INT IDENTITY(1,1), val NVARCHAR(MAX))
AS
BEGIN
DECLARE #xml XML
SET #xml = N'<t>' + REPLACE(#delimited,#delimiter,'</t><t>') + '</t>'
INSERT INTO #t(val)
SELECT r.value('.','varchar(MAX)') as item
FROM #xml.nodes('/t') as records(r)
RETURN
END
then Eg.
create table #Fruits (
id int identity(1,1),
string nvarchar(100)
)
insert into #Fruits (string) values ('Apple,Mango'), ('Orange'), ('Apple')
select val,val+'(' +cast(count(val) as varchar(10))+')'val
from #Fruits
cross apply dbo.split(string,',')
group by val
I have a stored procedure that needs to be filtered by a select box that could have a value between 0 to 3. This value corresponds to table where each int value could have two or more VARCHAR values associated with it. For example 0 could be assigned to 'A' 'B' or 'C' while 1 could be 'D' or 'E'
I have attempted to store these options as a variable #m which is set like this:
DECLARE #m varchar(50) =
CASE
WHEN #mid = 0
THEN N'''A'',''B'',''C'',''D'',''E'''
WHEN #mid = 1
THEN N'''A'',''B'''
WHEN #mid = 2
THEN N'(''C'',''D'')'
ELSE N'''E'''
END
Which returns the expected value if I return it on it's own, with the ' marks and , separators in the correct places. But when I try to use it to select the correct records from my table, I get nothing.
select #m --Returns 'C','D','E'
select * from table where mValue in (#m) -- Returns nothing
select * from table where mValue in ('C','D','E') -- Returns all expected rows
And it doesn't make any difference if I have the brackets in the #m variable or not, then second select won't return any rows.
As Gordon mentioned, you will have to create a function for splitting your string. I use below logic (if it's always delimited by ,)
CREATE FUNCTION [dbo].[fn_split_string] ( #stringToSplit VARCHAR(MAX) )
RETURNS
#returnList TABLE ([Name] [nvarchar] (500))
AS
BEGIN
DECLARE #name NVARCHAR(255)
DECLARE #pos INT
WHILE CHARINDEX(',', #stringToSplit) > 0
BEGIN
SELECT #pos = CHARINDEX(',', #stringToSplit)
SELECT #name = SUBSTRING(#stringToSplit, 1, #pos-1)
INSERT INTO #returnList
SELECT #name
SELECT #stringToSplit = SUBSTRING(#stringToSplit, #pos+1, LEN(#stringToSplit)-#pos)
END
INSERT INTO #returnList
SELECT #stringToSplit
RETURN
END
Then you can simply use this function in your query.
select * from table where mValue in (select * from fn_split_string (#m))
One method is to split #m. You can find various "split" functions on the web (the functionality was added in SQL Server 2016).
The idea is then to do something like:
with m(item) as (
select *
from dbo.split(#m, ',') s
)
select *
from table
where mValue in (select item from m) ;
I am having a table with columns ID, CHAT_ID, USERNAME, MONTH, DAY, YEAR, STATUS.
[my_table][1][1]: https://i.stack.imgur.com/5XjM2.png
The status contains tweet by a user. Now I'm trying to find the frequency of words on status column grouping by username i.e., I need to create a new table which shows how many times a user uses a particular word. The table needs to have columns username, word and frequency.
I used the following query to find frequency of words on status column
select sep.col AS WORD, count(*) as Frequency from
(
select * from
(
select value = Lower
(RTrim
(LTrim
(convert(VARCHAR(MAX), STATUS))
)) FROM My_table
) easyValues
Where value <> ''
) actualValues
Cross Apply dbo.SeparateValues(value, ' ') sep
Group By USERNAME,sep.col
Order By Count(*) Desc
The dbo. SeparateValues() is a table valued function
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER Function [dbo].[SeparateValues]
(
#data VARCHAR(MAX),
#delimiter VARCHAR(10)
)
RETURNS
#tbldata TABLE(col VARCHAR(MAX))
As
--Declare #data VARCHAR(MAX) ,#delimiter VARCHAR(10)
--Declare #tbldata TABLE(col VARCHAR(10))
--Set #data = 'hello,how,are,you?,234234'
--Set #delimiter = ','
--DECLARE #tbl TABLE(col VARCHAR(10))
Begin
DECLARE #pos INT
DECLARE #prevpos INT
SET #pos = 1
SET #prevpos = 0
WHILE #pos > 0
BEGIN
SET #pos = CHARINDEX(#delimiter, #data, #prevpos+1)
if #pos > 0
INSERT INTO #tbldata(col) VALUES(LTRIM(RTRIM(SUBSTRING(#data, #prevpos+1, #pos-#prevpos-1))))
else
INSERT INTO #tbldata(col) VALUES(LTRIM(RTRIM(SUBSTRING(#data, #prevpos+1, len(#data)-#prevpos))))
SET #prevpos = #pos
End
RETURN
END
I got it from the internet.
But I'm unable to do the part of finding the number of times the word was used by a user. I'm new to SQL, Can anyone please point me in a right direction to do so? Any help would be really appreciated. thank you!
Currently I'm working on a project where we just added the ability to have multiple values stored within a field. Previously, it stored a single value, but now it contains multiple. Below is an example of what I'm talking about.
Ex. A person's name is passed in (John Smith). Now the user can pass in multiple people's names, delimited by a ';' (John Smith;John Doe;Jane Tandy).
My issue is that we have a data source for a drop down that currently feeds off this field. Below is the SQL for it.
Select Distinct Content
From LIB_DocumentAttribute A
inner join LIB_PublicDocument B on A.PublicDocumentId = B.PublicDocumentId
Where AttributeId = (Select AttributeId From LIB_Attribute Where FieldName='Author')
and B.Status = 'Approved'
This somewhat works now. Content is the field that contains the multiple names. Now when the drop down is loaded, it pulls back the concatenated string of names (the longer one from above). I want to break it apart for the data source. So far, my only ideas is to split out the data based on the ';'. However, I need to take that split out data and apply it to the table that returns the rest of the data. Below is where I have gotten to but have become stuck on.
CREATE TABLE #Authors
(
Content varchar(MAX)
)
CREATE TABLE #Temp1
(
Content varchar(MAX)
)
CREATE TABLE #Temp2
(
Content varchar(MAX)
)
CREATE TABLE #Temp3
(
Content varchar(MAX)
)
--Load Authors table to store all Authors
INSERT INTO #Authors
Select Distinct Content
From LIB_DocumentAttribute A
inner join LIB_PublicDocument B on A.PublicDocumentId = B.PublicDocumentId
Where AttributeId = (Select AttributeId From LIB_Attribute Where FieldName='Author')
and B.Status = 'Approved'
--Take multiple Authors separated by '; ' and add to Temp1
INSERT INTO #Temp1
SELECT REPLACE(Content, '; ', ';') FROM #Authors WHERE Content LIKE '%; %'
--Remove multiple Authors separated by '; ' from Authors table
DELETE FROM #Authors
WHERE Content LIKE '%; %'
--Take multiple Authors separated by ';' and add to Temp2
INSERT INTO #Temp2
SELECT Content FROM #Authors WHERE Content LIKE '%;%'
--Remove multiple Authors separated by ';' from Authors table
DELETE FROM #Authors
WHERE Content LIKE '%;%'
--Somewhow split data and merge back together
DROP TABLE #Authors
DROP TABLE #Temp1
DROP TABLE #Temp2
DROP TABLE #Temp3
Edit:
So in the end, I came up with a solution that utilized some of the pieces that Kumar suggested. I created a function for splitting the string as he suggested and added some personal changes to make it work. Mind you this is in a table return function, with the table called #Authors, and it has one column called Content.
BEGIN
DECLARE #Temp TABLE
(
Content varchar(MAX)
)
--Load Authors table to store all Authors
INSERT INTO #Authors
Select Distinct Content
From LIB_DocumentAttribute A
inner join LIB_PublicDocument B on A.PublicDocumentId = B.PublicDocumentId
Where AttributeId = (Select AttributeId From LIB_Attribute Where FieldName='Author')
--Take multiple Authors separated by ', ' and add to Temp
INSERT INTO #Temp
SELECT REPLACE(Content, ', ', ',')
FROM #Authors;
--Remove multiple Authors separated by ', ' from Authors table
DELETE FROM #Authors
WHERE Content LIKE '%,%';
--Readd multiple Authors now separated into Authors table
INSERT INTO #Authors
SELECT s.Content
FROM #Temp
OUTER APPLY SplitString(Content,',') AS s
WHERE s.Content <> (SELECT TOP 1 a.Content FROM #Authors a WHERE s.Content = a.Content)
RETURN
END
Check the demo in fiddler link http://sqlfiddle.com/#!3/390f8/11
Create table test(name varchar(1000));
Insert into test values('AAA BBB; CCC DDD; eee fff');
CREATE FUNCTION SplitString
(
#Input NVARCHAR(MAX),
#Character CHAR(1)
)
RETURNS #Output TABLE (
Item NVARCHAR(1000)
)
AS
BEGIN
DECLARE #StartIndex INT, #EndIndex INT
SET #StartIndex = 1
IF SUBSTRING(#Input, LEN(#Input) - 1, LEN(#Input)) <> #Character
BEGIN
SET #Input = #Input + #Character
END
WHILE CHARINDEX(#Character, #Input) > 0
BEGIN
SET #EndIndex = CHARINDEX(#Character, #Input)
INSERT INTO #Output(Item)
SELECT SUBSTRING(#Input, #StartIndex, #EndIndex - 1)
SET #Input = SUBSTRING(#Input, #EndIndex + 1, LEN(#Input))
END
RETURN
END
Declare #name varchar(100)
Declare #table as table(name varchar(1000))
Declare cur cursor for
Select name from test
Open cur
fetch next from cur into #name
while (##FETCH_STATUS = 0)
begin
Insert into #table
Select * from dbo.splitstring(#name,';')
fetch next from cur into #name
end
close cur
deallocate cur
Select * from #table
this might work.
drop table authors
GO
create table Authors (Author_ID int identity (1,1),name varchar (255), category varchar(255))
GO
insert into authors
(name,category)
select
'jane doe','nonfiction'
union
select
'Jules Verne; Mark Twain; O. Henry', 'fiction'
union
select
'John Smith; John Doe', 'nonfiction'
GO
DECLARE #table TABLE (
names VARCHAR(255)
,id INT
)
DECLARE #category VARCHAR(255)
SET #category = 'nonfiction'
DECLARE #Author_ID INT
DECLARE AuthorLookup CURSOR
FOR
SELECT Author_ID
FROM authors
WHERE category = #category
OPEN AuthorLookup
FETCH NEXT
FROM AuthorLookup
INTO #Author_ID
WHILE ##FETCH_STATUS = 0
BEGIN
IF (
SELECT CHARINDEX(';', NAME, 0)
FROM authors
WHERE Author_ID = #Author_ID
) = 0
BEGIN
INSERT INTO #table
SELECT NAME
,Author_ID
FROM authors
WHERE Author_ID = #Author_ID
END
ELSE
BEGIN
DECLARE #value VARCHAR(255)
SELECT #value = NAME
FROM authors
WHERE Author_ID = #Author_ID
WHILE len(#value) > 0
BEGIN
INSERT INTO #table
SELECT substring(#value, 0, CHARINDEX(';', #value, 0))
,#Author_ID
SELECT #value = replace(#value, substring(#value, 0, CHARINDEX(';', #value, 0) + 2), '')
IF CHARINDEX(';', #value, 0) = 0
BEGIN
INSERT INTO #table
SELECT #value
,#Author_ID
SET #value = ''
END
END
END
FETCH NEXT
FROM AuthorLookup
INTO #Author_ID
END
CLOSE AuthorLookup
DEALLOCATE AuthorLookup
SELECT *
FROM #table
You can create function to split your data
create function SplitString
(
#data nvarchar(max),
#sep char(1)
)
returns #result table (data nvarchar(max))
as
begin
declare #i int
while 1 = 1
begin
select #i = charindex(#sep, #data)
if #i = 0
begin
insert into #result
select #data
break
end
insert into #result
select rtrim(left(#data, #i - 1))
select #data = ltrim(right(#data, len(#data) - #i))
end
return
end
and use it like this:
select s.data
from test as t
outer apply SplitString(t.data,';') as s
If you're sure that you don't have special characters inside your data, you can also consider trick with xml:
;with cte as (
select
cast('<s>' + replace(data, ';', '</s><s>') + '</s>' as xml) as data
from test
)
select
t.c.value('.', 'nvarchar(max)') as data
from cte
outer apply data.nodes('s') as t(c)
sql fiddle demo
I have a list of ids separated by comma like:
1,17,25,44,46,67,88
I want to convert them to a table records ( into a temporary table ) like
#tempTable
number_
--------
1
17
25
44
46
67
88
It is possible with a function, a table-valued one ?
Why I want this ? I want to use for INNER JOIN clause (into stored procedure) with another table(s) like as:
SELECT a,b,c FROM T1
INNER JOIN functionNameWhichReturnsTable
ON functionNameWhichReturnsTable.number_ = T1.a
I cannot use IN because I will use stored procedure which accepts a parameter of type NVARCHAR. That parameter will provide the list of ids.
Thank you
Possible duplicate of separate comma separated values and store in table in sql server.
Please try a precise one from Comma-Delimited Value to Table:
CREATE FUNCTION [dbo].[ufn_CSVToTable] ( #StringInput VARCHAR(8000), #Delimiter nvarchar(1))
RETURNS #OutputTable TABLE ( [String] VARCHAR(10) )
AS
BEGIN
DECLARE #String VARCHAR(10)
WHILE LEN(#StringInput) > 0
BEGIN
SET #String = LEFT(#StringInput,
ISNULL(NULLIF(CHARINDEX(#Delimiter, #StringInput) - 1, -1),
LEN(#StringInput)))
SET #StringInput = SUBSTRING(#StringInput,
ISNULL(NULLIF(CHARINDEX(#Delimiter, #StringInput), 0),
LEN(#StringInput)) + 1, LEN(#StringInput))
INSERT INTO #OutputTable ( [String] )
VALUES ( #String )
END
RETURN
END
GO
Check the requirement in other way using XML:
DECLARE #param NVARCHAR(MAX)
SET #param = '1:0,2:1,3:1,4:0'
SELECT
Split.a.value('.', 'VARCHAR(100)') AS CVS
FROM
(
SELECT CAST ('<M>' + REPLACE(#param, ',', '</M><M>') + '</M>' AS XML) AS CVS
) AS A CROSS APPLY CVS.nodes ('/M') AS Split(a)
Here's a trick that doesn't need a function or XML.
Basically the string gets transformed into a single insert statement for a temporary table.
The temp table can then be used for further processing.
IF OBJECT_ID('tempdb..#tmpNum') IS NOT NULL
DROP TABLE #tmpNum;
CREATE TABLE #tmpNum (num int);
DECLARE #TEXT varchar(max) = '1,17,25,44,46,67,88';
DECLARE #InsertStatement varchar(max);
SET #InsertStatement = 'insert into #tmpNum (num) values ('+REPLACE(#TEXT,',','),(')+');';
EXEC (#InsertStatement);
-- use the temp table
SELECT *
FROM YourTable t
WHERE t.id IN (SELECT DISTINCT num FROM #tmpNum);
This method is usable for up to 1000 values.
Because 1000 is the max limit of a row value expression.
Also, as Stuart Ainsworth pointed out.
Since this method uses Dynamic Sql, be wary of code injection and don't use it for strings based on user input.
Side-note
Starting from MS Sql Server 2016, one could simply use the STRING_SPLIT function.
DECLARE #TEXT varchar(max);
SET #TEXT = '1,17,25,44,46,67,88';
SELECT t.*
FROM YourTable t
JOIN (SELECT DISTINCT CAST(value AS INT) num FROM STRING_SPLIT(#TEXT, ',')) nums
ON t.id = nums.num;
Completing the answers, you could also use the CSV string to store multiple values in multiple columns:
--input sql text
declare #text_IN varchar(max) ='text1, text1.2, text1.3, 1, 2010-01-01\r\n text2, text2.2, text2.3, 2, 2016-01-01'
Split the csv file into rows:
declare #temptable table (csvRow varchar(max))
declare #DelimiterInit varchar(4) = '\r\n'
declare #Delimiter varchar(1) = '|'
declare #idx int
declare #slice varchar(max)
set #text_IN = REPLACE(#text_IN,#DelimiterInit,#Delimiter)
select #idx = 1
if len(#text_IN)<1 or #text_IN is null return
while #idx!= 0
begin
set #idx = charindex(#Delimiter,#text_IN)
if #idx!=0
set #slice = left(#text_IN,#idx - 1)
else
set #slice = #text_IN
if(len(#slice)>0)
insert into #temptable(csvRow) values(#slice)
set #text_IN = right(#text_IN,len(#text_IN) - #idx)
if len(#text_IN) = 0 break
end
Split rows into columns:
;WITH XMLTable (xmlTag)
AS
(
SELECT CONVERT(XML,'<CSV><champ>' + REPLACE(csvRow,',', '</champ><champ>') + '</champ></CSV>') AS xmlTag
FROM #temptable
)
SELECT RTRIM(LTRIM(xmlTag.value('/CSV[1]/champ[1]','varchar(max)'))) AS Column1,
RTRIM(LTRIM(xmlTag.value('/CSV[1]/champ[2]','varchar(max)'))) AS Column2,
RTRIM(LTRIM(xmlTag.value('/CSV[1]/champ[3]','varchar(max)'))) AS Column3,
RTRIM(LTRIM(xmlTag.value('/CSV[1]/champ[4]','int'))) AS Column4,
RTRIM(LTRIM(xmlTag.value('/CSV[1]/champ[5]','datetime'))) AS Column5
FROM XMLTable
The following works:
declare #parStoreNo As varchar(8000) = '1,2,3,4'
CREATE TABLE #parStoreNo (StoreNo INT)-- drop #parStoreNo
declare #temptable VARCHAR(1000) = #parStoreNo
declare #SQL VARCHAR(1000)
SELECT #SQL = CONVERT(VARCHAR(1000),' select ' + REPLACE(ISNULL(#temptable,' NULL '),',', ' AS Col UNION ALL SELECT '))
INSERT #parStoreNo (StoreNo)
EXEC (#SQL)
I am using XML Function as below...
DECLARE #str VARCHAR(4000) = '6,7,7,8,10,12,13,14,16,44,46,47,394,396,417,488,714,717,718,719,722,725,811,818,832,833,836,837,846,913,914,919,922,923,924,925,926,927,927,928,929,929,930,931,932,934,935,1029,1072,1187,1188,1192,1196,1197,1199,1199,1199,1199,1200,1201,1202,1203,1204,1205,1206,1207,1208,1209,1366,1367,1387,1388,1666,1759,1870,2042,2045,2163,2261,2374,2445,2550,2676,2879,2880,2881,2892,2893,2894'
Declare #x XML
select #x = cast('<A>'+ replace(#str,',','</A><A>')+ '</A>' as xml)
select t.value('.', 'int') as inVal
from #x.nodes('/A') as x(t)
I prefer this because not need to create any separate function and proc. Also I don't have to opt dynamic SQL query which I prefer most.
Convert Comma Separated String to Table
DECLARE #str VARCHAR(4000) = '6,7,7,8,10,12,13,14,16,44,46,47,394,396,417,488,714,717,718,719,722,725,811,818,832'
DECLARE #x XML
select #x = cast('<A>'+ replace(#str,',','</A><A>')+ '</A>' as xml)
select t.value('.', 'int') as inVal
from #x.nodes('/A') as x(t)
Try this code
SELECT RTRIM(part) as part
INTO Table_Name
FROM dbo.splitstring(#Your_Comma_string,',')
splitstring Function is as follows
CREATE FUNCTION dbo.splitstring ( #stringToSplit VARCHAR(MAX) )
RETURNS
#returnList TABLE ([Name] [nvarchar] (500))
AS
BEGIN
DECLARE #name NVARCHAR(255)
DECLARE #pos INT
WHILE CHARINDEX(',', #stringToSplit) > 0
BEGIN
SELECT #pos = CHARINDEX(',', #stringToSplit)
SELECT #name = SUBSTRING(#stringToSplit, 1, #pos-1)
INSERT INTO #returnList
SELECT #name
SELECT #stringToSplit = SUBSTRING(#stringToSplit, #pos+1, LEN(#stringToSplit)-#pos)
END
INSERT INTO #returnList
SELECT #stringToSplit
RETURN
END