0

在 SQL 中,有没有办法完成这样的事情?这是基于 SQL Server 报表生成器中内置的报表,用户可以在其中将多个文本值指定为单个报表参数。报表查询获取用户选择的所有值并将它们存储在单个变量中。我需要一种方法让查询只返回与用户指定的每个值都有关联的记录。

-- Assume there's a table of Elements with thousands of entries.  
-- Now we declare a list of properties for those Elements to be associated with.

create table #masterTable (
    ElementId int, Text varchar(10)
)

insert into #masterTable (ElementId, Text) values (1, 'Red');
insert into #masterTable (ElementId, Text) values (1, 'Coarse');
insert into #masterTable (ElementId, Text) values (1, 'Dense');
insert into #masterTable (ElementId, Text) values (2, 'Red');
insert into #masterTable (ElementId, Text) values (2, 'Smooth');
insert into #masterTable (ElementId, Text) values (2, 'Hollow');

-- Element 1 is Red, Coarse, and Dense.  Element 2 is Red, Smooth, and Hollow.  
-- The real table is actually much much larger than this; this is just an example.

-- This is me trying to replicate how SQL Server Report Builder treats 
-- report parameters in its queries.  The user selects one, some, all, 
-- or no properties from a list. The written query treats the user's   
-- selections as a single variable called @Properties.
-- Example scenario 1: User only wants to see Elements that are BOTH Red and Dense.
select e.* 
from Elements e 
where (@Properties) --ideally a set containing only Red and Dense
in
(select Text from #masterTable where ElementId = e.Id) --ideally a set containing only Red, Coarse, and Dense
--Both Red and Dense are within Element 1's properties (Red, Coarse, Dense), so Element 1 gets returned, but not Element 2.

-- Example scenario 2: User only wants to see Elements that are BOTH Red and Hollow.  
select e.* from Elements e where
(@Properties) --ideally a set containing only Red and Hollow
in
(select Text from #masterTable where ElementId = e.Id)
--Both Red and Hollow are within Element 2's properties (Red, Smooth, Hollow), so Element 2 gets returned, but not Element 1.


--Example Scenario 3: User only picked the Red option.
select e.* from Elements e where
(@Properties) --ideally a set containing only Red
in
(select Text from #masterTable where ElementId = e.Id)
--Red is within both Element 1 and Element 2's properties, so both Element 1 and Element 2 get returned.

上面的语法实际上不起作用,因为 SQL 似乎不允许在“in”比较的左侧有多个值。返回的错误:

Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.

我什至在正确的轨道上吗?对不起,如果这个例子看起来冗长或令人困惑。

这是我正在使用的确切代码:

select p.*
from Products p
where
(
    (
        --user can search through gloves, bats, or both
        p.TypeId = 2 and 'Bat' in (@ProductTypes) 
        and 
        (
            (
                (@BatProperties) COLLATE DATABASE_DEFAULT in 
                (
                    select props.Name from PropertyTypes props
                    inner join ProductProperties pp on props.Id = pp.TypeId
                    where pp.ProductId = p.Id
                )
            --still want query to run when no properties are selected
            ) or not exists(select * from @BatProperties)
        )
    )
    or
    (
        p.TypeId = 1 and 'Glove' in (@ProductTypes) --user can search through gloves, bats, or both
        and
        (
            (
                (@GloveProperties) COLLATE DATABASE_DEFAULT in 
                (
                    select props.Name from PropertyTypes props
                    inner join ProductProperties pp on props.Id = pp.TypeId
                    where pp.ProductId = p.Id
                )
            --still want query to run when no properties are selected
            ) or not exists(select * from @GloveProperties)
        )
    )
)
4

1 回答 1

0

假设元素文本字段不包含逗号,您可以先将参数字符串标记化并将属性填充到临时表中,随后可以将其与#masterTable 连接以获得具有所有所需属性的元素。CTE 可用于如下标记(代码不是生产标准)

declare @parameter varchar(1000)
declare @num_of_params int
set @num_of_params = 0
set @parameter = 'Red, Dense, Coarse'
CREATE TABLE #tmp (id int, string varchar(1000))

INSERT INTO #tmp (id, string)
SELECT 1, @parameter 

;WITH test (id, lft, rght, idx)
AS 
(
    SELECT t.id
        ,LEFT(t.string, CHARINDEX(', ', t.string) - 1)
        ,SUBSTRING(t.string, CHARINDEX(', ', t.string) + 2, DATALENGTH(t.string))
        ,0
    FROM #tmp t
    UNION ALL
    SELECT c.id
        ,CASE WHEN CHARINDEX(', ', c.rght) = 0 THEN c.rght ELSE LEFT(c.rght, CHARINDEX(', ', c.rght) - 1) END
        ,CASE WHEN CHARINDEX(', ', c.rght) > 0 THEN SUBSTRING(c.rght, CHARINDEX(', ', c.rght) + 2, DATALENGTH(c.rght)) 
            ELSE '' END
        ,idx + 1
    FROM test c 
    WHERE DATALENGTH(c.rght) > 0
)

select * into #test from test
select @num_of_params = count(*) from #test
Drop table #tmp
select @num_of_params


select e.* from Elements e where elementID in (
select elementID
from #masterTable  m inner join #test t 
on m.text = t.lft
group by m.ElementID
having count(*) = @num_of_params
)

drop table #test
于 2012-10-16T22:03:54.543 回答