Combining columns from the same sections of a form - sql

I was tasked to find out what is possible to be output to excel from forms that are filled out. What I am thinking to do is make one row for each form but the problem is the way the form is input in the database. This is how the the database looks when I try to output all columns from one of the sections(History of Present Illness) of one of the forms: https://imgur.com/a/QKf2XHY it looks like it contains other programming languages aside from sql as well. This what the form looks like originally when filled out: https://imgur.com/a/hBRPqDE
Right now I tried converting the Text columns to be formatted into varchar using a previous code I used before with the help of another user from here but it is not working correctly for this case.
SET ANSI_WARNINGS ON
Select A.IDEncounter_Note
,FormatMessage = ltrim(rtrim(replace(replace(replace(TxtOnly,' ','†‡'),'‡†',''),'†‡',' ')))
From Encounter_Note A
Cross Apply ( values ( --replace(
replace(
replace(
replace(
replace(
replace('<div>'+CONVERT(VARCHAR(MAX),Text)+'</div>'
,char(13),' ')
,char(10),' ')
,'&'+'nbsp;',' ')
,'&'+'amp;','&')
,'&'+'rsquo;','''')
--,'<o:p></o:p>','')-- May want to add items like — as - and ™ as TM etc...
)
) B(CleanString)
Cross Apply (
Select TxtOnly = stuff((Select ' ' +RetVal
From (
Select RetSeq
,RetVal = left(RetVal,charindex('<',RetVal+RetVal)-1)
From (
Select RetSeq = row_number() over (order by 1/0)
,RetVal = ltrim(rtrim(B.i.value('(./text())[1]', 'varchar(max)')))
From ( values (cast('<x>' + replace((Select replace(CleanString,'>','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.'))) as A(x)
Cross Apply x.nodes('x') AS B(i)
) C1
Where charindex('<',RetVal)>1
) C2
Order By RetSeq
For XML Path(''),TYPE).value('(./text())[1]','varchar(max)')
,1,1,'')
) C
WHERE EncounterID = 89617
What I am trying to do right now is make one column for each section of the form. For example Chief Complaint is one column, and History of Present Columns is another(So I have to combine the 'text' column from the 1st imgur).

Related

TSQL - Extract text between two words

I did find some info on the site but I am unable to make it work correctly. I have a text field [User] that contains USER: John.Smith SessionId: {There is a space after User: and one after the name}
Everything I tried will either remove the first section or the last one, none remove both. Or will give me this message Invalid length parameter passed to the LEFT or SUBSTRING function
I want to have the name John.Smith extracted from that field.
If possible I do not want to declare any tables.
Thanks
Why not use replace()?
select replace(replace(col, 'USER: ', ''), ' SessionId:', '')
If open to a TVF
Example
Select A.ID
,B.*
From YourTable A
Cross Apply [dbo].[tvf-Str-Extract](SomeCol,'USER:','SessionId:') B
Returns
ID RetSeq RetVal
1 1 John.Smith
The Function if Interested
CREATE FUNCTION [dbo].[tvf-Str-Extract] (#String varchar(max),#Delim1 varchar(100),#Delim2 varchar(100))
Returns Table
As
Return (
Select RetSeq = row_number() over (order by RetSeq)
,RetVal = left(RetVal,charindex(#Delim2,RetVal)-1)
From (
Select RetSeq = row_number() over (order by 1/0)
,RetVal = ltrim(rtrim(B.i.value('(./text())[1]', 'varchar(max)')))
From ( values (convert(xml,'<x>' + replace((Select replace(#String,#Delim1,'§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>').query('.'))) as A(XMLData)
Cross Apply XMLData.nodes('x') AS B(i)
) C1
Where charindex(#Delim2,RetVal)>1
)
/*
Declare #String varchar(max) = 'Dear [[FirstName]] [[LastName]], ...'
Select * From [dbo].[tvf-Str-Extract] (#String,'[[',']]')
*/
I got SUBSTRING() to work:
SUBSTRING(USER, 7,(LEN(USER)-7)-(charindex('SessionId',USERID)))
Where:
7 = # of characters in "USERID:"
LEN(User)-7 counts the character length less the 7 from "USERID:"
charindex('SessionId',USERID) gives you the character location where "SessionId" starts

Remove HTML tags in SQL Server

I have to output, using a select statement, a column as a varchar but even though I convert the column to varchar it still outputs the html tags which I don't need the formatting/only the text is required. A big problem as well is I have no permission to create functions, even though I have seen several functions in this site that lets me create a function to fix this issue. I have limited permission to adding to our database but I have tried using temporary tables and CTE and I have permission to do those.
Right now I have used
REPLACE(REPLACE(CONVERT(VARCHAR(8000),Message), CHAR(13),''), CHAR(10),'')
to try and convert the column from html to varchar and also remove the line breaks. It removes the line breaks which we don't want as well but the HTML tags such as </font>,</o:p>,</p>,<br/>,&nbsp, etc... are still showing up when I output to excel.
This is a test output im getting from the column:
<br /><h1><span style="font-size: x-small"><span style="font-family: Verdana"><u><i><b>test</b></i></u></span></span><u><i><b><br /></b></i></u></h1><div contenteditable="false">George C Test2 </div><br /><br />
The desired output:
test George C Test2
It is doable without the creation of a Table-Valued Function but is NOT pretty
Cross Apply B will "Clean the String" this may require some maintenance.
Cross Apply C will Split/Parse the String, reduce it down to values between > ... < and then aggregate the string.
You may notice that char(13) and char(10) are NOT removed, but replaced with a space. This is to ensure the replace is not destructive.
Example
Declare #YourTable table (ID int, [Message] varchar(max))
Insert Into #YourTable values
(1,'<br /><h1><span style="font-size: x-small"><span style="font-family: Verdana"><u><i><b>test</b></i></u></span></span><u><i><b><br /></b></i></u></h1><div contenteditable="false">George C Test2 </div><br /><br />')
,(2,'<div><h1>This is the Title</hr><br >The content<br >Extra Spaces will be trapped. The End</div>')
,(3,'No HTML Tags')
Select A.id
,NewValue = ltrim(rtrim(replace(replace(replace(TxtOnly,' ','†‡'),'‡†',''),'†‡',' ')))
From #YourTable A
Cross Apply ( values ( replace(
replace(
replace('<fake>'+[Message]+'</fake>'
,char(13),' ')
,char(10),' ')
,' ',' ') -- May want to add items like — as - and ™ as TM etc...
)
) B(CleanString)
Cross Apply (
Select TxtOnly = stuff((Select ' ' +RetVal
From (
Select RetSeq
,RetVal = left(RetVal,charindex('<',RetVal+RetVal)-1)
From (
Select RetSeq = row_number() over (order by 1/0)
,RetVal = ltrim(rtrim(B.i.value('(./text())[1]', 'varchar(max)')))
From ( values (cast('<x>' + replace((Select replace(CleanString,'>','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.'))) as A(x)
Cross Apply x.nodes('x') AS B(i)
) C1
Where charindex('<',RetVal)>1
) C2
Order By RetSeq
For XML Path(''),TYPE).value('(./text())[1]','varchar(max)')
,1,1,'')
) C
Returns
id NewValue
1 test George C Test2
2 This is the Title Extra Spaces will be trapped. The End
3 No HTML Tags

SQL Server: Find words in string that don't exist in dictionary

Consider the following tables:
DROP TABLE IF EXISTS ##tableA;
CREATE TABLE ##tableA (id int,keywords VARCHAR(MAX));
INSERT INTO ##tableA (id,keywords) VALUES
(1,'apple,orange,potato'),
(2,'I typed a sentence here because I can''t follow directions.'),
(3,'potato and apple');
DROP TABLE IF EXISTS ##dictionary;
CREATE TABLE ##dictionary (id int,keyword VARCHAR(255));
INSERT INTO ##dictionary (id,keyword) VALUES
(1,'apple'),
(2,'orange'),
(3,'lemon'),
(4,'potato');
We have users entering keywords into the keyword column in tableA. I want return the id of any record that contains a word not in ##dictionary.
In the case above:
- id 1 would not be returned because each comma separated keyword is found in the dictionary
- id 2 would be returned because it contains words that are not in the dictionary
- id 3 would be returned because it contains the word "and", which is not in the dictionary
The ideal situation I think would somehow break up the keywords column from ##tableA into individual keywords, then check each of them against the keyword column in ##dictionary.
Here is an inline approach
Example
Select Distinct A.*
From ##tableA A
Cross Apply (
Select RetSeq = Row_Number() over (Order By (Select null))
,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
From (Select x = Cast('<x>' + replace((Select replace(replace(A.KeyWords,',',' '),' ','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.')) as A
Cross Apply x.nodes('x') AS B(i)
) B
Left Join ##dictionary C on B.RetVal=C.keyword
Where C.keyWord is null
Returns
id keywords
2 I typed a sentence here because I can't follow directions.
3 potato and apple
Just another BRUTE FORCE OPTION - Just for fun
Declare #S varchar(max) = (Select * From ##tableA For XML Raw )
Select #S = replace(#S,keyword,'') From ##dictionary
Select id = B.i.value('#id', 'int')
From (Select x = Cast(#S as xml).query('.')) as A
Cross Apply x.nodes('row') AS B(i)
Where B.i.value('#keywords', 'varchar(max)') like '%[a-z]%'
Under SQL Server 2017, you can use STRING_SPLIT:
SELECT
id
FROM
##tableA
CROSS APPLY STRING_SPLIT(keywords, ' ') splitBySpace
CROSS APPLY STRING_SPLIT(splitBySpace.value, ',') splitBySpaceOrComma
WHERE
splitBySpaceOrComma.value NOT IN (SELECT keyword FROM ##dictionary)
GROUP BY
id;
Using:
Splitter
you can split lines by delimiter then use the result to match against the dictionary. like this:
SELECT t.keywords FROM ##tablea t
CROSS APPLY (SELECT REPLACE(t.keywords, ' and ', ',')) new(kwds)
CROSS APPLY dbo.DelimitedSplit8K(new.kwds, ',') s
WHERE s.item NOT IN (SELECT keyword FROM ##dictionary)
Try this:
select t.*
from ##tableA t
cross join (
select max(case when id = 1 then keyword end) firstKeyword,
max(case when id = 2 then keyword end) secondKeyword,
max(case when id = 3 then keyword end) thirdKeyword,
max(case when id = 4 then keyword end) fourthKeyword
from ##dictionary
) d where
len(replace(replace(replace(replace(replace(replace(keywords, firstKeyword, ''), secondKeyword, ''), thirdKeyword, ''), fourthKeyword, ''), ' ', ''), ',', '')) > 0
First, you need to pivot your data from ##dictionary, then you can replace your keywords with '' as well as spaces and commas, and see in the end if the are any characters left.

SQL replace/ remove multiple date and time stamp from any part of a string

I have a nvarchar field that contains multiple date & time stamps and various text. The date and time can be at any position in the field.
I want to select only the text from the field. I have tried with REPLACE and PATINDEX to no avail.
Please can anyone share how i would write my select on this example notes field which contains this string:
ADMIN1 21/04/2017 02:01:01 This student is here and trying to gain a masters.
ITSYS2 09/05/2017 03:51:04 60 APL Credits on xout
The following will exclude dates and times from the note_detail. This is an in-line approach, but just about any split/parse function will do the trick as well.
Example
Declare #YourTable table(studend_id int,note_detail varchar(max))
Insert Into #YourTable values
(1,'CHIDLOL 21/04/2017 02:01:01 '+CHAR(13)+CHAR(10)+'This studend is here and trying to gain a masters. THOMASXC 09/05/2014 03:54:04 60 APL Credon on xout')
Select A.studend_id
,new_note_detail = B.S
From #YourTable A
Cross Apply (
Select S = Stuff((Select ' ' +RetVal
From (
Select RetSeq = Row_Number() over (Order By (Select null))
,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
From (Select x = Cast('<x>' + replace((Select replace(replace(replace(A.note_detail,char(13),' '),char(10),' '),' ','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.')) as A
Cross Apply x.nodes('x') AS B(i)
) B1
Where RetVal not like '%[0-9]/[0-9][0-9]/[0-9]%'
and RetVal not like '%[0-9]:[0-9][0-9]:[0-9]%'
Order by RetSeq
For XML Path ('')),1,1,'')
) B
Returns
studend_id new_note_detail
1 CHIDLOL This studend is here and trying to gain a masters. THOMASXC 60 APL Credon on xout
Edit - Option 2 with a Parse Function
Select A.studend_id
,new_note_detail = B.S
From #YourTable A
Cross Apply (
Select S = Stuff((Select ' ' +RetVal
From [dbo].[udf-Str-Parse](replace(replace(A.note_detail,char(13),' '),char(10),' '),' ') B1
Where RetVal not like '%[0-9]/[0-9][0-9]/[0-9]%'
and RetVal not like '%[0-9]:[0-9][0-9]:[0-9]%'
Order by RetSeq
For XML Path ('')),1,1,'')
) B
The UDF if Interested
CREATE FUNCTION [dbo].[udf-Str-Parse] (#String varchar(max),#Delimiter varchar(10))
Returns Table
As
Return (
Select RetSeq = Row_Number() over (Order By (Select null))
,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
From (Select x = Cast('<x>' + replace((Select replace(#String,#Delimiter,'§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.')) as A
Cross Apply x.nodes('x') AS B(i)
);
--Thanks Shnugo for making this XML safe
--Select * from [dbo].[udf-Str-Parse]('Dog,Cat,House,Car',',')
--Select * from [dbo].[udf-Str-Parse]('John Cappelletti was here',' ')
--Select * from [dbo].[udf-Str-Parse]('this,is,<test>,for,< & >',',')

How to sort the words of a single cell in an SQL table?

For example:
Pillars 101 in an apartment
Zuzu Durga International Hotel
Wyndham Garden Fresh Meadows
Need to sort the above as,
101 an apartment in Pillars
Durga Hotel International Zuzu
Fresh Garden Meadows Wyndham
Try this:
DECLARE #tbl TABLE(YourString VARCHAR(100));
INSERT INTO #tbl VALUES
('Pillars 101 in an apartment')
,('Zuzu Durga International Hotel')
,('Wyndham Garden Fresh Meadows');
SELECT CAST('<x>' + REPLACE((SELECT YourString AS [*] FOR XML PATH('')),' ','</x><x>') + '</x>' AS XML).query
('
for $x in /x
order by $x
return
concat($x/text()[1], " ")
').value('.','varchar(max)')
FROM #tbl;
The code will frist transfer your text in an XML like <x>Pillars</x><x>101</x> ....
Then a FLWOR XQuery is used to return the text parts sorted.
The last call to .value() will return the sorted fragments as text again.
The result
101 Pillars an apartment in
Durga Hotel International Zuzu
Fresh Garden Meadows Wyndham
Final statement
This code is kind of an exercise. Your design is really bad and should be changed...
So there's nothing that you can do natively. If you want to sort the values just as a return value, i.e. not update the database itself, you can transform the results with either a stored procedure or perhaps a view.
So let's construct an answer.
Let's just assume you want to do it visually, for a single row. If you have SQL 2016 you can use STRING_SPLIT but SQL Fiddle doesn't, so I used a common UDF fnSplitString
http://sqlfiddle.com/#!6/7194d/2
SELECT value
FROM fnSplitString('Pillars 101 in an apartment', ' ')
WHERE RTRIM(value) <> '';
That gives me each word, split out. What about ordering it?
SELECT value
FROM fnSplitString('Pillars 101 in an apartment', ' ')
WHERE RTRIM(value) <> ''
ORDER BY value;
And if I want to do it for each row in the DB table I have? http://sqlfiddle.com/#!6/7194d/8
SELECT split.value
FROM [Data] d
CROSS APPLY dbo.fnSplitString(IsNull(d.Value,''), ' ') AS split
WHERE RTRIM(split.value) <> ''
ORDER BY value;
That's sort of helpful, except now all my words are jumbled. Let's go back to our original query and identify each row. Each row probably has an Identity column on it. If so, you've got your grouping there. If not, you can use ROW_NUMBER, such as:
SELECT
ROW_NUMBER() OVER(ORDER BY d.Value) AS [Identity] -- here, use identity instead of row_number
, d.Value
FROM [Data] d
If we then use this query as a subquery in our select, we get:
http://sqlfiddle.com/#!6/7194d/21
SELECT d.[Identity], split.value
FROM
(
SELECT
ROW_NUMBER() OVER(ORDER BY d.Value) AS [Identity] -- here, use identity instead of row_number
, d.Value
FROM [Data] d
) d
CROSS APPLY dbo.fnSplitString(IsNull(d.Value,''), ' ') AS split
WHERE RTRIM(split.value) <> ''
ORDER BY d.[Identity], value;
This query now sorts all rows within each identity. But now you need to reconstruct those individual words back into a single string, right? For that, you can use STUFF. In my example I use a CTE because of SQL Fiddle limitations but you could use a temp table, too.
WITH tempData AS (
SELECT d.[Identity], split.value
FROM
(
SELECT
ROW_NUMBER() OVER(ORDER BY d.Value) AS [Identity] -- here, use identity instead of row_number
, d.Value
FROM [Data] d
) d
CROSS APPLY dbo.fnSplitString(IsNull(d.Value,''), ' ') AS split
WHERE RTRIM(split.value) <> ''
)
SELECT grp.[Identity]
, STUFF((SELECT N' ' + [Value] FROM tempData WHERE [Identity] = grp.[Identity] ORDER BY Value FOR XML PATH(N''))
, 1, 1, N'')
FROM (SELECT DISTINCT [Identity] FROM tempData) AS grp
Here's the end result fiddle: http://sqlfiddle.com/#!6/7194d/27
As expressed in comments already, this is not a common case for SQL. It's an unnecessary burden on the server. I would recommend pulling data out of SQL and sorting it through your programming language of choice; or making sure it's sorted as you insert it into the DB. I went through the exercise because I had a few minutes to kill :)
Already +1 on Shnugo's solution. I actually watch for his posts.
Just another option use a parse UDF in concert with a Cross Apply.
Example
Select B.*
From YourTable A
Cross Apply ( Select Sorted=Stuff((Select ' ' +RetVal From [dbo].[tvf-Str-Parse](A.SomeCol,' ') Order By RetVal For XML Path ('')),1,1,'') )B
Returns
Sorted
101 an apartment in Pillars
Durga Hotel International Zuzu
Fresh Garden Meadows Wyndham
The UDF if Interested
CREATE FUNCTION [dbo].[tvf-Str-Parse] (#String varchar(max),#Delimiter varchar(10))
Returns Table
As
Return (
Select RetSeq = Row_Number() over (Order By (Select null))
,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
From (Select x = Cast('<x>' + replace((Select replace(#String,#Delimiter,'§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.')) as A
Cross Apply x.nodes('x') AS B(i)
);
--Thanks Shnugo for making this XML safe
--Select * from [dbo].[tvf-Str-Parse]('Dog,Cat,House,Car',',')