Feeds:
Posts
Comments

Archive for the ‘TSQL’ Category

Interesting error today:

When we query over linked server, we run into some restrictions. One such restrictions is this, where we could not query XML columns over LinkedServer connection.

For example, if the remote server table has a XML column in its structure, when we query the remote server for this table, we run this error:

--
-- Old query that throws this error
--
SELECT ID, XMLColumn
FROM LinkedServer01.DatabaseName.dbo.TableName
GO
Error : Msg 9514, Level 16, State 1, Line 1 
Xml data type is not supported in distributed queries. 
Remote object has xml column(s)

Resolution:

Not sure why this limitation exists, but there is a workaround: OPENQUERY

In the past, we’ve covered some key benefits of OPENQUERY and how it allows us to circumvent some limitations of remote-table-value function calls. Today, we’ll see another benefit of OPENQUERY in circumventing the XML limitation of linked servers.

Rather than querying XML directly, we convert XML into NVARCHAR(MAX) and revert it back to XML once the data is on local server.

--
-- Modified query to circumvent the limitation
--
SELECT ID, CONVERT(XML, XMLColumn) AS XMLcolumn
FROM OPENQUERY(LinkedServer01,
	'SELECT ID
		 , CONVERT(NVARCHAR(MAX), XMLColumn) AS XMLColumn
	FROM DatabaseName.dbo.TableName') AS RemoteQuery
GO

The XML data is retrieved as NVARCHAR(MAX) to the local machine; And immediately converted to XML before displaying.

 

Hope this helps,
_Sqltimes
Advertisements

Read Full Post »

Interesting Error today:

While playing with XML DML, ran into this cryptic error.

Msg 2207, Level 16, State 1, Line 89
XQuery [dbo.Table.XMLColumn.modify()]: Only non-document nodes 
can be inserted. Found "xs:string ?".

 

While INSERTing a new XML element into an existing XML column, using the below code, this error pops up:

 

DECLARE @NewXMLElement VARCHAR(50) = 'Test'

UPDATE dbo.TableName
SET XMLColumn.modify('
		insert sql:variable("@NewXMLElement")
		into (/Parent/Child)[1]
	')
WHERE XMLColumn.exist('(/Parent/Child/NewXMLElement/text())') = 0
GO

Resolution:

Here, the goal is to INSERT a XML snippet into an existing XML column value. So, it is important that in the modify() function, the new XML element variable is defined as XML datatype

DECLARE @NewXMLElement XML = 'Test'

UPDATE dbo.TableName
SET XMLColumn.modify('
		insert sql:variable("@NewXMLElement")
		into (/Parent/Child)[1]
	')
WHERE XMLColumn.exist('(/Parent/Child/NewXMLElement/text())') = 0
GO

 

Hope this helps,
_Sqltimes

 

Read Full Post »

Interesting one today:

In the past, we’ve seen multiple posts on XML DML covering several XML manipulation capabilities. Today, we’ll cover another aspect of XQuery : Ability to add multiple conditions within the same XQuery path.

--
-- Create new sample table
--
CREATE TABLE dbo.SampleXML (
ID INT NOT NULL IDENTITY(1,1)
, XML_Col XML
)
GO

--
-- Create XML value
--
DECLARE   @X XML = '

				Marco
				Polo

				Hello 16
				World 16

				Hello 17
				World 17

				Hello

'
--
-- INSERT XML value into table column
--
INSERT INTO dbo.SampleXML (XML_Col) VALUES (@X)
GO

--
-- See the result
--
SELECT *
FROM dbo.SampleXML
GO

The sample XMl looks like this:

XMLDML_MultipleConditions

And in it we’ll be querying the following things:

  1. Using Single Condition
    1. Query the number of ParentElement under Root
    2. Query the number of ParentElement with @Att1 = “Color”
      1. Answer: 1
    3. Query the number of ParentElement with @Att1 = “Brand”
      1. Answer: 2
    4. Retrieve element text of ParentElement with @Att1 = “Brand” for Child1
      1. Answer: Hello 16
  2. Using Two conditions
    1. Retrieve element text of ParentElement with @Att1 = “Brand” and @Att2=”17″ for Child1
      1. Answer: Hello 17

XMLDML_MultipleConditions_Detailed.png

--
-- XML Query to retrieve with multiple conditions in XQuery<span id="mce_SELREST_start" style="overflow:hidden;line-height:0;"></span>
--
SELECT	  XML_Col.value('count(/Root/ParentElement)', 'INT')													AS [NumberOf_ParentElements]
		, XML_Col.value('count(/Root/ParentElement[@Att1="Color"])', 'INT')										AS [NumberOf_ParentElements_Color_Attribute]
		, XML_Col.value('count(/Root/ParentElement[@Att1="Brand"])', 'INT')										AS [NumberOf_ParentElements_Brand_Attribute]

		, XML_Col.value('(/Root/ParentElement[@Att1="Brand"]/Child1/text())[1]', 'VARCHAR(30)')					AS [Child1_Text_in_Brand]
		, XML_Col.value('(/Root/ParentElement[@Att1="Brand" and @Att2="17"]/Child1/text())[1]', 'VARCHAR(30)')	AS [Child1_Text_in_Brand_and_17]

FROM dbo.SampleXML
GO

Output looks like this:

XMLDML_MultipleConditions_Output

 

Hope this helps,
_Sqltimes

 

Read Full Post »

Quick one today

Once in a while, we need to process XML files (a.k.a. deadlock XML files) to retrieve some pertinent information to uncover the deadlock patterns.

In the past, we’ve seen some XML DML queries to parse XML files.

Here we’ll focus on filtering capability based on attribute values in elements:

Take an deadlock XML for example:

Deadlock_Filter_Attribute

The goal is to search for the action element with attribute value as “collect_system_time“.

--
-- Parse & Filter XMl file data
--
SELECT
	  ROW_NUMBER() OVER(ORDER BY DeadLockXML.value('(/event/@timestamp)[1]', 'DATETIME2'))											AS [RowN]
	, DeadLockXML.value('(/event/action[@name="collect_system_time"]/value)[1]', 'DATETIME') AS SystemTime
FROM  dbo.Deadlocks_07262018_Step2
GO

The [] allow us to provide the value to filter the XMl elements:

(/event/action[@name="collect_system_time"]/value)[1]'

 

Hope this helps,
_Sqltimes

 

Read Full Post »

Quick one today:

Quite often, we all run into this scenario where we need to convert column data into row with either comma or some other delimiter.

Below, we’ll see a couple of ways; Each serving a different purpose:

Build data set first:

--
--  Convert row data into column (without using PIVOT)
--

-- Create tables
CREATE TABLE Parent (
	  ID	INT			IDENTITY(1,1)
	, Name	VARCHAR(20)
)

CREATE TABLE Child (
	  ID		INT		IDENTITY(1,1)
	, ParentID	INT
	, Name		VARCHAR(25)
)

-- Create Parent records
INSERT INTO Parent (Name) VALUES ('Mary')
INSERT INTO Parent (Name) VALUES ('Patel')
INSERT INTO Parent (Name) VALUES ('Risvic')

-- Create child records
INSERT INTO Child (ParentID, Name) values (1, 'Jil')
INSERT INTO Child (ParentID, Name) values (1, 'Jim')
INSERT INTO Child (ParentID, Name) values (1, 'Kat')

INSERT INTO Child (ParentID, Name) values (2, 'Raja')
INSERT INTO Child (ParentID, Name) values (2, 'Kamat')
GO

SELECT * FROM Parent
SELECT * FROM Child
GO

Data set looks like this:

RowToColumn_DataSet

Option 1 : Using Variable

This method gives us result in comma delimited fashion, but it only works for one parent at a time; And we cannot combine Parent name as a column next to Children column, as Sql Server does not allow combining data-retrieval with variable manipulation query.

It spits out an error like this:

Msg 141, Level 15, State 1, Line 35
A SELECT statement that assigns a value to a variable must not be 
combined with data-retrieval operations.
--
--	Option 1 : using @Variable
--
DECLARE @Children VARCHAR(8000)

SELECT @Children = COALESCE(@Children, '') + ', ' + Name
FROM dbo.Child
WHERE ParentID = 1

SELECT STUFF(@Children, 1, 2, '')
GO

RowToColumn_Option1

Option 2 : Using XML

XML PATH gives us more control over retrieval manipulation. It allows us to retrieve data in a tabular fashion, while keeping the children names into one column.

Breakdown:

This below query has 3 parts:

  1. Main warp-around query (Parent)
  2. STUFF section
  3. XML PATH section
--
--	Using XML
--
SELECT    P.ID	AS [ParentID]
	, P.Name	AS [ParentName]
	, STUFF(
				(
					SELECT ', ' + C.Name
					FROM dbo.Child AS C
					WHERE C.ParentID = P.ID
					FOR XML PATH('')
				)
				, 1, 2, ''
		  ) AS [Children]
FROM dbo.Parent AS P
GO

--
-- Clean up
--
DROP TABLE Parent
DROP TABLE Child
GO

RowToColumn_Option2

We’ll see the details of each query in reverse order:

XML PATH Query

This is the most important part of this whole query. It allows us to retrieve all children of a given parent in a comma delimited fashion.

Without FOR XML PATH(”) statement, this is just a query that returns children as rows. When you add FOR XML, that data set is now converted into XML format (sort of).

PATH (”) makes sure that the the element trees notation is replaced with whatever is in between ” (empty). So we get a simple comma delimited child list.

Now we have an extra comma to remove. That’s where STUFF comes into play.

STUFF query section replaces comma with empty space.

Main wrap-around query is to bring this all together with ParentID & Parent Name to make it more usable.

 

Hope this helps,
_Sqltimes

 

Read Full Post »

Quick one today:

Extended Events make it easy to capture deadlock details into an XEL file, that has deadlock data in XML format.

This XEL file could be imported into a Sql table using sys.fn_xe_file_target_read_file function; It is similar to ::fn_trace_gettable function to load Sql trace file into a table.


SELECT * INTO dbo.DeadlocksXML
FROM sys.fn_xe_file_target_read_file ('I:\Deadlocks\xml_deadlock_report_0_131771103583570000.xel', null, null, null)
GO

MSDN also has an article on this function.

Hope this helps,
_Sqltimes

 

Read Full Post »

Quick one today:

Earlier, while troubleshooting deadlocks, we needed a quick way to count the number of victims & processes in each deadlock incident. Since deadlock information is available in XML, we could use COUNT function to measure the occurrences.

Deadlock_Counts

In the past, we’ve covered some techniques to uncover details from deadlock XML data.

XML provides many tools to parse XML files. Below query makes it easy to gather such counts at each level; Be it Victim count or processes count, etc

SELECT
	, DeadLockXML.value('count(/event/data/value/deadlock/victim-list/victimProcess)', 'INT') AS [Count_Victims]
FROM dbo.DeadlocksXMLTable
GO

We could send XML Path we want to count as a parameter to COUNT function to gather the number.

DeadLockXML.value(‘count(/event/data/value/deadlock/victim-list/victimProcess)’, ‘INT‘)
Below is an expanded TSQL query that includes more columns from deadlock XML file.

SELECT
	  ROW_NUMBER() OVER(ORDER BY DeadLockXML.value('(/event/@timestamp)[1]', 'DATETIME2'))											AS [RowN]
	, DeadLockXML.value('(/event/@timestamp)[1]', 'DATETIME2')																		AS [TimeStamp]
	, DeadLockXML.value('count(/event/data/value/deadlock/victim-list/victimProcess)', 'INT')										AS [Count_Victims]
	, DeadLockXML.value('(/event/data/value/deadlock/victim-list/victimProcess/@id)[1]', 'VARCHAR(100)')							AS [Victim1]
	, DeadLockXML.value('(/event/data/value/deadlock/victim-list/victimProcess/@id)[2]', 'VARCHAR(100)')							AS [Victim2]
	, DeadLockXML.value('(/event/data/value/deadlock/victim-list/victimProcess/@id)[3]', 'VARCHAR(100)')							AS [Victim3]
	, DeadLockXML.value('(/event/data/value/deadlock/victim-list/victimProcess/@id)[4]', 'VARCHAR(100)')							AS [Victim4]
FROM dbo.DeadlocksXMLTable
GO

 

Hope this helps,
_Sqltimes

Read Full Post »

Older Posts »