Сравните XML со строкой? - PullRequest
0 голосов
/ 11 февраля 2020

У меня есть таблица со столбцом XML. Большинство огромных строк содержат эти данные:

<X C="1"></X>

Я хотел бы найти любую строку среди миллионов, которые не соответствуют этому. Итак ...

select * 
from DataResults 
where cast(baserentamount as varchar(250)) not like '<X C="1"></X>'

Это возвращает каждую строку. Я предполагаю, что здесь есть что-то очень простое?

Обновление: возможно, мне следует изменить смысл этого вопроса - каков самый простой способ сделать запрос I хотите, IE, сопоставить запись в столбце XML в WHERE?

Ответы [ 4 ]

3 голосов
/ 11 февраля 2020

Вы должны посмотреть на фактическое XML в вашей таблице. Если вы вставите это xml в таблицу, это будет не та строка, о которой вы думаете.

declare @Something table(baserentamount xml)

insert @Something select '<X C="1"></X>'

select *
    , cast(baserentamount as varchar(250))
from @Something

Поэтому ваш запрос, скорее всего, будет возвращать каждую строку. И он обязательно вернет строки, которые вы хотите исключить, потому что строка xml изменилась.

0 голосов
/ 12 февраля 2020

Любой подход к выполнению этого на строковом уровне является неправильным (из-за различных XML макетов, которые семантически равны и, следовательно, возможны на любом этапе).

Попробуйте что-то подобное:

DECLARE @tbl TABLE (ID INT IDENTITY PRIMARY KEY, xmldata XML);
INSERT INTO @tbl (xmldata) VALUES
 (N'<X C="1"></X>')
,(N'<X A="huh?" C="1"></X>')
,(N'<X C="2"></X>')
,(N'<X C="1"></X><X C="2"></X>')
,(N'<Y C="1"></Y>')
,(N'<root>some other</root>')
;

SELECT t.*
      ,CASE WHEN t.xmldata.query(N'count(/*)>1').value('.','bit')=1 THEN 'X' END MoreThanOneNodeInLevel1
      ,CASE WHEN t.xmldata.query(N'count(/*[1]/@*)>1').value('.','bit')=1 THEN 'X' END MoreThanOnAttributeInFirstElement
      ,CASE WHEN t.xmldata.query(N'/X[1]/@C != "1"').value('.','bit')=1 THEN 'X' END Attribute_X_C_isNot1
      ,CASE WHEN t.xmldata.query(N'local-name(/*[1])!="X"').value('.','bit')=1 THEN 'X' END FirstElementIsNotNamedX
FROM @tbl t;

Результат

+----+-------------------------+-------------------------+-----------------------------------+----------------------+-------------------------+
| ID | xmldata                 | MoreThanOneNodeInLevel1 | MoreThanOnAttributeInFirstElement | Attribute_X_C_isNot1 | FirstElementIsNotNamedX |
+----+-------------------------+-------------------------+-----------------------------------+----------------------+-------------------------+
| 1  | <X C="1" />             | NULL                    | NULL                              | NULL                 | NULL                    |
+----+-------------------------+-------------------------+-----------------------------------+----------------------+-------------------------+
| 2  | <X A="huh?" C="1" />    | NULL                    | X                                 | NULL                 | NULL                    |
+----+-------------------------+-------------------------+-----------------------------------+----------------------+-------------------------+
| 3  | <X C="2" />             | NULL                    | NULL                              | X                    | NULL                    |
+----+-------------------------+-------------------------+-----------------------------------+----------------------+-------------------------+
| 4  | <X C="1" /><X C="2" />  | X                       | NULL                              | NULL                 | NULL                    |
+----+-------------------------+-------------------------+-----------------------------------+----------------------+-------------------------+
| 5  | <Y C="1" />             | NULL                    | NULL                              | NULL                 | X                       |
+----+-------------------------+-------------------------+-----------------------------------+----------------------+-------------------------+
| 6  | <root>some other</root> | NULL                    | NULL                              | NULL                 | X                       |
+----+-------------------------+-------------------------+-----------------------------------+----------------------+-------------------------+

Как видите, только первая строка NULL во всех столбцах.

Идея вкратце:

Ваш XML имеет - 1 единственный элемент на уровне 1 - Этот элемент имеет 1 единственный атрибут - "C" - значение атрибута в первом элементе = "1" - локальное имя первого элемента - "X" "

В примеры данных я добавил различные" неправильные "XML экземпляры.
Возможно, вам потребуется добавить больше правил.
Подсказка: вы можете поместить выражение в предложение WHERE или использовать XML.exist().

0 голосов
/ 12 февраля 2020

Чистый XML способ справиться с тем, что вам нужно, - создать крошечную XML схему. После этого вы можете проверить этот столбец XML.

SQL

USE tempdb;
GO

-- DDL and sample data population, start
IF EXISTS (SELECT * FROM sys.xml_schema_collections 
                    WHERE name = N'MySchema' 
                    AND schema_id = SCHEMA_ID(N'dbo'))
   DROP XML SCHEMA COLLECTION dbo.MySchema;

CREATE XML SCHEMA COLLECTION dbo.MySchema
    AS N'<?xml version="1.0"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <xsd:element name="X">
        <xsd:complexType>
            <xsd:attribute name="C" type="xsd:int" use="required" fixed="1"/>
        </xsd:complexType>
    </xsd:element>
</xsd:schema>'
GO

DECLARE @validationTbl TABLE (ID INT NOT NULL, Reason NVARCHAR(1024) NULL);

DECLARE @tbl TABLE (ID INT IDENTITY PRIMARY KEY, xmldata XML);
INSERT INTO @tbl (xmldata) VALUES
(N'<X C="1"></X>')          -- good
,(N'<X C="1"/>')            -- good
,(N'<X C="2"></X>')         -- bad
,(N'<root>Miami</root>');   -- ugly
-- DDL and sample data population, end

-- Method #1
DECLARE @ID INT, @XML AS XML(dbo.MySchema)
   , @RowCount INT = (SELECT COUNT(*) FROM @tbl);

WHILE @RowCount > 0
BEGIN
   BEGIN TRY
      SELECT @ID = ID, @XML = XMLData
      FROM @tbl
      ORDER BY ID DESC
      OFFSET @RowCount - 1 ROWS FETCH NEXT 1 ROWS ONLY;
   END TRY
   BEGIN CATCH
      INSERT INTO @validationTbl (ID, Reason)
      VALUES (@ID, ERROR_MESSAGE());  
   END CATCH

   SET @RowCount -= 1;
END;

-- test
SELECT * FROM @validationTbl;

-- Method #2
-- unfortunately, it stops at the very first error
-- TRY_CAST() shall swallow all XSD validation errors internally
-- and produce a NULL value for all invalid rows
/*
Validate Expressions (XQuery)
https://docs.microsoft.com/en-us/sql/xquery/validate-expressions-xquery?view=sql-server-ver15
https://docs.microsoft.com/en-us/sql/t-sql/functions/try-cast-transact-sql?view=sql-server-ver15
*/
SELECT TOP(4) *, TRY_CAST(xmldata AS XML(dbo.MySchema)) AS Result
FROM @tbl
ORDER BY ID;

Вывод

+----+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| ID |                                                                                     Reason                                                                                     |
+----+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
|  2 | XML Validation: Element or attribute 'C' was defined as fixed, the element value has to be equal to value of 'fixed' attribute specified in definition. Location: /*:X[1]/@*:C |
|  3 | XML Validation: Declaration not found for element 'root'. Location: /*:root[1]                                                                                                 |
+----+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
0 голосов
/ 11 февраля 2020

Аааа, ну вот, черт в деталях:

XML как представлено:

<X C="1" />

XML после CAST:

<X C="1"/>

Не совсем очевидно и не очевидно, почему это должно быть, но это так, это проблема WS.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...