I was intrigued as which one would perform better so I did a test and would like to share the results here.
I create a table to keep put some data in XML and for testing later on
USE MY_TEST_DATABASE
GO
--drop table radhe_01
--GO
CREATE TABLE RADHE_01 ( i int not null identity(1,1) primary key clustered,
MYXML XML NOT NULL
)
GO
Ops, the MYXML column should be able to accept nulls - need to change it
ALTER TABLE RADHE_01
ALTER COLUMN MYXML XML NULL
I create a procedure in order to create random values to be added to MYXML column
I want to add one million records to that table
CREATE PROCEDURE [dbo].[SpGenerateRandomString]
@sLength tinyint = 10,
@randomString varchar(50) OUTPUT
AS
BEGIN
SET NOCOUNT ON
DECLARE @counter tinyint
DECLARE @nextChar char(1)
SET @counter = 1
SET @randomString = ''
WHILE @counter <= @sLength
BEGIN
SELECT @nextChar = CHAR(48 + CONVERT(INT, (122-48+1)*RAND()))
IF ASCII(@nextChar) not in (58,59,60,61,62,63,64,91,92,93,94,95,96)
BEGIN
SELECT @randomString = @randomString + @nextChar
SET @counter = @counter + 1
END
END
END
I test the procedure to check it is working nicely
declare @randomString varchar(50)
exec SpGenerateRandomString 30, @randomString output
select @randomString
I insert one million rows into the table.
I kept my XML format as I have in the live procedure.
This XML has 2 values, I forgot about this detail and left the first value there GarmentComposition.
set nocount on
declare @randomString varchar(50)
declare @i int
select @i =1
while @i < 1000000 begin
exec SpGenerateRandomString 30, @randomString output
insert into radhe_01 (myxml) values (N'<BulletPoint><NoteTypeCode>GarmentComposition</NoteTypeCode><NoteTypeCode>' + @randomString + '</NoteTypeCode></BulletPoint>')
select @i = @i +1
end
I play with the data, in order to test the results later on,
I will change some rows to NULL and some rows to empty ''
SELECT COUNT(*) FROM
RADHE_01
WHERE I%117=0
--8547
SELECT COUNT(*) FROM
RADHE_01
WHERE I%1217=0
--821
SET NOCOUNT OFF
UPDATE RADHE_01
SET MYXML = ''
WHERE I%117=0
-- 8547 WILL BE EMPTY
UPDATE RADHE_01
SET MYXML = NULL
WHERE I%1217=0
-- 821 WILL BE NULL
SET NOCOUNT ON
--(8547 row(s) affected)
--(821 row(s) affected)
Now doing the test
I have run both queries as you can see on the script below.
set statistics io off
set statistics time off
DBCC FREEPROCCACHE
DBCC DROPCLEANBUFFERS
set statistics io on
set statistics time on
--find the number of records that are null or empty - using XML.exists
select t.*
from RADHE_01 t
where t.MYXML.exist('*') = 0 OR t.MYXML IS NULL
set statistics io off
set statistics time off
DBCC FREEPROCCACHE
DBCC DROPCLEANBUFFERS
set statistics io on
set statistics time on
--find the number of records that are null or empty - using datalength
select t.*
from RADHE_01 t
where DATALENGTH(t.MYXML) = 5 OR t.MYXML IS NULL
set statistics io oFF
set statistics time oFF
The result of the statistics TIME and statistics IO are as follows:
DBCC execution completed. If DBCC printed error messages, contact your system administrator.
DBCC execution completed. If DBCC printed error messages, contact your system administrator.
Table 'RADHE_01'. Scan count 1, logical reads 23902, physical reads 3, read-ahead reads 23445, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 2187 ms, elapsed time = 2646 ms.
SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 0 ms.
DBCC execution completed. If DBCC printed error messages, contact your system administrator.
DBCC execution completed. If DBCC printed error messages, contact your system administrator.
Table 'RADHE_01'. Scan count 5, logical reads 24132, physical reads 1, read-ahead reads 23477, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 391 ms, elapsed time = 1853 ms.
SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 0 ms.
This is the execution plan (picture) from sql server

This is the full XML execution plan of the query and picture above.
This is another picture using another tool to compare execution plans.

Conclusion:
Using DATALENGTH on this occasion, for this situation, has been proven quicker.
DBCCcommands commented out. Run multiple times. And, when not commented out, you can addWITH NO_INFOMSGSto the end of most (all?)DBCCcommands to prevent the "DBCC execution completed..." output. – Solomon Rutzky Aug 19 '16 at 16:14