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
我有 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