Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/gwt/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 解析XML以构建动态查询_Sql_Tsql_Sql Server 2008 - Fatal编程技术网

Sql 解析XML以构建动态查询

Sql 解析XML以构建动态查询,sql,tsql,sql-server-2008,Sql,Tsql,Sql Server 2008,我正试图找出处理这样一个场景的最佳方法,即向我传递包含搜索条件的XML。如果用户选择了特定的过滤器,那么这些过滤器将以XML的形式发送,如果有一个部分没有过滤,那么它将不会出现在XML中,这意味着应该返回该过滤器的所有内容 我的问题是如何最好地分解XML并根据从XML对象中得到的内容构建动态查询。有没有更好的方法来处理这种情况 以下是我目前的做法: 分解XML并将过滤后的数据放入全局临时表中,以便我可以使用它们构建动态查询。 使用这些临时表在查询中创建Where-Exists条件,根据XML中传

我正试图找出处理这样一个场景的最佳方法,即向我传递包含搜索条件的XML。如果用户选择了特定的过滤器,那么这些过滤器将以XML的形式发送,如果有一个部分没有过滤,那么它将不会出现在XML中,这意味着应该返回该过滤器的所有内容

我的问题是如何最好地分解XML并根据从XML对象中得到的内容构建动态查询。有没有更好的方法来处理这种情况

以下是我目前的做法:

分解XML并将过滤后的数据放入全局临时表中,以便我可以使用它们构建动态查询。 使用这些临时表在查询中创建Where-Exists条件,根据XML中传递给我的内容筛选结果。如果没有筛选其中一个搜索条件部分,临时表将没有行,我不会用exists语句将其添加到where子句中。 我在查询中使用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或构建动态片段

永远感谢你提供的信息


考虑以下几点

主键:

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 

很严重,我不明白。为什么不在where子句中使用EXTER apply从xml中提取数据?在开头添加create schema test。我认为更合适的问题名称是“如何使用xml作为搜索条件搜索数据”。谢谢Denis!太棒了。