SQL Server 2014: apply against xml nodes() function suddenly stops working - sql-server

I have a stored procedure that suddenly started returning NULL on only one of two supposedly equivalent SQL Server 2014 machines (primary and failover).
The specific operation that is suddenly misbehaving is:
declare #skus TABLE (intSkuId int, attributes xml);
insert into #skus
VALUES
(11443, '<attributes><style>basic_mug</style><color>white</color><size>15oz</size></attributes>'),
(11444, '<attributes><style>basic_mug</style><color>black</color><size>15oz</size></attributes>');
select
s.intSkuId, s.xmlAttributes,
att.query('.') as element
from
tb_Skus s
outer apply
xmlAttributes.nodes(N'//attributes/*') as atts(att)
where
s.intSkuId = 11443;
Note that this would normally be run against a segment of the tb_Skus table, not a single value, but I'm simplifying for debugging purposes.
This returns the following result:
intSkuId: 11443
xmlAttributes: <attributes><style>basic_mug</style><color>white</color><size>15oz</size></attributes>
element: NULL
Note also that the following script works as intended:
DECLARE #x xml;
SELECT #x = xmlAttributes
FROM tb_Skus s
WHERE s.intSkuId = 11443
SELECT #x AS xmlAttributes, att.query('.') AS element
FROM #x.nodes(N'//attributes/*') AS atts(att)
yielding the same value for xmlAttribute, but I get the expected values for element:
<style>basic_mug</style>
<color>white</color>
<size>15oz</size>
So, is there something wrong with the first query? And if not, are there session or database settings that could alter the behavior of this function?

There is no need to use .nodes() method in this case. It is needed just when there is a need to convert/shred XML data type into a rectangular/relational data.
Please try the following.
SQL
-- DDL and sample data population, start
DECLARE #tbl TABLE (intSkuId INT, xmlAttributes XML);
INSERT INTO #tbl (intSkuId, xmlAttributes) VALUES
(11443, N'<attributes>
<style>basic_mug</style>
<color>white</color>
<size>15oz</size>
</attributes>');
-- DDL and sample data population, end
SELECT *
, xmlAttributes.query('/attributes/*') AS [element]
FROM #tbl
where intSkuId=11443;

Related

SQL Server : how can I find the specific value from all stored procedures in my database?

How can I find the specific value from all stored procedures in my SQL Server database?
To be more specific, I want to know the value that is inserted into the specified column on the specified table.
For example:
A database has 3 stored procedures; dbo.sp_1, dbo.sp_2, dbo.sp_3
And that database also has a [Log] table which has a [number] column
dbo.sp_1
INSERT INTO [dbo].[Log] ([number])
VALUES (1);
dbo.sp_2
INSERT INTO [dbo].[Log] ([number])
VALUES (2);
dbo.sp_3
INSERT INTO [dbo].[Log] ([number])
VALUES (4);
So, the query results I expect are as follows:
I found a snippet that was used for a somewhat-similar task, however, I did not have to parse values. This may get you close if you really have to parse the sql. Sorry, the rest of the parsing will be left up to you
DECLARE #NonEscapeTextToFindLike NVARCHAR(MAX) = 'INSERTINTO\[dbo\].\[LOG\](\[number\])VALUES('
DECLARE #NonEscapeTextToFind NVARCHAR(MAX) = 'INSERTINTO[dbo].[LOG]([number])VALUES('
;WITH Procs AS
(
SELECT
StoredProcedure = name,
StoredProcedureText = OBJECT_DEFINITION(object_id),
NoBlankText = REPLACE(REPLACE(REPLACE(REPLACE(OBJECT_DEFINITION(object_id),' ',''),CHAR(9),''),CHAR(10),''),CHAR(13),'')
FROM
sys.procedures
)
SELECT
*,
StartOfPossibleInt = CHARINDEX(#NonEscapeTextToFind, Procs.NoBlankText) + LEN(#NonEscapeTextToFind)
FROM
Procs
WHERE
Procs.NoBlankText LIKE '%'+#NonEscapeTextToFindLike+'%' ESCAPE '\'

Pass test data to table-valued parameter within SQL

Is it possible, and if so how, to pass data to a table-valued parameter of a stored function using SQL EXEC?
I know how to pass in data from C#. One of my four stored procs using table-valued parameters is not producing the expected results. I'd like to execute my proc from SQL server management studio for debugging purposes, but I am unable to find the correct syntax for doing so, if such a syntax even exists. I haven't found anything relevant in the docs.
My type table:
CREATE TYPE [MyNameSpace].[MyTypeTable] AS TABLE(
//... all my fields
)
My stored proc:
//... bunch of stuff
ALTER PROCEDURE [MyNameSpace].[MyStoredProc]
#MyTypeTableVar MyTypeTable READONLY
AS
BEGIN
//Do a bunch of stuff
//Want to test the stuff in here
END
I have tried:
IF OBJECT_ID('tempdb.dbo.#MyTempTable') IS NOT NULL DROP TABLE tempdb.dbo.#MyTempTable;
select top 0 *
into #MyTempTable
//existing table with structure that matches the table-valued param
from MyNameSpace.MyTable;
//...Long insert statement assigning test data to #MyTempTable
EXECUTE MyNameSpace.MyStoredProc #MyTypeTableVar = #MyTempTable;
which throws:
Operand type clash: nvarchar is incompatible with MyTypeTable
You can't use a temp table - you have to use a table variable:
declare #t [MyNameSpace].[MyTypeTable]
insert into #t (/*columns*/) values
(/* first row */),
(/* second row */)
EXECUTE MyNameSpace.MyStoredProc #MyTypeTableVar = #t;
(You can populate it with either INSERT ... VALUES as shown above or INSERT ... SELECT if you have an existing table containing the data you care about)
Here's a working example:
-- Declare a table parameter
DECLARE #registryUpdates AS typ_KeyValuePairStringTable;
-- Insert one row
INSERT INTO #registryUpdates
VALUES ('Hello', 'World');
-- Call Stored Procedure
EXEC prc_UpdateRegistry #registryUpdates

How do I assign multiple values to a variable of the same column in sql server

How do I assign multiple values of the same column in a variable so that I can use the independent values in the programming. Is there any concept of an array in SQL server?
SELECT #x= Code FROM Security WHERE Department=#dpt;
This works correctly when code has single value, but when code has multiple values it provides errors.
There are no arrays in SQL Server.
There are table variables though which are similar in usage to temporary tables.
As described on MSDN:
table is a special data type that can be used to store a result set for processing at a later time. table is primarily used for temporary storage of a set of rows returned as the result set of a table-valued function. Functions and variables can be declared to be of type table. table variables can be used in functions, stored procedures, and batches.
You can use it this way:
DECLARE #x TABLE (Code int)
INSERT INTO #x
SELECT Code FROM Security WHERE Department=#dpt
Two ways I can think, depending on what you are planning to do...
declare #mytable table (Code Int)
Insert into #mytable
select Code from security where Department =#dpt.
declare #x nvarchar(max)
set #x = "";
select #x = cast(Code as Nvarchar(max)) + "," + #x from Security WHERE Department=#dpt;
now you have the data as comma seperated string, you can then further split it into a table like #1 above or send it out as string, which the program can consume.

TSQLT Returning Results from a Stored Procedure

In TSQLT, I'm trying to return a result from a stored procedure and add it to a variable so that I can assert if it matches my expected result.
I've seen loads of examples of returning results from functions but none where a stored procedure is called.
Does anybody have examples that they could share?
Thanks in advance
If you want to get a variable back from a stored procedure one way to do this is to use an out parameter
CREATE PROC MyTest
(#myVar int output)
AS
BEGIN
SET #myVar = 10
END
GO
DECLARE #x int
EXEC MyTest #myVar=#x output
SELECT #x
If you are getting a result set back from the stored procedure, here is an example from a tSQLt test that I wrote. I haven't bothered with the whole test because this should give you what you need.
CREATE TABLE #Actual (SortOrder int identity(1,1),LastName varchar(100), FirstName varchar(100), OrderDate datetime, TotalQuantity int)
-- Act
INSERT #Actual (LastName, FirstName, OrderDate, TotalQuantity)
EXEC Report_BulkBuyers #CurrentDate=#CurrentDate
The trick here is that you have to create the #actual table first. It should contain the same columns as what is returned from the stored procedure.
Just as an aside, you may have noticed I have a SortOrder column in the #actual table. This is because I was interested in testing the order of the data returned for this specific report. EXEC tSQLt.AssertEqualsTable will match rows like for like, but does not match the order in which the rows appear in the expected and actual so the way to ensure the order is to add a SortOrder column (which is an identity column) to both the #expected and #actual
Have a look here:
http://technet.microsoft.com/en-us/library/ms188655.aspx
Lots of examples about returning values from a stored procedure. At the bottom of the page there is also an example about evaluating a return code.
its actually really simple.
declare #variable int
exec #variable = _Stored_Procedure

How to pass multiple values in a single parameter for a stored procedures?

I want to pass multiple values in a single parameter. SQL Server 2005
You can have your sproc take an xml typed input variable, then unpack the elements and grab them. For example:
DECLARE #XMLData xml
DECLARE
#Code varchar(10),
#Description varchar(10)
SET #XMLData =
'
<SomeCollection>
<SomeItem>
<Code>ABCD1234</Code>
<Description>Widget</Description>
</SomeItem>
</SomeCollection>
'
SELECT
#Code = SomeItems.SomeItem.value('Code[1]', 'varchar(10)'),
#Description = SomeItems.SomeItem.value('Description[1]', 'varchar(100)')
FROM #XMLDATA.nodes('//SomeItem') SomeItems (SomeItem)
SELECT #Code AS Code, #Description AS Description
Result:
Code Description
========== ===========
ABCD1234 Widget
You can make a function:
ALTER FUNCTION [dbo].[CSVStringsToTable_fn] ( #array VARCHAR(8000) )
RETURNS #Table TABLE ( value VARCHAR(100) )
AS
BEGIN
DECLARE #separator_position INTEGER,
#array_value VARCHAR(8000)
SET #array = #array + ','
WHILE PATINDEX('%,%', #array) <> 0
BEGIN
SELECT #separator_position = PATINDEX('%,%', #array)
SELECT #array_value = LEFT(#array, #separator_position - 1)
INSERT #Table
VALUES ( #array_value )
SELECT #array = STUFF(#array, 1, #separator_position, '')
END
RETURN
END
and select from it:
DECLARE #LocationList VARCHAR(1000)
SET #LocationList = '1,32'
SELECT Locations
FROM table
WHERE LocationID IN ( SELECT CAST(value AS INT)
FROM dbo.CSVStringsToTable_fn(#LocationList) )
OR
SELECT Locations
FROM table loc
INNER JOIN dbo.CSVStringsToTable_fn(#LocationList) list
ON CAST(list.value AS INT) = loc.LocationID
Which is extremely helpful when you attempt to send a multi-value list from SSRS to a PROC.
Edited: to show that you may need to CAST - However be careful to control what is sent in the CSV list
Just to suggest. You can't really do so in SQL Server 2005. At least there is no a straightforward way. You have to use CSV or XML or Base 64 or JSON. However I strongly discourage you to do so since all of them are error prone and generate really big problems.
If you are capable to switch to SQL Server 2008 you can use Table valued parameters (Reference1, Reference2).
If you cannot I'd suggest you to consider the necessity of doing it in stored procedure, i.e. do you really want (should/must) to perform the sql action using SP. If you are solving a problem just use Ad hoc query. If you want to do so in education purposes, you might try don't even try the above mentioned things.
There are multiple ways you can achieve this, by:
Passing CSV list of strings as an argument to a (N)VARCHAR parameter, then parsing it inside your SP, check here.
Create a XML string first of all, then pass it as an XML datatype param. You will need to parse the XML inside the SP, you may need APPLY operator for this, check here.
Create a temp table outside the SP, insert the multiple values as multiple rows, no param needed here. Then inside the SP use the temp table, check here.
If you are in 2008 and above try TVPs (Table Valued Parameters) and pass them as params, check here.

Resources