SQL Server 2008 R2 - 在同一列上具有多个可为空参数的搜索表

时间:2015-04-16 16:19:46

标签: search sql-server-2008-r2

我正在开发一个搜索工具,我需要使用最多6个搜索参数搜索表格,问题是,6个参数中的5个需要搜索同一列。

我正在搜索从item_category表填充的临时表,此临时表有3列,itemitem_desccat_desc。第一个搜索参数为@Desc,此parm将搜索item_desc列,其他5个参数为@P1@P2@P3,{{1} }和@P4,都是@P5种类型。这些nvarchar(40)个参数都需要搜索@P#列。

下面是创建包含示例数据的临时表的代码,以及我到目前为止的查询代码。我尝试用注释解释每个部分,我将在下面解释代码我需要它做什么。

cat_desc

我们的商品可以链接到多个类别,此查询的目标是能够按最多五个不同的类别搜索商品,按商品描述加上,因此查询参数-- Create Temp Tables -- CREATE TABLE #Item_Category_List( item nvarchar(30) ,item_desc nvarchar(40) ,cat_desc nvarchar(40)); -- Populate #Item_Category_List with sample data INSERT #Item_Category_List(item, item_desc, cat_desc) VALUES ('2-77132', '2-77132 P1812-24-120', 'Keypad PB Controls W/Variable Speed') ,('2-77132', '2-77132 P1812-24-120', '60 Hertz') ,('2-77132', '2-77132 P1812-24-120', '1 x 3.00HP Motor') ,('2-77132', '2-77132 P1812-24-120', 'Light Curtain Option') ,('2-77132', '2-77132 P1812-24-120', 'Bin Detection Option') ,('2-77132', '2-77132 P1812-24-120', '3 Phase') ,('2-77132', '2-77132 P1812-24-120', '480 Volts') ,('2-70470', 'CANTILEVER-CRSL-C20243-3912-194', 'Vidir Inventory Control Software') ,('2-70470', 'CANTILEVER-CRSL-C20243-3912-194', '60 Hertz') ,('2-70470', 'CANTILEVER-CRSL-C20243-3912-194', 'SEW Motor') ,('2-70470', 'CANTILEVER-CRSL-C20243-3912-194', '3 Phase') ,('2-70470', 'CANTILEVER-CRSL-C20243-3912-194', '460 Volts') ,('2-77562', 'HT54193-0663-12-RAMP', 'Keypad PB Controls W/Variable Speed') ,('2-77562', 'HT54193-0663-12-RAMP', '60 Hertz') ,('2-77562', 'HT54193-0663-12-RAMP', 'Top Right Side Front Location') ,('2-77562', 'HT54193-0663-12-RAMP', '2 x 2.00HP Motor') ,('2-77562', 'HT54193-0663-12-RAMP', '3 Phase') ,('2-77562', 'HT54193-0663-12-RAMP', '208 Volts') ,('2-76559', 'R20116-1416-4M 2-HAND CONTROLS', '2 Hand Security Control') ,('2-76559', 'R20116-1416-4M 2-HAND CONTROLS', 'Keypad PB Controls') ,('2-76559', 'R20116-1416-4M 2-HAND CONTROLS', '50 Hertz') ,('2-76559', 'R20116-1416-4M 2-HAND CONTROLS', 'Right Side Front Location') ,('2-76559', 'R20116-1416-4M 2-HAND CONTROLS', '1 x 1.50HP Motor') ,('2-76559', 'R20116-1416-4M 2-HAND CONTROLS', '1 Phase') ,('2-76559', 'R20116-1416-4M 2-HAND CONTROLS', '220 Volts') ,('2-73432', 'R20116-1614-06', 'PB Controls') ,('2-78125', 'R20116-1614-06', 'PB Controls') ,('2-74803', 'R20116-1614-06', 'PB Controls Dual W/Selector and Beeper') ,('2-78125', 'R20116-1614-06', '50 Hertz') ,('2-74803', 'R20116-1614-06', '50 Hertz') ,('2-73432', 'R20116-1614-06', '60 Hertz') ,('2-73432', 'R20116-1614-06', 'Right Side Front and Back Location') ,('2-74803', 'R20116-1614-06', 'Right Side Front and Back Location') ,('2-78125', 'R20116-1614-06', 'Right Side Front Location') ,('2-78125', 'R20116-1614-06', '1 x 1.50HP Motor') ,('2-73432', 'R20116-1614-06', '1 x 1.50HP Motor') ,('2-74803', 'R20116-1614-06', '1 x 1.50HP Motor') ,('2-74803', 'R20116-1614-06', 'Pigtail, Top Option') ,('2-74803', 'R20116-1614-06', '1 Phase') ,('2-78125', 'R20116-1614-06', '1 Phase') ,('2-73432', 'R20116-1614-06', '3 Phase') ,('2-74803', 'R20116-1614-06', '110 Volts') ,('2-78125', 'R20116-1614-06', '208 Volts') ,('2-73432', 'R20116-1614-06', '480 Volts') ,('2-76582', 'R20116-1614-09', 'PB Controls') ,('2-76582', 'R20116-1614-09', '60 Hertz') ,('2-76582', 'R20116-1614-09', 'Right Side Front and Back Location') ,('2-76582', 'R20116-1614-09', '2 x 1.50HP Motor') ,('2-76582', 'R20116-1614-09', '3 Phase') ,('2-76582', 'R20116-1614-09', '220 Volts') ,('2-59350', 'R20116-1614-12-STD-CNTRLS', 'Keypad PB Controls') ,('2-59350', 'R20116-1614-12-STD-CNTRLS', '60 Hertz') ,('2-59350', 'R20116-1614-12-STD-CNTRLS', 'Right Side Front Location') ,('2-59350', 'R20116-1614-12-STD-CNTRLS', '1 x 1.50HP Motor') ,('2-59350', 'R20116-1614-12-STD-CNTRLS', '3 Phase') ,('2-59350', 'R20116-1614-12-STD-CNTRLS', '220 Volts') ,('2-77592', 'R20158-2214-06', 'PB Controls Dual W/Selector and Beeper') ,('2-77592', 'R20158-2214-06', '60 Hertz') ,('2-77592', 'R20158-2214-06', 'Right Side Front and Back Location') ,('2-77592', 'R20158-2214-06', '2 x 1.00HP Motor') ,('2-77592', 'R20158-2214-06', '3 Phase') ,('2-77592', 'R20158-2214-06', '208 Volts') ,('2-48924', 'R20179-2514-12', 'Keypad PB Controls') ,('2-48924', 'R20179-2514-12', '60 Hertz') ,('2-48924', 'R20179-2514-12', 'Right Side Front and Back Location') ,('2-48924', 'R20179-2514-12', '1 x 1.50HP Motor') ,('2-48924', 'R20179-2514-12', '3 Phase') ,('2-48924', 'R20179-2514-12', '208 Volts') ,('2-70697', 'R20228-3214-06', 'PB Controls') ,('2-70697', 'R20228-3214-06', '60 Hertz') ,('2-70697', 'R20228-3214-06', 'Right Side Front Location') ,('2-70697', 'R20228-3214-06', '2 x 1.00HP Motor') ,('2-70697', 'R20228-3214-06', '3 Phase') ,('2-70697', 'R20228-3214-06', '208 Volts') ,('2-76637', 'R24125-1220-06-DC 2-76637', 'PB Controls Dual W/Selector and Beeper') ,('2-76637', 'R24125-1220-06-DC 2-76637', '60 Hertz') ,('2-76637', 'R24125-1220-06-DC 2-76637', 'Top Right Side Front and Back Location') ,('2-76637', 'R24125-1220-06-DC 2-76637', '3 Phase') ,('2-76637', 'R24125-1220-06-DC 2-76637', '208 Volts') ,('2-76965', 'R39150-MS-06-DC 2-76965', 'PB Controls') ,('2-76965', 'R39150-MS-06-DC 2-76965', '60 Hertz') ,('2-76965', 'R39150-MS-06-DC 2-76965', 'Left Side Front and Back Location') ,('2-76965', 'R39150-MS-06-DC 2-76965', '2 x 1.00HP Motor') ,('2-76965', 'R39150-MS-06-DC 2-76965', '3 Phase') ,('2-76965', 'R39150-MS-06-DC 2-76965', '208 Volts') ,('2-76909', 'SMALL WIRE CRSL R24159-1422-52', 'Keypad PB Controls') ,('2-76909', 'SMALL WIRE CRSL R24159-1422-52', '60 Hertz') ,('2-76909', 'SMALL WIRE CRSL R24159-1422-52', 'Right Side Front Location') ,('2-76909', 'SMALL WIRE CRSL R24159-1422-52', '1 x 1.00HP Motor') ,('2-76909', 'SMALL WIRE CRSL R24159-1422-52', '3 Phase') ,('2-76909', 'SMALL WIRE CRSL R24159-1422-52', '208 Volts') ,('2-54416', 'T39148-0740-15', 'Keypad PB Controls') ,('2-54416', 'T39148-0740-15', '60 Hertz') ,('2-54416', 'T39148-0740-15', 'Right Side Front Location') ,('2-54416', 'T39148-0740-15', '2 x 1.50HP Motor') ,('2-54416', 'T39148-0740-15', '3 Phase') ,('2-54416', 'T39148-0740-15', '208 Volts') ,('2-56095', 'T39188-0940-15-SV', 'Keypad PB Controls') ,('2-56095', 'T39188-0940-15-SV', '60 Hertz') ,('2-56095', 'T39188-0940-15-SV', 'Right Side Front Location') ,('2-56095', 'T39188-0940-15-SV', '2 x 1.50HP Motor') ,('2-56095', 'T39188-0940-15-SV', '3 Phase') ,('2-56095', 'T39188-0940-15-SV', '208 Volts') ,('2-53564', 'T45156-0744-15', 'Keypad PB Controls') ,('2-53564', 'T45156-0744-15', '60 Hertz') ,('2-53564', 'T45156-0744-15', 'Right Side Front Location') ,('2-53564', 'T45156-0744-15', '2 x 1.50HP Motor') ,('2-53564', 'T45156-0744-15', '3 Phase') ,('2-53564', 'T45156-0744-15', '208 Volts') ,('2-65573', 'T45200-0944-15 SV WITH FOLDING GATE', 'Keypad PB Controls') ,('2-65573', 'T45200-0944-15 SV WITH FOLDING GATE', '60 Hertz') ,('2-65573', 'T45200-0944-15 SV WITH FOLDING GATE', 'Right Side Front Location') ,('2-65573', 'T45200-0944-15 SV WITH FOLDING GATE', '2 x 1.50HP Motor') ,('2-65573', 'T45200-0944-15 SV WITH FOLDING GATE', '3 Phase') ,('2-65573', 'T45200-0944-15 SV WITH FOLDING GATE', '480 Volts') ,('2-76617', 'W39191-1134-06 2-76617', 'Keypad PB Controls') ,('2-76617', 'W39191-1134-06 2-76617', '60 Hertz') ,('2-76617', 'W39191-1134-06 2-76617', 'Right Side Front Location') ,('2-76617', 'W39191-1134-06 2-76617', '2 x 1.00HP Motor') ,('2-76617', 'W39191-1134-06 2-76617', '3 Phase') ,('2-76617', 'W39191-1134-06 2-76617', '208 Volts') ,('2-70274', 'W39250-1630-16.5', 'Keypad PB Controls') ,('2-70274', 'W39250-1630-16.5', '50 Hertz') ,('2-70274', 'W39250-1630-16.5', 'Right Side Front Location') ,('2-70274', 'W39250-1630-16.5', '2 x 1.50HP Motor') ,('2-70274', 'W39250-1630-16.5', '3 Phase') ,('2-70274', 'W39250-1630-16.5', '208 Volts') ,('2-57285', 'W45243-1240-W-100', 'Keypad PB Controls') ,('2-57285', 'W45243-1240-W-100', '60 Hertz') ,('2-57285', 'W45243-1240-W-100', 'Right Side Front Location') ,('2-57285', 'W45243-1240-W-100', '2 x 1.50HP Motor') ,('2-57285', 'W45243-1240-W-100', '3 Phase') ,('2-57285', 'W45243-1240-W-100', '208 Volts') GO -- Query Parms DECLARE @Desc nvarchar(40) = NULL ,@P1 nvarchar(40) = NULL ,@P2 nvarchar(40) = NULL ,@P3 nvarchar(40) = NULL ,@P4 nvarchar(40) = NULL ,@P5 nvarchar(40) = NULL -- Encase parm values with wildcards, and replace spaces in parm values with wildcards SELECT @Desc = '%' + REPLACE(@Desc, ' ', '%') + '%' ,@P1 = '%' + REPLACE(@P1, ' ', '%') + '%' ,@P2 = '%' + REPLACE(@P2, ' ', '%') + '%' ,@P3 = '%' + REPLACE(@P3, ' ', '%') + '%' ,@P4 = '%' + REPLACE(@P4, ' ', '%') + '%' ,@P5 = '%' + REPLACE(@P5, ' ', '%') + '%'; -- Create temp table to hold the @P1 - @P5 parms CREATE TABLE #Parms_List(parms nvarchar(40)); -- Insert @P parms into temp table for use later in INNER JOIN WITH p (parms) AS ( SELECT @P1 UNION ALL SELECT @P2 UNION ALL SELECT @P3 UNION ALL SELECT @P4 UNION ALL SELECT @P5 ) INSERT INTO #Parms_List SELECT parms FROM p WHERE parms IS NOT NULL -- Insert all distinct cat_desc if all @P parms are null IF @P1 IS NULL AND @P2 IS NULL AND @P3 IS NULL AND @P4 IS NULL AND @P5 IS NULL BEGIN INSERT INTO #Parms_List SELECT DISTINCT c.cat_desc FROM #Item_Category_List AS c END -- Create temp table to hold matching item numbers for @Desc search CREATE TABLE #Item_Desc_Match_List(item nvarchar(30)); -- Search #Item_Category_List for matching items based on description -- and insert into #Item_Desc_Match_List for use later in INNER JOIN IF @Desc IS NOT NULL BEGIN -- Insert only matching items from #Item_Category_List INSERT INTO #Item_Desc_Match_List SELECT DISTINCT t.item FROM #Item_Category_List AS t WHERE t.item_desc LIKE @Desc END ELSE BEGIN -- Insert all items from #Item_Category_List INSERT INTO #Item_Desc_Match_List SELECT DISTINCT t.item FROM #Item_Category_List AS t END -- Final Query for matching items SELECT DISTINCT t.item FROM #Item_Category_List AS t INNER JOIN #Item_Desc_Match_List AS i ON i.item = t.item INNER JOIN #Parms_List AS p ON t.cat_desc LIKE p.parms ORDER BY t.item DROP TABLE #Item_Category_List DROP TABLE #Parms_List DROP TABLE #Item_Desc_Match_List @P1@P5

对于项目描述查询,我​​创建了一个临时表@Desc,并且我将临时日期表插入了描述匹配的#Item_Desc_Match_List项列表,然后我在最后的查询中执行了DISTINCT这个INNER JOIN表。{/ p>

对于类别搜索,我的第一次尝试只是尝试像这样的where子句,#Item_Desc_Match_List等...但是因为5个WHERE cat_desc LIKE @P1 OR cat_desc LIKE @P2 parms中的任何一个都可以为null,没用。所以我的第二种方法是将所有@P parms放入临时表中,然后在@P列的最终查询中对{temp}数据表执行INNER JOIN。这很有用。

使用以下查询参数运行以上代码:

cat_desc

您将获得以下结果:

@Desc       nvarchar(40) = '1614'
,@P1        nvarchar(40) = NULL
,@P2        nvarchar(40) = NULL
,@P3        nvarchar(40) = NULL
,@P4        nvarchar(40) = NULL
,@P5        nvarchar(40) = NULL

到目前为止,这是正确的,因为所有这些项目的描述中都有 1614 。现在将item ------------------------------ 2-59350 2-73432 2-74803 2-76582 2-78125 更改为@P1,结果为:

'60 hertz'

这也是正确的,因为这三个项目的描述中 1614 ,并且它们都链接了 60赫兹类别。现在将item ------------------------------ 2-59350 2-73432 2-76582 更改为@P2,结果保持不变。这是问题的开始,因为只有项目编号2-76582和2-59350链接了 220伏类别。

'220 Volt'更改为@P3,结果应该减少到只有一个2-59350,因为该项目#是唯一一个描述类似的项目{{1链接的类别与'1 x 1.50HP'@Desc@P1匹配。

我已经在这个报告上旋转了几天,我不确定如何在我定义更多@P2 parms时缩小我的结果,而不是增加我的结果就像现在一样。

1 个答案:

答案 0 :(得分:0)

问题在于,您匹配的结果是 ANY 行与cat_desc匹配,而不是所有行。

对于您最简单的示例,让我们假设你的第二个案例没有DISTINCT

DECLARE  @Desc      nvarchar(40) = '1614'
        ,@P1        nvarchar(40) = '60 hertz'
        ,@P2        nvarchar(40) = '220 Volt'
        ,@P3        nvarchar(40) = NULL
        ,@P4        nvarchar(40) = NULL
        ,@P5        nvarchar(40) = NULL;

WITH p (param) AS
(
    SELECT  param
    FROM    (VALUES (@p1), (@p2), (@p3), (@p4), (@p5)) p (param)
    WHERE   p.param IS NOT NULL
)
SELECT  *
FROM    #Item_Category_List AS t
        INNER JOIN p
            ON t.cat_desc LIKE '%' + p.param + '%'
WHERE   t.item_desc LIKE '%' + @Desc     + '%';

您实际上正在返回所有行:

item    item_desc                   cat_desc    param
--------------------------------------------------------
2-73432 R20116-1614-06              60 Hertz    60 hertz
2-76582 R20116-1614-09              60 Hertz    60 hertz
2-76582 R20116-1614-09              220 Volts   220 Volt
2-59350 R20116-1614-12-STD-CNTRLS   60 Hertz    60 hertz
2-59350 R20116-1614-12-STD-CNTRLS   220 Volts   220 Volt

您真正需要的是按item分组并计算匹配的行数:

SELECT  t.item, Matches = COUNT(*) 
FROM    #Item_Category_List AS t
        INNER JOIN p
            ON t.cat_desc LIKE '%' + p.param + '%'
WHERE   t.item_desc LIKE '%' + @Desc + '%'
GROUP BY t.item;

给出了:

item    Matches
-------------
2-59350 2
2-73432 1
2-76582 2

然后您可以将结果限制为与参数数量匹配的项目(即匹配所提供的所有参数而不是任何参数):

DECLARE  @Desc      nvarchar(40) = '1614'
        ,@P1        nvarchar(40) = '60 hertz'
        ,@P2        nvarchar(40) = '220 Volt'
        ,@P3        nvarchar(40) = NULL
        ,@P4        nvarchar(40) = NULL
        ,@P5        nvarchar(40) = NULL;

WITH p (param) AS
(
    SELECT  param
    FROM    (VALUES (@p1), (@p2), (@p3), (@p4), (@p5)) p (param)
    WHERE   p.param IS NOT NULL
)
SELECT  t.item
FROM    #Item_Category_List AS t
        INNER JOIN p
            ON t.cat_desc LIKE '%' + p.param + '%'
WHERE   t.item_desc LIKE '%' + @Desc + '%'
GROUP BY t.item
HAVING COUNT(p.Param) = (SELECT COUNT(*) FROM P);

这给出了期望的结果:

item
------------
2-59350
2-76582

最后,对于你的上一个案例,你会得到一条记录(2-59350):

DECLARE  @Desc      nvarchar(40) = '1614'
        ,@P1        nvarchar(40) = '60 hertz'
        ,@P2        nvarchar(40) = '220 Volt'
        ,@P3        nvarchar(40) = '1 x 1.50HP'
        ,@P4        nvarchar(40) = NULL
        ,@P5        nvarchar(40) = NULL;

WITH p (param) AS
(
    SELECT  param
    FROM    (VALUES (@p1), (@p2), (@p3), (@p4), (@p5)) p (param)
    WHERE   p.param IS NOT NULL
)
SELECT  t.item
FROM    #Item_Category_List AS t
        INNER JOIN p
            ON t.cat_desc LIKE '%' + p.param + '%'
WHERE   t.item_desc LIKE '%' + @Desc + '%'
GROUP BY t.item
HAVING COUNT(p.Param) = (SELECT COUNT(*) FROM P);

这当然不考虑没有提供参数,也没有说明,所以这里我将内连接更改为左连接,并在having和where子句中添加额外的逻辑以允许不提供参数:

DECLARE  @Desc      nvarchar(40) = NULL
        ,@P1        nvarchar(40) = NULL
        ,@P2        nvarchar(40) = NULL
        ,@P3        nvarchar(40) = NULL
        ,@P4        nvarchar(40) = NULL
        ,@P5        nvarchar(40) = NULL;

WITH p (param) AS
(
    SELECT  param
    FROM    (VALUES (@p1), (@p2), (@p3), (@p4), (@p5)) p (param)
    WHERE   p.param IS NOT NULL
)
SELECT  t.item
FROM    #Item_Category_List AS t
        LEFT JOIN p
            ON t.cat_desc LIKE '%' + p.param + '%'
WHERE   t.item_desc LIKE '%' + @Desc + '%'
OR      @Desc IS NULL
GROUP BY t.item
HAVING  COUNT(p.Param) = (SELECT COUNT(*) FROM P)
OR      (SELECT COUNT(*) FROM P) = 0;

这不太可能表现良好,但由于您的所有比较都像操作一样,我不认为您可以避免全表扫描。

顺便说一下,我会考虑使用表值参数,而不是你的6个参数:

CREATE TYPE dbo.ListOfString AS TABLE (Value NVARCHAR(MAX));

我倾向于为这样的事情创建泛型类型,因此它们更可重用,但这是个人偏好,如果你不介意有大量的类型,并担心表格中的严格数据,那么感觉免费使类型更具体。

然后您的程序将是:

CREATE PROCEDURE dbo.SearchItemCategoryList @Desc NVARCHAR(40), @Params dbo.ListOfString READONLY
AS
BEGIN

    SELECT  t.item
    FROM    dbo.Item_Category_List AS t
            LEFT JOIN @Params AS p
                ON t.cat_desc LIKE '%' + p.Value + '%'
    WHERE   t.item_desc LIKE '%' + @Desc + '%'
    OR      @Desc IS NULL
    GROUP BY t.item
    HAVING  COUNT(p.Value) = (SELECT COUNT(*) FROM @Params)
    OR      (SELECT COUNT(*) FROM @Params) = 0;

END

您可以使用以下内容调用该过程:

-- GET ALL
DECLARE @P dbo.ListOfString;
EXECUTE dbo.SearchItemCategoryList NULL, @p;
GO
-- GET MATCHING JUST DESCRIPTION
DECLARE @P dbo.ListOfString;
EXECUTE dbo.SearchItemCategoryList '1614', @p;
GO
-- GET USING ONE PARAMETER
DECLARE @P dbo.ListOfString;
INSERT @p (Value) VALUES ('60 hertz');
EXECUTE dbo.SearchItemCategoryList '1614', @p;
GO
-- GET USING THREE PARAMETERS
DECLARE @P dbo.ListOfString;
INSERT @p (Value) VALUES ('60 hertz'), ('220 Volt'), ('1 x 1.50HP');
EXECUTE dbo.SearchItemCategoryList '1614', @p;

<强> Example on SQL Fiddle