我正在尝试找出处理我将传递XML的方案的最佳方法,该方案将包含搜索条件。如果用户选择了特定的过滤器,那么这些过滤器将以XML格式发送,如果有一个部分未经过滤,那么它将不会出现在XML中(这意味着应返回该过滤器的所有内容)。
我的问题是围绕最好的流程来分解XML,并根据我从XML对象中获取的内容构建动态查询。有没有更好的方法来处理这种情况?
这是我目前的做法:
构建测试模式/对象:
--------------------------------------------------------
--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或构建动态部分?
始终欣赏信息。
取值
答案 0 :(得分:3)
请考虑以下事项。
主键:
ALTER TABLE [test].[Brands] ADD CONSTRAINT [PK_Brands] PRIMARY KEY CLUSTERED ( [BrandID] ASC ) ON [PRIMARY]
ALTER TABLE [test].[Categories] ADD CONSTRAINT [PK_Categories] PRIMARY KEY CLUSTERED ( [CategoryID] ASC ) ON [PRIMARY]
ALTER TABLE [test].[Products] ADD CONSTRAINT [PK_Products] PRIMARY KEY CLUSTERED ( [ProductID] ASC ) ON [PRIMARY]
ALTER TABLE [test].[Products_Brands] ADD CONSTRAINT [PK_Products_Brands] PRIMARY KEY CLUSTERED ( [ProductID] ASC, [BrandID] ASC ) ON [PRIMARY]
ALTER TABLE [test].[Products_Categories] ADD CONSTRAINT [PK_Products_Categories] PRIMARY KEY CLUSTERED ( [ProductID] ASC, [CategoryID] ASC ) ON [PRIMARY]
ALTER TABLE [test].[Products_Types] ADD CONSTRAINT [PK_Products_Types] PRIMARY KEY CLUSTERED ( [ProductID] ASC, [TypeID] ASC ) ON [PRIMARY]
ALTER TABLE [test].[Types] ADD CONSTRAINT [PK_Types] PRIMARY KEY CLUSTERED ( [TypeID] ASC ) ON [PRIMARY]
索引观点:
create view [test].[vw_Products] with schemabinding as
select li.ProductID ,
Product ,
le.CategoryID ,
Category ,
lc.BrandID ,
Brand ,
lm.TypeID ,
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
go
create unique clustered index IX_vw_Products on test.vw_Products (ProductID, CategoryID, BrandID, TypeID)
go
--Categories
create view test.vw_Product_Category
with schemabinding
as
select p.ProductID, c.CategoryID, c.Category from test.Products as p
join test.Products_Categories as pc on p.ProductID = pc.ProductID
join test.Categories as c on pc.CategoryID = c.CategoryID
go
create unique clustered index IX_vw_Product_Category on test.vw_Product_Category (ProductID, CategoryID)
go
--Brands
create view test.vw_Product_Brand
with schemabinding
as
select p.ProductID, b.BrandID, b.Brand
from test.Products as p
join test.Products_Brands as pb on p.ProductID = pb.ProductID
join test.Brands as b on pb.BrandID = b.BrandID
go
create unique clustered index IX_vw_Product_Brand on test.vw_Product_Brand (ProductID, BrandId)
go
--Types
create view test.vw_Product_Types
with schemabinding
as
select p.ProductID, t.typeid, t.[type]
from test.Products as p
join test.Products_Types as pt on p.ProductID = pt.ProductID
join test.Types as t on pt.TypeID = t.TypeID
go
create unique clustered index IX_vw_Product_Types on test.vw_Product_Types (ProductID, TypeId)
go
没有动态sql的新搜索:
declare @xml 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>';
;with SearchData as(
select distinct
vp.Product ,
(select distinct ',' + vpc.Category from test.vw_Product_Category as vpc where vp.ProductID = vpc.ProductID for xml path('')) Categories,
(select distinct ',' + vpb.Brand from test.vw_Product_Brand as vpb where vp.ProductID = vpb.ProductID for xml path('')) Brands,
(select distinct ',' + vpt.[Type] from test.vw_Product_Types as vpt where vp.ProductID = vpt.ProductID for xml path('')) Types
from test.vw_Products as vp
where
exists (
select top 1 1
from @xml.nodes('/FilterData/Filter[@id="Category"]/select') f(n)
where f.n.value('@id', 'int') = vp.CategoryID
)
and exists (
select top 1 1
from @xml.nodes('/FilterData/Filter[@id="Brand"]/select') f(n)
where f.n.value('@id', 'int') = vp.BrandID
)
and exists (
select top 1 1
from @xml.nodes('/FilterData/Filter[@id="Type"]/select') f(n)
where f.n.value('@id', 'int') = vp.TypeID
)
)
select
sd.Product,
stuff(sd.Categories,1,1,'') Categories,
stuff(sd.Brands,1,1,'') Brands,
stuff(sd.Types,1,1,'') Types
from SearchData sd