in SQL, how can I find duplicate string values within the same record? - sql

Sample table
Record Number | Filter | Filters_Applied
----------------------------------------------
1 | yes | red, blue
2 | yes | green
3 | no |
4 | yes | red, red, blue
Is it possible to query all records where there are duplicate string values? For example, how could I query to pull record 4 where the string "red" appeared twice? Except in the table that I am dealing with, there are far more string values that can populate in the "filters_applied" column.
CLARIFICATION I am working out of Periscope and pulling data using SQL.

I assume that you have to check that in the logical page.
You can query the table with like '%red%'.
select Filters_Applied from table where Filters_Applied like '%red%';
You will get the data which has red at least one. Then, doing some string analysis in logic page.
In php, You can use the substr_count function to determine the number of occurrences of the string.
//the loop to load db query
while(){
$number= substr_count("Filters_Applied",red);
if($number>1){
echo "this".$Filters_Applied.">1"
}
}

for SQL-SERVER or other versions which can run these functions
Apply this logic
declare #val varchar(100) = 'yellow,blue,white,green'
DECLARE #find varchar(100) = 'green'
select #val = replace(#val,' ','') -- remove spaces
select #val;
select (len(#val)-len(replace(#val,#find,'')))/len(#find) [recurrence]

Create this Function which will parse string into rows and write query as given below. This will works for SQL Server.
CREATE FUNCTION [dbo].[StrParse]
(#delimiter CHAR(1),
#csv NTEXT)
RETURNS #tbl TABLE(Keys NVARCHAR(255))
AS
BEGIN
DECLARE #len INT
SET #len = Datalength(#csv)
IF NOT #len > 0
RETURN
DECLARE #l INT
DECLARE #m INT
SET #l = 0
SET #m = 0
DECLARE #s VARCHAR(255)
DECLARE #slen INT
WHILE #l <= #len
BEGIN
SET #l = #m + 1--current position
SET #m = Charindex(#delimiter,Substring(#csv,#l + 1,255))--next delimiter or 0
IF #m <> 0
SET #m = #m + #l
--insert #tbl(keys) values(#m)
SELECT #slen = CASE
WHEN #m = 0 THEN 255 --returns the remainder of the string
ELSE #m - #l
END --returns number of characters up to next delimiter
IF #slen > 0
BEGIN
SET #s = Substring(#csv,#l,#slen)
INSERT INTO #tbl
(Keys)
SELECT #s
END
SELECT #l = CASE
WHEN #m = 0 THEN #len + 1 --breaks the loop
ELSE #m + 1
END --sets current position to 1 after next delimiter
END
RETURN
END
GO
CREATE TABLE Table1# (RecordNumber int, [Filter] varchar(5), Filters_Applied varchar(100))
GO
INSERT INTO Table1# VALUES
(1,'yes','red, blue')
,(2,'yes','green')
,(3,'no ','')
,(4,'yes','red, red, blue')
GO
--This query will return what you are expecting
SELECT t.RecordNumber,[Filter],Filters_Applied,ltrim(rtrim(keys)), count(*)NumberOfRows
FROM Table1# t
CROSS APPLY dbo.StrParse (',', t.Filters_Applied)
GROUP BY t.RecordNumber,[Filter],Filters_Applied,ltrim(rtrim(keys)) HAVING count(*) >1

You didn't state your DBMS, but in Postgres this isn't that complicated:
select st.*
from sample_table st
join lateral (
select count(*) <> count(distinct trim(item)) as has_duplicates
from unnest(string_to_array(filters_applied,',')) as t(item)
) x on true
where x.has_duplicates;
Online example: http://rextester.com/TJUGJ44586
With the exception of string_to_array() the above is actually standard SQL

Related

I have a column with datatype nvarchar and I want to sort it in ascending order. How do I achieve it in SSRS?

This is what I'm getting
abc 1
abc 12
abc 15
abc 2
abc 3
And this is how I want
abc 1
abc 2
abc 3
abc 12
abc 15
Query that I use:
select *
from view_abc
order by col1
Use a function to strip out the non numeric characters and leave just the value. Use another function to strip out all the numeric data. You can then sort on the two returned values.
It seems like a bit of work at first but once the functions are in you can re-use them in the future. Here's two functions I use regularly when we get data in from external sources and it's not very normalised.
They may not be the most efficient functions in the world but they work for my purposes
1st a function to just leave the numeric portion.
CREATE FUNCTION [fn].[StripToAlpha]
(
#inputString nvarchar(4000)
)
RETURNS varchar(4000)
AS
BEGIN
DECLARE #Counter as int
DECLARE #strReturnVal varchar(4000)
DECLARE #Len as int
DECLARE #ASCII as int
SET #Counter=0
SET #Len=LEN(#inputString)
SET #strReturnVal = ''
WHILE #Counter<=#Len
BEGIN
SET #Counter = #Counter +1
SET #ascii= ASCII(SUBSTRING(#inputString,#counter,1))
IF(#ascii BETWEEN 65 AND 90) OR (#ascii BETWEEN 97 AND 122)
BEGIN
SET #strReturnVal = #strReturnVal + (SUBSTRING(#inputString,#counter,1))
END
END
RETURN #strReturnVal
END
2nd a function to extract the value from a text field, this also handle percentages (e.g. abc 23% comes out as 0.23) but this is not required in your case.
You'll need to CREATE an 'fn' schema of change the schema name first...
CREATE FUNCTION [fn].[ConvertToValue]
(
#inputString nvarchar(4000)
)
RETURNS Float
AS
BEGIN
DECLARE #Counter as int
DECLARE #strReturnVal varchar(4000)
DECLARE #ReturnVal Float
DECLARE #Len as int
DECLARE #ASCII as int
SET #Counter=0
SET #Len=LEN(#inputString)
SET #strReturnVal = ''
IF #inputString IS NULL
BEGIN
Return NULL
END
IF #Len = 0 OR LEN(LTRIM(RTRIM(#inputString))) = 0
BEGIN
SET #ReturnVal=0
END
ELSE
BEGIN
WHILE #Counter<=#Len
BEGIN
SET #Counter = #Counter +1
SET #ascii= ASCII(SUBSTRING(#inputString,#counter,1))
IF(#ascii BETWEEN 48 AND 57) OR (#ascii IN (46,37))
BEGIN
SET #strReturnVal = #strReturnVal + (SUBSTRING(#inputString,#counter,1))
END
END
if RIGHT(#strReturnVal,1)='%'
BEGIN
SET #strReturnVal = LEFT(#strReturnVal,len(#strReturnVal)-1)
SET #strReturnVal = CAST((CAST(#strReturnVal AS FLOAT)/100) AS nvarchar(4000))
END
SET #ReturnVal = ISNULL(#strReturnVal,0)
END
RETURN #ReturnVal
END
Now we have the two functions created you can simply do
SELECT *
FROM view_abc
ORDER BY fn.StripToAlpha(Col1), fn.ConvertToValue(Col1)
Try this
Edited :
SELECT CAST(SUBSTRING(ColumnNameToOrder, CHARINDEX(' ', ColumnNameToOrder, 0), LEN (ColumnNameToOrder)) AS INT) AS IntColumn, SUBSTRING(ColumnNameToOrder,0, CHARINDEX(' ', ColumnNameToOrder, 0)) AS CharColumn, * FROM view_abc ORDER BY Charcolumn, Intcolumn
Instead of ColumnNameToOrder, you can put your column name which contains the data like 'abc 123'...
Tell me if it works please.
This is what I have come up with. Maybe it can help you, or at least point you in the right direction.
I tested the values. When the values have a zero, the order is like you would like the order to be. Like this:
abc 01
abc 02
abc 03
abc 12
abc 15
So you can run this query to update the existing values, to add the zero.
UPDATE abc
SET col1 = 'abc 0' + SUBSTRING(col1, 5, 1)
WHERE LEN(col1) = 5
Or you can do the above query like this if the first three characters can vary:
UPDATE abc
SET col1 = (SUBSTRING(col1, 1, 3) + ' 0' + SUBSTRING(col1, 5, 1))
WHERE col1 LIKE 'abc__'
This will override the existing value in the col1 column, only when the length of the current String is of length 5.
Then you can run the following query to get the results:
SELECT col1
FROM abc
ORDER BY col1 ASC
=cint(right(Fields!Col1.Value, instrrev(Fields!Col1.Value, " ")-1))
This will work in SSRS and sort correctly, but will only work if Col1 always contains a space, and that the characters after the space can be converted to an integer.

How to split comma separated string in sql

Split only after comma 3 times appear in sql.
I have string that looks like this
"abc,123,855,jkl,ddd,rrr,sss,999,777,kkk,jkl,ddd,rrr,sss,999"
getting this from db.
What I want returned is a string[] that was split after every 3rd comma, so it would look like this:
abc,123,855,
jkl,ddd,rrr,
sss,999,777,
kkk,jkl,ddd,
rrr,sss,999
Need this field to display in my JSP page, currently it taking more space of the table row.
Would really appreciate any help I can get!
Here is the complete example.
DECLARE #valueList varchar(8000)
DECLARE #pos INT
DECLARE #len INT
DECLARE #value varchar(8000)
SET #valueList = 'Pakistan,UAE,USA,UK,'
set #pos = 0
set #len = 0
WHILE CHARINDEX(',', #valueList, #pos+1)>0
BEGIN
set #len = CHARINDEX(',', #valueList, #pos+1) - #pos
set #value = SUBSTRING(#valueList, #pos, #len)
SELECT #pos, #len, #value
set #pos = CHARINDEX(',', #valueList, #pos+#len) +1
END
Solution for MS SQL. Assuming that your string with commas is in column in a table and there could be more rows with more different strings. Also assuming that last string remaining after parsing could have less than 3 commas.
Using two loops, First loop is checking if there are still rows to process. Second inner loop is checking if there is still remaining string with commas to process.
Original table with comma string column TableWithCommaStrings
Final table in this case only temp #Temp_3, but you can insert it anywhere.
IF OBJECT_ID('tempdb..#Temp_1') IS NOT NULL DROP TABLE #Temp_1
SELECT *
INTO #Temp_1
FROM TableWithCommaStrings
;
DECLARE #RowCount AS INT = 1;
WHILE #RowCount >= 1
BEGIN
IF OBJECT_ID('tempdb..#Temp_2') IS NOT NULL DROP TABLE #Temp_2
SELECT TOP 1 * /* Pick Only First line */
INTO #Temp_2
FROM #Temp_1;
WITH AA AS (SELECT TOP 1 * FROM #Temp_1 ) /* Delete that first line from Temp1*/
DELETE FROM AA;
/* Parse Column till last comma character remain there*/
DECLARE #CommaCharCount AS INT = 3;
WHILE #CommaCharCount >= 3
BEGIN
IF OBJECT_ID('tempdb..#Temp_3') IS NULL CREATE TABLE #Temp_3 (YourStringColumn VARCHAR(MAX));
INSERT INTO #Temp_3 /* Insert substring into temp3 based on Third Comman from beginning*/
SELECT substring(AA.YourStringColumn,1,(CharIndex(',',AA.YourStringColumn,Charindex(',',AA.YourStringColumn,CharIndex(',',AA.YourStringColumn)+1) +1))-1)
FROM #Temp_2 AS AA
UPDATE BB /*Remove part of string which has been procssed already in step above*/
SET BB.YourStringColumn = substring(YourStringColumn,((CharIndex(',',YourStringColumn,Charindex(',',YourStringColumn,CharIndex(',',YourStringColumn)+1) +1)+1)+1),LEN(YourStringColumn))
FROM #Temp_2 AS BB
/* Set comma counter */
SELECT #CommaCharCount= LEN(YourStringColumn) - LEN(REPLACE(YourStringColumn, ',', '')) FROM #Temp_2
END
IF (SELECT LEN(YourStringColumn) FROM #Temp_2) > 0
INSERT INTO #Temp_3 /* Last remaining Characters */
SELECT YourStringColumn
FROM #Temp_2
SELECT #RowCount = COUNT(*) FROM #Temp_1;
END
You can accomplish this far more easily in JSP using JSTL. In fact, since your actual need is just of display, you don't even need to create an array. Here is how to do it using the <c:forTokens> JSTL tag.
<c:set var="sqlString" value="abc,123,855,jkl,ddd,rrr,sss,999,777,kkk" />
<c:forTokens var="token" varStatus="tokenCounter" items="${sqlString}" delims=",">
<c:out value="${token}" />,
<c:if test="${tokenCounter.count % 3 == 0}">
<br />
</c:if>
</c:forTokens>

SQL function to split a string into characters

I hava an ID column in my database, and it shows the results as follows
1121
1232
1233
and i want to get an extra column where i can have their sums as follows
5
8
9
can anyone help me which sql function should i use to break a string into characters and add them?
Assuming a number that is always 4 digits long, you can simply do this:
select (id/1000)+((id%1000)/100)+((id%100)/10)+(id%10)
If the ID field is varchar, just cast it to an int before division. Of course, if the result of this has more than 1 digit, you will not be able to get the sum of its digits again.
You could use this table-valued function:
CREATE FUNCTION [dbo].[Chars]
(
#Text NVARCHAR(MAX)
)
RETURNS #ItemTable TABLE (Item VARCHAR(250))
AS
BEGIN
DECLARE #i INT
DECLARE #Item NVARCHAR(4000)
SET #i = 1
WHILE (#i <= LEN(#Text))
BEGIN
INSERT INTO #ItemTable(Item)
VALUES(SUBSTRING(#Text, #i, 1))
SET #i = #i + 1
END
RETURN
END
Now this query should work as desired:
SELECT t.ID, SUM(CAST(Split.Item AS INT)) AS SumID
FROM dbo.TableName t
CROSS APPLY dbo.Chars(CONVERT(varchar(10), t.ID))Split
GROUP BY t.ID
Here's a demo: http://sqlfiddle.com/#!3/8eea7/8/0

SQL Query return values in a set sequence

I have been trying for a while now to return data from the database with the ID(int) values in the following order.
3, 6, 1, 9, 2, 5.
Is there anyway this can be done?
EDIT: Ok i made a bit of a stuff up in my post. the ID's above are just an example.
I am trying to do this dynamically, based around how many records from another table are linked to the record i want to pull out, e.g. i host 3 branches and each branch has a group of shops how would i determine which has the most?
I hope this helps.
Yes, something like this:
select ID from tablename
order by
CASE WHEN ID = 3 THEN 1
WHEN ID = 6 THEN 2
WHEN ID = 1 THEN 3
WHEN ID = 9 THEN 4
WHEN ID = 2 THEN 5
WHEN ID = 5 THEN 6
ELSE 7 END, ID ASC
This will put 3,6,1,9,2,5 and afterwords the other numbers in ascending order.
select cols from table where
order by
case ID when 3 then 0
when 6 then 1
when 1 then 2
when 9 then 3
...
end
You get the idea...
Create a table for the sorting.
CREATE TABLE SortPriority (
SourceID int NULL,
Priority int NULL)
Populate it with the ids and what order they should showup in. Join to the table. and use SortPriority.Priority in your sorting.
You can more easily change the sorting around this way. You would just need to modify the data. You can also later write scripts to populate the table to handle predictable needs in the changing of the sorting.
A split function like this one:
CREATE FUNCTION fnSplit(#str varchar(max), #dlm char(1))
RETURNS #result TABLE (id int, value varchar(50))
AS BEGIN
DECLARE
#id int, #value varchar(50),
#lastpos int, #pos int, #len int;
SET #id = 0;
SET #len = LEN(#str);
SET #lastpos = 1;
SET #pos = CHARINDEX(#dlm, #str + #dlm);
IF #pos <> 0
WHILE 1 = 1 BEGIN
SET #value = SUBSTRING(#str, #lastpos, #pos - #lastpos);
IF #value <> '' BEGIN
SET #id = #id + 1;
INSERT INTO #result VALUES (#id, #value);
END;
IF #pos > #len BREAK;
SET #lastpos = #pos + 1;
SET #pos = CHARINDEX(#dlm, #str + #dlm, #lastpos);
END;
RETURN;
END
would return a row set containing not only the values, but also their indexes within the list. You could then use the function in this way:
SELECT
…
FROM atable t
LEFT JOIN dbo.Split('3,6,1,9,2,5', ',') s ON t.Value = s.Value
ORDER BY
CASE WHEN s.id IS NULL THEN 2147483647 ELSE s.id END

Split string and return data in multiple columns

First of all many thanks to the site creator and most importantly helping guru's on this site.
I have the same problem splitting string from a field and displaying it in multiple columns example my table has got three columns
dbo.tests
Fname ID wTest Loc
ABC 1 "XYZ,PTO,LKMD,HGGFFD," R1
BCE 2 "PTO,XYZ,LKMD,," R1
LKJ 3 "XYZ" R3
JKL 4 "XYZ,PTO,LKMD,HGGFFD,PKL" R2
The output for the select statement should display the data as follows: (Dynamically generate number of columns based on maximum columns required from wTest string and fill the empty columsn with null or some value.
Returns:
Fname ID Loc wTest wTest1 wTest2,wTest3,Wtest4...
ABC 1 R1 XYZ PTO LKMD HGGFFD Null
BCE 2 R1 PTO XYZ LKMD Null Null
LKJ 3 R3 XYZ Null Null Null Null
JKL 4 R2 XYZ PTO LKMD HGGFFD PKL
Two close function I came accross are as follows:
CREATE FUNCTION dbo.Split (#sep char(1), #s varchar(512))
RETURNS table AS RETURN
(
WITH Pieces (pn, start, stop) AS
(
SELECT 1, 1, CHARINDEX(#sep, #s)
UNION ALL
SELECT pn + 1, stop + 1,
CHARINDEX(#sep, #s, stop + 1)
FROM Pieces
WHERE stop > 0 )
SELECT pn, SUBSTRING(#s, start, CASE WHEN stop > 0 THEN stop-start ELSE 512 END) AS s FROM Pieces )
with testTable AS
( SELECT 1 AS Id, N'how now brown cow' AS txt
UNION ALL
SELECT 2, N'she sells sea shells upon the sea shore' UNION ALL
SELECT 3, N'red lorry yellow lorry' UNION ALL
SELECT 4, N'the quick brown fox jumped over the lazy dog' )
SELECT display_term, COUNT(*) As Cnt
FROM testTable CROSS APPLY sys.dm_fts_parser('"' + txt + '"', 1033, 0,0)
GROUP BY display_term
HAVING COUNT(*) > 1 ORDER BY Cnt DESC
Any help in this regard is highly appreciated.
Zain...
zainali2006#hotmail.co.uk
Someone suggested this, but I am having difficulties applying for my purpose....
Returns #Tbl_IDs
Table (Id int identity(1,1),
Data Varchar(500)) As
Begin
--Remove the leading delimiter if any
while (substring(#IDs,1,1) =#Delimiter)
set #IDs = substring(#IDs, 2,len(#IDs)-1)
-- Append comma
--Set #IDs = #IDs + #Delimiter
set #IDs = REPLACE(RTRIM(LTRIM(REPLACE(#IDs,#Delimiter,' '))),' ',#Delimiter)
-- Indexes to keep the position of searching
Declare #Pos1 Int
Declare #pos2 Int
Declare #RowNum Int
-- Start from first character
Set #Pos1=1
Set #Pos2=1
While #Pos1>0
Begin
Set #Pos1 = CharIndex(#Delimiter,#IDs,#Pos1)
Insert #Tbl_IDs Values (Substring(#IDs,#Pos2,#Pos1-#Pos2))
-- Go to next non comma character
Set #Pos2=#Pos1+1
-- Search from the next charcater
Set #Pos1 = #Pos1+1
End
Return
End
Another one I came across quite interesting and simple but not sure how to use in my select statement:
DECLARE #NextString NVARCHAR(40)
DECLARE #Pos INT
DECLARE #NextPos INT
DECLARE #String NVARCHAR(40)
DECLARE #Delimiter NVARCHAR(40)
SET #String ='SQL,TUTORIALS,,TCF'
SET #Delimiter = ','
SET #String = #String + #Delimiter
SET #Pos = charindex(#Delimiter,#String)
WHILE (#pos <> 0)
BEGIN
SET #NextString = substring(#String,1,#Pos - 1)
SELECT #NextString -- Show Results
SET #String = substring(#String,#pos+1,len(#String))
SET #pos = charindex(#Delimiter,#String)
END