Заполнение отдельного списка из повторяющихся данных в SQL Server - PullRequest
2 голосов
/ 07 ноября 2011

Мне нужно собрать список отдельных сотрудников из файла XML, который содержит журнал продаж, сделанных каждым сотрудником. К сожалению, данные в файле XML не совсем "согласованы". Файл структурирован так:

<Sale EmployeeId="67890" EmployeeName="John" EmployeeManagerId="12345" 
      CustomerName="Bob" SaleNumber="..." />
<Sale EmployeeId="67890" EmployeeName="John" EmployeeManagerId="12345" 
      CustomerName="Pat" SaleNumber="..." />
<Sale EmployeeId="67890" EmployeeName=""     EmployeeManagerId="12345" 
      CustomerName="Sally" SaleNumber="..." />
<Sale EmployeeId="67890" EmployeeName=""     EmployeeManagerId="12345" 
      CustomerName="Sue" SaleNumber="..." />
<Sale EmployeeId="67890" EmployeeName="John" EmployeeManagerId=""      
      CustomerName="Jack" SaleNumber="..." />
<Sale EmployeeId="58203" EmployeeName="Fred" EmployeeManagerId=""      
      CustomerName="Bill" SaleNumber="..." />

Этот XML-файл загружается в веб-приложение, которое передает свое содержимое (в виде XML) в хранимую процедуру в SQL Server для обработки. Из-за размера этого файла (до 30 000 элементов) я хотел бы выполнить как можно меньше обработки в веб-приложении.

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

Таким образом, для каждого уникального идентификатора сотрудника я бы дважды повторил результаты, чтобы узнать, смогу ли я найти его имя и идентификатор менеджера.

После обработки файла я ожидаю, что таблица Employee будет выглядеть следующим образом:

+---------+------+------------+
| Id (PK) | Name | ManagerId  |
+---------+------+------------+
| 12345   | NULL | NULL       |
| 67890   | John | 12345      |
| 58203   | Fred | NULL       |
+---------+------+------------+

Есть ли более эффективное (и менее процедурное) решение для этого?

Ответы [ 2 ]

3 голосов
/ 07 ноября 2011

Это дает результаты, но может потребовать некоторой очистки, если данные образца отличаются.

DECLARE @T TABLE ( x XML )
INSERT  INTO @T
        ( x )
VALUES  ( '<Sale EmployeeId="67890" EmployeeName="John" EmployeeManagerId="12345"        CustomerName="Bob" SaleNumber="..." />' )
    ,   ( '<Sale EmployeeId="67890" EmployeeName="John" EmployeeManagerId="12345"        CustomerName="Pat" SaleNumber="..." />' ),
        ( '<Sale EmployeeId="67890" EmployeeName=""     EmployeeManagerId="12345"        CustomerName="Sally" SaleNumber="..." />' )
     ,  ( '<Sale EmployeeId="67890" EmployeeName=""     EmployeeManagerId="12345"        CustomerName="Sue" SaleNumber="..." />' ),
        ( '<Sale EmployeeId="67890" EmployeeName="John" EmployeeManagerId=""             CustomerName="Jack" SaleNumber="..." />' ),
        ( '<Sale EmployeeId="58203" EmployeeName="Fred" EmployeeManagerId=""             CustomerName="Bill" SaleNumber="..." />' ) 

;WITH c 
AS (

SELECT DISTINCT ID = x.value('(/Sale/@EmployeeId)[1]', 'int')
      , NAME = x.value('(/Sale/@EmployeeName)[1]', 'varchar(4)')
      , ManagerID = x.value('(/Sale/@EmployeeManagerId)[1]', 'int')
FROM    @t
WHERE  x.value('(/Sale/@EmployeeName)[1]', 'varchar(4)') <> ''
)

SELECT ID, NAME, ManagerID =MIN( NULLIF(ManagerID, 0))
FROM c 
GROUP BY ID, Name
UNION 
SELECT ManagerID, NULL, NULL
FROM c
WHERE ManagerID NOT IN (SELECT DISTINCT ID FROM c)
    AND ManagerID <> 0
2 голосов
/ 07 ноября 2011
declare @xml xml = '
<Sale EmployeeId="67890" EmployeeName="John" EmployeeManagerId="12345" 
      CustomerName="Bob" SaleNumber="..." />
<Sale EmployeeId="67890" EmployeeName="John" EmployeeManagerId="12345" 
      CustomerName="Pat" SaleNumber="..." />
<Sale EmployeeId="67890" EmployeeName=""     EmployeeManagerId="12345" 
      CustomerName="Sally" SaleNumber="..." />
<Sale EmployeeId="67890" EmployeeName=""     EmployeeManagerId="12345" 
      CustomerName="Sue" SaleNumber="..." />
<Sale EmployeeId="67890" EmployeeName="John" EmployeeManagerId=""      
      CustomerName="Jack" SaleNumber="..." />
<Sale EmployeeId="58203" EmployeeName="Fred" EmployeeManagerId=""      
      CustomerName="Bill" SaleNumber="..." />'

-- "E1 is all employees"
;with E1 as      
(
  select T.N.value('@EmployeeId', 'int') as Id,
         T.N.value('@EmployeeName', 'nvarchar(100)') as Name,
         T.N.value('@EmployeeManagerId', 'int') as ManagerID
  from @xml.nodes('/Sale') as T(N)
),
-- E2 groups on id to get only one emp for each id
E2 as
(
  select Id, max(Name) as Name, nullif(max(ManagerID), 0) as ManagerID
  from E1 
  group by Id
),
-- "All manager id's"
M as
(
  select distinct T.N.value('@EmployeeManagerId', 'int') as Id
  from @xml.nodes('/Sale') as T(N)
  where T.N.value('@EmployeeManagerId', 'int') <> 0       
)
-- "All unique employees"
select Id, Name, ManagerID
from E2
union all
-- "Add managers with a lookup against emp for name and manager id"
select M.Id, E2.Name, E2.ManagerID
from M
  left outer join E2 
    on M.Id = E2.ID
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...