2
我想弄清楚處理場景的最佳方式,我將傳遞將包含搜索條件的XML。如果用戶選擇了特定的過濾器,那麼這些過濾器將以XML格式發送,並且如果有一個部分未過濾,那麼它將不會出現在XML中(這意味着應該返回該過濾器的所有內容)。解析XML構建動態查詢
我的問題是關於撕碎XML和建立一個動態查詢出我的XML對象的最佳過程。有沒有更好的方法來處理這種情況?
這是我目前的做法:
- XML分解,並把過濾後的數據爲全球臨時表,這樣我可以用它來建立我的動態查詢。
- 使用這些臨時表在查詢中創建「Where Exists」條件,以根據在XML中傳遞給我的結果過濾結果。如果其中一個搜索條件部分沒有被過濾,臨時表將會有零行,我不會用exists語句將它添加到where子句中。
- 我在查詢中使用FOR XML PATH('')將數據彙總爲逗號分隔值。
構建測試架構/對象:
--------------------------------------------------------
--Build Test Schema to demonstrate XML Parsing
--------------------------------------------------------
SET NOCOUNT ON;
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[test].[Products]') AND type in (N'U'))
DROP TABLE [test].[Products]
GO
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[test].[Categories]') AND type in (N'U'))
DROP TABLE [test].[Categories]
GO
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[test].[Brands]') AND type in (N'U'))
DROP TABLE [test].[Brands]
GO
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[test].[Types]') AND type in (N'U'))
DROP TABLE [test].[Types]
GO
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[test].[Products_Categories]') AND type in (N'U'))
DROP TABLE [test].[Products_Categories]
GO
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[test].[Products_Brands]') AND type in (N'U'))
DROP TABLE [test].[Products_Brands]
GO
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[test].[Products_Types]') AND type in (N'U'))
DROP TABLE [test].[Products_Types]
GO
--IF EXISTS (SELECT * FROM sys.schemas WHERE name = N'test')
--DROP SCHEMA [test]
--GO
--CREATE SCHEMA [test] AUTHORIZATION [dbo]
--GO
Create Table test.Categories(
CategoryID INT IDENTITY(1,1),
Category varchar(100));
Insert Into test.Categories
Values('HDTV');
Insert Into test.Categories
Values('Receiver');
Insert Into test.Categories
Values('Headphones');
Insert Into test.Categories
Values('Blu-Ray');
GO
Create Table test.Brands(
BrandID INT IDENTITY(1,1),
Brand varchar(100));
Insert Into test.Brands
Values('Sony');
Insert Into test.Brands
Values('Samsung');
GO
Create Table test.[Types](
TypeID INT IDENTITY(1,1),
[Type] varchar(100));
Insert Into test.[Types]
Values('LCD');
Insert Into test.[Types]
Values('Plasma');
Insert Into test.[Types]
Values('Rear Projection');
Insert Into test.[Types]
Values('LED');
GO
Create Table test.Products_Categories(
ProductCategoryID INT IDENTITY(1,1),
ProductID INT,
CategoryID INT)
GO
Create Table test.Products_Brands(
ProductBrandID INT IDENTITY(1,1),
ProductID INT,
BrandID INT)
GO
Create Table test.Products_Types(
ProductTypeID INT IDENTITY(1,1),
ProductID INT,
TypeID INT)
GO
Insert Into test.Products_Categories
Select 1,1
UNION
Select 1,2
UNION
Select 1,3
UNION
Select 1,4
UNION
Select 2,1
UNION
Select 2,2
UNION
Select 2,3
GO
Insert Into test.Products_Brands
Select 1,1
UNION
Select 1,2
UNION
Select 1,3
UNION
Select 1,4
UNION
Select 2,1
UNION
Select 2,2
UNION
Select 2,3
UNION
Select 2,4
GO
Insert Into test.Products_Types
Select 1,1
UNION
Select 1,2
UNION
Select 2,1
GO
CREATE TABLE [test].[Products](
ProductID [int] IDENTITY(1,1) NOT NULL,
Product [varchar](25) NULL
) ON [PRIMARY]
GO
Insert Into [test].[Products]
Select 'A.1'
UNION
Select 'B.1'
SET NOCOUNT OFF;
構建過程將XML分解,並建立動態查詢:
--------------------------------------------------------
--Create Sproc to Parse XML Input
--------------------------------------------------------
GO
ALTER PROCEDURE dbo.GetMySearchResults
@XML XML,
@Debug BIT = 0
AS
BEGIN
SET NOCOUNT ON;
DECLARE @SearchOutput TABLE(
Product VARCHAR(50),
Category VARCHAR(50),
Brand VARCHAR(50),
[Type] VARCHAR(50));
DECLARE @Category VARCHAR(200) = '',
@Brand VARCHAR(200) = '',
@Type VARCHAR(200) = '',
@Where VARCHAR(500) = '',
@SQL NVARCHAR(4000)
------Shred Material Data---
IF OBJECT_ID('tempdb..##Category') IS NOT NULL DROP TABLE ##Category;
CREATE TABLE ##Category (ID INT PRIMARY KEY);
INSERT INTO ##Category SELECT Nodes.ID.value('@id', 'int') FROM @xml.nodes('//Filter[@id="Category"]//select') AS Nodes(ID);
IF (Select COUNT(*) From ##Category) > 0
SET @Category = 'and exists (Select 1 From ##Category el Where el.ID = e.CategoryID)'
------Component Material Data---
IF OBJECT_ID('tempdb..##Brand') IS NOT NULL DROP TABLE ##Brand;
CREATE TABLE ##Brand (ID INT PRIMARY KEY);
INSERT INTO ##Brand SELECT Nodes.ID.value('@id', 'int') FROM @xml.nodes('//Filter[@id="Brand"]//select') AS Nodes(ID);
IF (Select COUNT(*) From ##Brand) > 0
SET @Brand = 'and exists (Select 1 From ##Brand cl Where cl.ID = c.BrandID)'
------Shred Environment Data---
IF OBJECT_ID('tempdb..##Type') IS NOT NULL DROP TABLE ##Type;
CREATE TABLE ##Type (ID INT PRIMARY KEY);
INSERT INTO ##Type SELECT Nodes.ID.value('@id', 'int') FROM @xml.nodes('//Filter[@id="Type"]//select') AS Nodes(ID);
IF (Select COUNT(*) From ##Type) > 0
SET @Type = 'and exists (Select 1 From ##Type ml Where ml.ID = m.TypeID)'
----Build Where Exists Clauses
IF @Category <> '' OR @Brand <> '' OR @Type <> ''
SET @Where = 'Where 1 = 1 ' + @Category + @Brand + @Type
---Build Dynamic SQL to generate results from XML--
SET @SQL = ';WITH SearchData
AS(
Select
Distinct
li.Product,
---------Material------
(Select Distinct m2.Category + '',''
From test.Products li2
join test.Products_Categories lm on li2.ProductID = lm.ProductID
join test.Categories m on lm.CategoryID = m.CategoryID
join test.Products_Categories lm2 on lm.ProductID = lm2.ProductID
join test.Categories m2 on lm2.CategoryID = m2.CategoryID
Where li2.ProductID = li.ProductID
FOR XML PATH('''')) Category,
---------Component------
(Select Distinct c2.Brand + '',''
From test.Products li2
join test.Products_Brands lc on li2.ProductID = lc.ProductID
join test.Brands c on lc.BrandID = c.BrandID
join test.Products_Brands lc2 on lc.ProductID = lc.ProductID
join test.Brands c2 on lc2.BrandID = c2.BrandID
Where li2.ProductID = li.ProductID
FOR XML PATH('''')) Brand,
---------Environment------
(Select Distinct e2.[Type] + '',''
From test.Products li2
join test.Products_Types le on li2.ProductID = le.ProductID
join test.[Types] e on le.TypeID = e.TypeID
join test.Products_Types le2 on le.ProductID = le2.ProductID
join test.[Types] e2 on le2.TypeID = e2.TypeID
Where li2.ProductID = li.ProductID
FOR XML PATH('''')) [Type]
From test.Products li
join test.Products_Categories le on li.ProductID = le.ProductID
join test.Categories e on le.CategoryID = e.CategoryID
join test.Products_Brands lc on li.ProductID = lc.ProductID
join test.Brands c on lc.BrandID = c.BrandID
join test.Products_Types lm on li.ProductID = lm.ProductID
join test.[Types] m on lm.TypeID = m.TypeID '
+ @Where + ')
Select
sd.Product,
SUBSTRING(sd.Category,1,LEN(sd.Category)-1) Category,
SUBSTRING(sd.Brand,1,LEN(sd.Brand)-1) Brand,
SUBSTRING(sd.[Type],1,LEN(sd.[Type])-1) [Type]
From SearchData sd '
IF @Debug = 1
PRINT @SQL;
Insert Into @SearchOutput
exec sp_executesql @SQL;
Select
Distinct
Product,
Category,
Brand,
[Type]
From @SearchOutput;
DROP TABLE ##Category;
DROP TABLE ##Brand;
DROP TABLE ##Type;
SET NOCOUNT OFF;
END
GO
-----------------------------------------------------------------------
--Test XML Parsing
-----------------------------------------------------------------------
DECLARE @XMLInput XML = '<FilterData>
<Filter id="Category">
<select id="1" value="HDTV"/>
<select id="2" value="Receiver"/>
<select id="3" value="Headphones"/>
<select id="4" value="Blu-Ray"/>
</Filter>
<Filter id="Brand">
<select id="1" value="Sony"/>
<select id="2" value="Samsung"/>
</Filter>
<Filter id="Type">
<select id="1" value="LCD"/>
<select id="2" value="Plasma"/>
<select id="3" value="Rear Projection"/>
<select id="4" value="LED"/>
</Filter>
</FilterData>';
exec dbo.GetMySearchResults
@XML = @XMLInput,
@Debug = 1
GO
有沒有更好的方式來處理切碎的XML或建設動態片段?
總是欣賞信息。
S
Seariously,我不明白這一點。你爲什麼不使用where子句中的'outer apply'來從你的xml中提取數據? – 2010-09-13 19:05:34
開始時添加'create schema test'。 – 2010-09-13 19:07:48
我相信,更正確的問題名稱是'如何使用xml搜索數據作爲搜索條件'。 – 2010-09-13 20:40:57