Извлечь элемент из XML с помощью SQL - PullRequest
0 голосов
/ 25 апреля 2019

У меня была ситуация

  • План выполнения XML, сохраненный в таблице

  • Необходимость фильтрации элемента ColumnReference и его атрибутов из плана xml

Пример элемента

<ColumnReference Database="[Adventureworks]" Schema="[dbo]" Table="[Product]" Column="ProductID" />

вызов: ColumnReference доступен в нескольких иерархиях, необходимо извлечь все из них

Ожидаемый вывод в виде таблицы вструктура ниже:

Database | Schema | Table | Column

Пример набора данных: (запустите код ниже, вы получите набор данных в temptable с именем #t)

    CREATE TABLE Employee 
    (
        EmpID INT NOT NULL , 
        EmpName VARCHAR(50) NOT NULL, 
        Designation VARCHAR(50) NULL, 
        Department VARCHAR(50) NULL, 
        JoiningDate DATETIME NULL,
        CONSTRAINT [PK_Employee] PRIMARY KEY CLUSTERED (EmpID)
    )

    INSERT INTO Employee 
    (EmpID, EmpName, Designation, Department, JoiningDate) VALUES 
    (1, 'CHIN YEN', 'LAB ASSISTANT', 'LAB', GETDATE()),
    (2, 'MIKE PEARL', 'SENIOR ACCOUNTANT', 'ACCOUNTS', GETDATE()),
    (3, 'GREEN FIELD', 'ACCOUNTANT', 'ACCOUNTS', GETDATE()),
    (4, 'DEWANE PAUL', 'PROGRAMMER', 'IT', GETDATE()),
    (5, 'MATTS', 'SR. PROGRAMMER', 'IT', GETDATE()),
    (6, 'PLANK OTO', 'ACCOUNTANT', 'ACCOUNTS', GETDATE())
create proc itemployee
as 
select EmpName, Designation  from Employee where department = 'it'

go 

exec itemployee


SELECT 
  'itemployee ' as SP_Name, 
  query_plan into #t  FROM sys.dm_exec_cached_plans cp CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle)
WHERE 
  object_id('itemployee') = objectid;

запрос, используемый для извлечения вывода:

SELECT 
  AnyColRef.value('@Table', 'nvarchar(250)') AS [Table], 
  AnyColRef.value('@Column', 'nvarchar(250)') AS [Column] 
FROM 
  #t t
  CROSS APPLY t.query_plan.nodes('//ColumnReference') A(AnyColRef);

1 Ответ

3 голосов
/ 25 апреля 2019

То, что вы предоставляете, недостаточно ... Для вашего следующего вопроса попробуйте создать mcve ( автономный образец для воспроизведения вашей проблемы) .

вызов: ColumnReference доступен в нескольких иерархиях, необходимо извлечь их все

В качестве быстрого снимка вы можете попробовать что-то подобное:

SELECT AnyColRef.value('@Database','nvarchar(250)') AS [Database]
      ,AnyColRef.value('@Schema','nvarchar(250)') AS [Schema]
      ,AnyColRef.value('@Table','nvarchar(250)') AS [Table]
      ,AnyColRef.value('@Column','nvarchar(250)') AS [Column]
FROM YourTable t
CROSS APPLY t.YourXMLColumn.nodes('//ColumnReference') A(AnyColRef);

Идея вкратце:
Глубокий поиск 1016 * (вызванный двойной косой чертой в //ColumnReference) будет искать любой элемент с таким именем в любом месте вашего XML.Все эти элементы возвращаются как производное множество , где каждый элемент возвращается в свою собственную строку (это делается с помощью .nodes()).Собственный метод XML .value(), наконец, извлечет внутренние значения атрибутов (обозначенные @).

UPDATE

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

Ваша проблема: XML объявляет пространство имен по умолчанию.Существует три подхода для решения этой проблемы:

  1. a) Использование WITHXMLNAMESPACES с префиксом
  2. b) Использование WITHXMLNAMESPACES с DEFUALT
  3. a) Используйте внутреннюю декларацию с префиксом
  4. b) Используйте внутреннюю декларацию для default element namespace
  5. Используйте подстановочный знак

Либо это

WITH XMLNAMESPACES('http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS ns)
SELECT
  AnyColRef.value('@Table', 'nvarchar(250)') AS [Table], 
  AnyColRef.value('@Column', 'nvarchar(250)') AS [Column] 
FROM 
  #t t
  CROSS APPLY t.query_plan.nodes('//ns:ColumnReference') A(AnyColRef);

- или это

WITH XMLNAMESPACES(DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan')
SELECT
  AnyColRef.value('@Table', 'nvarchar(250)') AS [Table], 
  AnyColRef.value('@Column', 'nvarchar(250)') AS [Column] 
FROM 
  #t t
  CROSS APPLY t.query_plan.nodes('//ColumnReference') A(AnyColRef);

- или это

    SELECT
      AnyColRef.value('@Table', 'nvarchar(250)') AS [Table], 
      AnyColRef.value('@Column', 'nvarchar(250)') AS [Column] 
    FROM 
      #t t
      CROSS APPLY t.query_plan.nodes('declare namespace ns="http://schemas.microsoft.com/sqlserver/2004/07/showplan";//ns:ColumnReference') A(AnyColRef);

- или это

    SELECT
      AnyColRef.value('@Table', 'nvarchar(250)') AS [Table], 
      AnyColRef.value('@Column', 'nvarchar(250)') AS [Column] 
    FROM 
      #t t
      CROSS APPLY t.query_plan.nodes('declare default element namespace "http://schemas.microsoft.com/sqlserver/2004/07/showplan" ;//ColumnReference') A(AnyColRef);

- или это

SELECT
  AnyColRef.value('@Table', 'nvarchar(250)') AS [Table], 
  AnyColRef.value('@Column', 'nvarchar(250)') AS [Column] 
FROM 
  #t t
  CROSS APPLY t.query_plan.nodes('//*:ColumnReference') A(AnyColRef);

Общий совет: будьте как можно точнее.Пространства имен являются не просто модным дополнением, но очень важны для работы с различными элементами с одинаковыми именами (часто при объединении различных XML).Используйте подстановочный знак easy-cheesy только в тех случаях, когда вы можете быть уверены, что пространство имен не требуется.Лично я предпочитаю подход WITH XMLNAMESPACES с DEFAULT, так как он наиболее близок к данному XML.

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