基于XML(列表)输入

时间:2017-02-22 10:31:44

标签: sql sql-server tsql sqlxml

我有这样的表:

MYDATA

Company Reference  FirstName  Surname
1         A001       Test1Name  Test1Surname
2         A001       Test2Name  Test2Surname
3         A001       Test3Name  Test3Surname

和保存此数据的xml变量@searchList

<list>
    <item>
        <company>1</company>
        <reference>A001</reference>
    </item>
    <item>
        <company>2</company>
        <reference>A001</reference>
    </item>
    <item>
        <company>3</company>
        <reference>A001</reference>
    </item>
</list>

通常在只有一个条件的地方我会做:

select * from dbo.MYDATA rec
    where Reference in
        (
            select entity.value('(reference/text())[1]', 'varchar(32)') 
            from searchList.nodes('/list/item') as T(entity)
        )

如果有两个条件(company = xml.company and reference = xml.reference)则会变得更复杂。

一种方法是创建一个包含companyid和reference列的临时表,将xml中的所有内容插入到该临时表中,并在MYDATA表上进行连接。其他方式是类似的东西,但有子查询。

还有其他更优雅,最重要的表现方式来实现这一目标吗?

更新

这就是我现在最好的表现结果:

DECLARE @tbl TABLE(Id INT, Company INT,Reference VARCHAR(10),FirstName VARCHAR(100),Surname VARCHAR(100));
INSERT INTO @tbl VALUES
 (1, 1,'A001','Test1Name','Test1Surname')
,(2, 2,'A001','Test2Name','Test2Surname')
,(3, 3,'A001','Test3Name','Test3Surname');

DECLARE @xml XML=
N'<list>
    <item>
        <company>1</company>
        <reference>A001</reference>
    </item>
    <item>
        <company>2</company>
        <reference>A001</reference>
    </item>
    <item>
        <company>3</company>
        <reference>A001</reference>
    </item>
</list>';

WITH SEARCHLIST (company, reference) as
(
    select li.value('(company/text())[1]', 'int') AS company
          ,li.value('(reference/text())[1]', 'nvarchar(max)') AS reference
    from @xml.nodes('/list/item') AS A(li)
)

select rec.* from SEARCHLIST srl
    left join @tbl rec on srl.reference = rec.Reference and srl.company = rec.Company
where rec.Id is not null;

1 个答案:

答案 0 :(得分:1)

您可以使用CTE使用.nodes()从XML中获取派生表

这使您可以处理从XML中获取的值,就像它们是普通表一样:

DECLARE @tbl TABLE(Company INT,Reference VARCHAR(10),FirstName VARCHAR(100),Surname VARCHAR(100));
INSERT INTO @tbl VALUES
 (1,'A001','Test1Name','Test1Surname')
,(2,'A001','Test2Name','Test2Surname')
,(3,'A001','Test3Name','Test3Surname');

DECLARE @xml XML=
N'<list>
    <item>
        <company>1</company>
        <reference>A001</reference>
    </item>
    <item>
        <company>2</company>
        <reference>A001</reference>
    </item>
    <item>
        <company>3</company>
        <reference>A001</reference>
    </item>
</list>';

WITH CTE AS
(
    SELECT li.value(N'company[1]',N'int') AS company
          ,li.value(N'reference[1]',N'nvarchar(max)') AS reference
    FROM @xml.nodes(N'/list/item') AS A(li)
)
SELECT *
FROM @tbl AS t
INNER JOIN CTE ON t.Reference=CTE.reference --use any column however you like it

更新表现

说实话:你写其他方式是类似的东西,但有一个子查询。在这种情况下,CTE方法在技术上与子选择完全相同。

根据XML的大小,在大多数情况下,使用XML方法.exist()的速度更快,您可以使用谓词来检查XPath的存在性。

CTE appraoch必须读出整套,只是为了再次拆掉它。如果性能很重要,您也可以在.nodes()中包含过滤谓词。

但最好的解决方案很大程度上取决于您的实际需求......

使用.exist()

更新2个工作示例

尝试此操作(更改样本数据以携带不同的值)

DECLARE @tbl TABLE(Company INT,Reference VARCHAR(10),FirstName VARCHAR(100),Surname VARCHAR(100));
INSERT INTO @tbl VALUES
 (1,'A001','Test1Name','Test1Surname')
,(2,'A002','Test2Name','Test2Surname')
,(3,'A004','Test3Name','Test3Surname'); <-- Will not be returned

DECLARE @xml XML=
N'<list>
    <item>
        <company>1</company>
        <reference>A001</reference>
    </item>
    <item>
        <company>2</company>
        <reference>A002</reference>
    </item>
    <item>
        <company>3</company>
        <reference>A003</reference>
    </item>
</list>';

select * from @tbl rec
where @xml.exist(N'/list/item/reference[text()=sql:column("Reference")]')=1