sql server full text search, replace, near and forms of - sql-server

Im getting trouble with a full text search query. First, my first try is not working because theres a syntax error close to replace (and I dont understand why) (if I do the replace outside the contains it works):
declare #what varchar(100) = 'word1 word2'
select *
from tablea
where
contains(column1, replace(#what, ' ', ' near '))
My second problem is about the NEAR and FORMS OF syntax. I tried:
'formsof(inflectional, "word1") near formsof(inflectional, "word2")'
But its not working. Theres a syntax error too, not in the replace thing, but in the query. Is it possible to use a replace over the #what variable (outside or inside the contains) to get the correct syntax?

Related

Using TSQL and XQuery to extract values from XML

I have a table with an XML column. Some of the XML is very large (8MB) but I'll present a simpler version of the problem here. Overall, I need to update the table and find those rows where the XML contains a node named <CompressedPart> at a known point in the XML tree, take its value, base64-decode it and replace <CompressedPart> with the resulting data.
This question is simply just the first part of that, which is trying to extract the text under a point in the XML tree. I've encountered XQuery once before and it just as life-destroying as it appears to be now.
To this end, I've simplified the XML to just two nodes thus:
<GovTalkMessage xmlns="http://www.govtalk.gov.uk/CM/envelope">
<EnvelopeVersion>2.0</EnvelopeVersion>
</GovTalkMessage>
and I'm simply trying to get the value "2.0". The code I'm using is:
SELECT CAST('<GovTalkMessage xmlns="http://www.govtalk.gov.uk/CM/envelope">
<EnvelopeVersion>2.0</EnvelopeVersion>
</GovTalkMessage>' AS XML).value('(/GovTalkMessage/EnvelopeVersion)[1]', 'VARCHAR(MAX)')
but this returns NULL. I've tried removing/adding forward slashes, removing the [1] (which gives the incredible un-useful error message "requires a singleton"). Whatever I specify in the XQuery I just get NULL or an error.
In time I will want to select across the whole table, as below, so I'm not just looking for a solution that works for a single XML variable in the FROM clause as I've seen in other examples. This type of thing:
SELECT GOVTALK_XML_INPUT_DATA.value('(/GovTalkMessage/EnvelopeVersion)[1]', 'VARCHAR(MAX)')
FROM dbo.IndividualSubmission
How do I go about querying to solve just this first part of my issue?
A couple ways..
DECLARE #X XML = '
<GovTalkMessage xmlns="http://www.govtalk.gov.uk/CM/envelope">
<EnvelopeVersion>2.0</EnvelopeVersion>
</GovTalkMessage>';
SELECT #X.value('(//*:EnvelopeVersion/text())[1]', 'varchar(20)');
Or..
DECLARE #X VARCHAR(1000) = '
<GovTalkMessage xmlns="http://www.govtalk.gov.uk/CM/envelope">
<EnvelopeVersion>2.0</EnvelopeVersion>
</GovTalkMessage>';
SELECT CAST(#X AS XML).value('(//*:EnvelopeVersion/text())[1]', 'varchar(20)');

SQL Server : display data/column from SELECT statement

I have a table with data that holds a bunch of HTML attributes.
For example: '<HTML><BODY></BODY></HTML>'
I would like to be able to write a SELECT statement that can grab those values, but display them modified.
So instead of displaying
'<HTML><BODY>DATA</BODY></HTML>'
I want to show:
'<HTML>
<BODY>DATA</BODY>
</HTML>'
Essentially, breaking out each one to a new line, based on finding a '>' value, without modifying the data.
I can't seem to find a way to do this. I tried looking into STRING_SPLIT, but I can't get that to apply from the SELECT part.
Any suggestions where I look?
Edit 2/22 - it appears REPLACE gets me further, but when reviewing this more, it may not be possible.
How would SQL know to break out to a new line when the ending HTML tag appears?
It's almost like I need to use a RegEx in here...
REPLACE(TD.DefDetails, '</', CHAR(13) + CHAR(10) + '</') As DefDetails
STRING_SPLIT can split a single column into multiple rows. STRING_SPLIT does require SQL Server Version 2016+. The following snippet does show an example for this scenario.
USE tempdb;
GO
DECLARE #HTMLString AS VARCHAR(100) = '<HTML><BODY></BODY></HTML>';
SELECT *
FROM STRING_SPLIT(#HTMLString,'>');
Actually appears this is done using REPLACE.
REPLACE(Table.DefDetails, '>', '>'+ CHAR(13) + CHAR(10)) As DefDetails
Demo on db<>fiddle
You can achieve it in this way
SELECT REPLACE('<HTML><BODY></BODY></HTML>', '>', '>'+ CHAR(13))
Or if you want to get the value into rows, you can do this way.
Select value + '>'
from STRING_SPLIT('<HTML><BODY></BODY></HTML>', '>')
where value <> ''
Output

Create wrapper with unknown data types

I'm trying to create a wrapper in T-SQL for a procedure where I'm not sure what the data types are. I can run the wrapper without an INSERT INTO statement and I get the data just fine, but I need to have it in a table.
Whenever I use the INSERT INTO I get an error:
Column name or number of supplied values does not match table definition
I've parsed back through my code and can't see where any column names don't match up, so I'm thinking that it has to be a data type. I've looked through the procedure I'm wrapping to see if I can find what the data types are, but some aren't defined there; I've referenced the tables they pull some data from to find the definitions; I've run SQL_VARIANT_PROPERTY on all of the data to see what data type it is (although some of them come up null).
Is there some better way for me to track down exactly where the error is?
I think you can find out your stored procedure result schema, using sp_describe_first_result_set (available from SQL2012) and FMTONLY. Something like this:
EXEC sp_describe_first_result_set
#tsql = N'SET FMTONLY OFF; EXEC yourProcedure <params are embedded here>'
More details can be found here.
However, if I remember correctly, this works only if your procedure used deterministic schemas (no SELECT INTO #tempTable or similar things).
One trick to find out the schema of your result is to actually materialize the result into ad-hoc created table. However, this is not easy since SELECT INTO does not work with EXEC procedure. One work-around is this:
1) Define a linked-server to the instance itself. E.g. loopback
2) Execute your procedure like this (for SQL 2008R2):
SELECT * INTO tempTableToHoldDataAndStructure
FROM OPENQUERY(' + #LoopBackServerName + ', ''set fmtonly off exec ' + #ProcedureFullName + ' ' + #ParamsStr
where
#LoopBackServerName = 'loopback'
#ProcedureFullName = loopback.database.schema.procedure_name
#ParamsStr = embedded parameters
For SQL2012 I think the execution might fail if RESULT SETS are not provided (i.e. schema definition of the expected result, which is kind of a chicken-egg problem in this case):
' WITH RESULT SETS (( ' + #ResultSetStr + '))'');
Okay, I have a solution to my problem. It's tedious, but tedious I can do. Randomly guessing is what drives me crazy. The procedure I'm wrapping dumps 51 columns. I already know I can get it to work without putting anything into a table. So I decided to comment out part of the select statement in the procedure I'm wrapping so it's only selecting 1 column. (First I made a copy of that procedure so I don't screw up the original; then I referenced the copy from my wrapper). Saved both, ran it, and it worked. So far so good. I could have done it line by line, but I'm more of a binary kind of guy, so I went about halfway down--now I'm including about 25 columns in both the select statement and my table--and it's still working. Repeat procedure until it doesn't work any more, then backtrack until it does again. My error was in identifying one of the data types followed by "IDENTITY". I'm not sure what will happen when I leave that out, but at least my wrapper works.

How can I pass a table name as a variable in SQL - Python 3.4

I am trying to write an SQL statement in python which passes a table name as a variable. However, I get the following error: Must declare the table variable "#P1".
pypyodbc.Programming Error: ('42000', '[42000]' [Miscrosoft] [SQL SERVER NATIVE CLIENT 10.0] [SQL SERVER] Must declare the table variable "#P1"
The code yielding the ERROR is:
query = cursor.execute('''SELECT * FROM ?''', (table_variable,))
I have other code where I pass variables to the SQL statement using the same syntax which works fine (code below works as intended).
query = cursor.execute('''SELECT column_name FROM information_schema.columns WHERE table_name = ?''', (table_variable,))
The error seems to occur when I am using a variable to pass a table name.
Any help resolving this error would be much appreciated.
With new comments from the OP this has changed rather significantly. If all you are trying to do is get a few rows of sample from each table you can easily leverage the sys.tables catalog view. This will create a select statement for every table in your database. If you have multiple schemas you could extend this to add the schema name too.
select 'select top 10 * from ' + QUOTENAME(t.name)
from sys.tables t
What you're trying to do is impossible. You can only pass values into queries as parameters - so
SELECT * FROM #Table
is banned but
SELECT * FROM TableName WHERE Column=#Value
is perfectly legal.
Now, as to why it's banned. From a logical point of view the database layer can't cache a query plan for what you're trying to do at all - the parameter will completely and utterly change where it goes and what returns - and can't guarantee in advance what it can or can't do. It's like trying to load an abstract source file at runtime and execute it - messy, unpredictable, unreliable and a potential security hole.
From a reliability point of view, please don't do
SELECT * FROM Table
either. It makes your code less readable because you can't see what's coming back where, but also less reliable because it could change without warning and break your application.
I know it can seem a long way round at first, but honestly - writing individual SELECT statements which specify the fields they actually want to bring back is a better way to do it. It'll also make your application run faster :-)
You can define a string variable:
table_var_str = 'Table_name'
st = 'SELECT * FROM ' + table_var_str
query = cursor.execute(st)
It will solve the problem.
You can also set the table_var_str as a list:
table_var_str = []
st = []
for i in range(N):
table_var_str.append = 'Table_name' + str(i)
st.append('SELECT * FROM ' + table_var_str[i])
for j in range(J):
query = cursor.execute(st[j])
If the query is very long, you should write them in a line instead of multi lines.

line breaks lost in sql server

I am entering error information into an ErrorLog table in my database. I have a utility class to do this:
ErrorHandler.Error("Something has broken!!\n\nDescription");
This works fine. However, when I try to access this table, the line breaks no longer seem to be present.
If I SELECT the table:
SELECT * from ErrorLog ORDER BY ErrorDate
there are no line breaks present in the log. This is kind of expected, as line breaks in one-line rows would break the formatting. However, If I copy the data out, the line break characters have been lost, and the data is all on one line.
How do I get line breaks in data at the end of my query when I put line breaks in? I don't know if the string has been stripped of line breaks when it enters the table, or if the viewer in SQL Server Management Studio has stripped out the line breaks.
The data type of the column into which error messages are put is nvarchar(Max), if that makes a difference.
EDIT: Unexpectedly, Pendri's solution didn't work.
Here is an excerpt of the string just before it passes into the SQL server:
POST /ipn/paymentResponse.ashx?installation=272&msgType=result HTTP/1.0\n\rContent-Length: 833\n\rContent-Type:
And here is the same string when I extract it from the grid viewer in SQL Server Management Studio:
POST /ipn/paymentResponse.ashx?installation=272&msgType=result HTTP/1.0 Content-Length: 833 Content-Type:
The place where the line break should be has been double spaced.
Any ideas?
No need to replace string input\output, you need just pick up correct option:
Tools -> Options...
> Query Results
> SQL Server
> Results to Grid
set "Retain CR\LF on copy or save" to true.
And don't forget to restart your management studio!
according Charles Gagnon answer
SSMS replaces linebreaks with spaces in the grid output. If you use Print to print the values (will go to your messages tab) then the carriage returns will be displayed there if they were stored with the data.
Example:
SELECT 'ABC' + CHAR(13) + CHAR(10) + 'DEF'
PRINT 'ABC' + CHAR(13) + CHAR(10) + 'DEF'
The first will display in a single cell in the grid without breaks, the second will print with a break to the messages pane.
A quick and easy way to print the values would be to select into a variable:
DECLARE #x varchar(100);
SELECT #x = 'ABC' + CHAR(13) + CHAR(10) + 'DEF';
PRINT #x;
Update a couple years later.
As described here, one solution to preserve viewing linebreaks in SSMS is to convert the output to XML:
SELECT * FROM (
SELECT * from ErrorLog ORDER BY ErrorDate
) AS [T(x)] FOR XML PATH
Fortunately, if you have SSMS 2012, this is no longer an issue, as line breaks are retained.
I echo David C's answer, except you should use the "TYPE" keyword so that you can click to open the data in a new window.
Note that any unsafe XML characters will not work well with either of our solutions.
Here is a proof of concept:
DECLARE #ErrorLog TABLE (ErrorText varchar(500), ErrorDate datetime);
INSERT INTO #ErrorLog (ErrorText, ErrorDate) VALUES
('This is a long string with a' + CHAR(13) + CHAR(10) + 'line break.', getdate()-1),
('Another long string with' + CHAR(13) + CHAR(10) + '<another!> line break.', getdate()-2);
SELECT
(
SELECT ErrorText AS '*'
FOR XML PATH(''), TYPE
) AS 'ErrorText',
ErrorDate
FROM #ErrorLog
ORDER BY ErrorDate;
I can confirm that the line breaks are preserved when copying out of a grid in SSMS 2012.
try using char(13) + char(10) instead of '\n' in your string (define a constant and concatenate to your sql)
Another simple solution is to click the "results to text" button in SSMS. Its not super clean, but gives you visibility to line breaks with about half a second of work.
For SQL Server 2008, there is no provision to set "Retain CR\LF on copy or save" to true.
For this issue, what I did is that, replace char(13) with "\r" and replace char(10) with "\n" like below.
REPLACE(REPlACE([COLUMN_NAME],char(13), '\r'),CHAR(10),'\n')
And in the code-behind again I've replaced "\r\n" with a break tag.
I worked out in this way as there was no option provided in SQL 2008 as mentioned above. This answer might be an alternative though.
Thanks

Resources