SQL 基于 XML(列表)输入的查询

SQL Query based on XML (list) input

我有 table 这样的:

我的数据

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)
        )

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

一种方法是创建一个带有 companyid 和引用列的临时 table,将 xml 中的所有内容插入该临时 table 并在 MYDATA table。其他方式是类似的事情,但有一个子查询。

是否有其他更优雅且最重要的性能明智的方法来实现此目的?


更新

这是目前给我最好的性能结果的原因:

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;

您可以使用 CTE 从您的 XML 中使用 .nodes() 获得 派生的 table

这允许您处理从 XML 中获取的值,就好像它们是 正常 table:

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 方法在技术上与子查询完全相同 select在这种情况下。

根据 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